@qkix/better-blocks-react-renderer 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/BlocksRenderer.tsx","../src/media.ts","../src/CodeBlock.tsx","../src/code.ts","../src/MermaidDiagram.tsx","../src/SocialEmbed.tsx","../src/Video.tsx"],"sourcesContent":["import {\n Fragment,\n cloneElement,\n createElement,\n isValidElement,\n type ComponentType,\n type CSSProperties,\n type MouseEvent as ReactMouseEvent,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport katex from 'katex';\nimport {\n buildTextMarks,\n formatFileSize,\n getBlockStyle as coreGetBlockStyle,\n getDefaultMarkRender,\n getFileIcon,\n getListStyleType,\n getModifierProps,\n getPlainText,\n} from '@qkix/better-blocks-core';\n\nimport {\n MEDIA_CAPTION_STYLE,\n getAspectRatio,\n getMediaFigureStyle,\n getMediaFrameStyle,\n} from './media';\nimport { CodeBlock } from './CodeBlock';\nimport { MermaidDiagram } from './MermaidDiagram';\nimport { SocialEmbed } from './SocialEmbed';\nimport { Video } from './Video';\nimport type {\n AudioAlignment,\n AudioNode,\n BlockNode,\n BlocksRendererProps,\n ButtonElement,\n ButtonFile,\n ButtonStyle,\n CalloutNode,\n CalloutVariant,\n CodeNode,\n CustomBlocksConfig,\n CustomModifiersConfig,\n DetailsNode,\n DiagramNode,\n EmbedNode,\n HeadingNode,\n HorizontalLineNode,\n ImageNode,\n InlineNode,\n ListItemNode,\n ListNode,\n MathNode,\n MediaAlignment,\n MediaEmbedNode,\n ParagraphNode,\n QuoteNode,\n SocialEmbedNode,\n TableNode,\n TableRowNode,\n TextNode,\n VideoNode,\n} from './types';\n\n// ── Text / Modifier Rendering ────────────────────────────────────────\n\nfunction renderTextNode(node: TextNode, key: number, modifiers?: CustomModifiersConfig): ReactNode {\n // The core decides which marks are active and in what order they nest; this\n // renderer only turns each one into an element. Marks come back outer → inner,\n // so wrapping from the end inwards reproduces the original nesting exactly.\n const marks = buildTextMarks(node);\n\n let content: ReactNode = node.text;\n for (let i = marks.length - 1; i >= 0; i -= 1) {\n const mark = marks[i];\n const Comp = modifiers?.[mark.name as keyof CustomModifiersConfig] as\n ComponentType<Record<string, unknown> & { children?: ReactNode }> | undefined;\n\n if (Comp) {\n content = <Comp {...getModifierProps(mark)}>{content}</Comp>;\n continue;\n }\n\n const { tag, style } = getDefaultMarkRender(mark);\n content = createElement(tag, { style: style as CSSProperties | undefined }, content);\n }\n\n return <Fragment key={key}>{content}</Fragment>;\n}\n\n// ── Math (KaTeX) Rendering ───────────────────────────────────────────\n\nfunction renderMath(node: MathNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const isBlock = node.format === 'block';\n const MathComp = blocks?.math;\n const formula = node.value ?? '';\n\n if (MathComp) {\n return <MathComp key={key} formula={formula} inline={!isBlock} />;\n }\n\n const Tag = isBlock ? 'div' : 'span';\n const className = isBlock ? 'katex-block' : 'katex-inline';\n\n // KaTeX renders to an HTML string (SSR-friendly). With `throwOnError: false`\n // it renders parse errors inline instead of throwing; the try/catch is a\n // last-resort guard that falls back to the raw LaTeX source.\n try {\n const html = katex.renderToString(formula, {\n displayMode: isBlock,\n throwOnError: false,\n });\n return <Tag key={key} className={className} dangerouslySetInnerHTML={{ __html: html }} />;\n } catch {\n return (\n <Tag key={key} className={className}>\n {formula}\n </Tag>\n );\n }\n}\n\n// ── Inline Rendering ─────────────────────────────────────────────────\n\nfunction renderInlineContent(\n children: InlineNode[],\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n return children.map((child, index) => {\n if (child.type === 'text') {\n return renderTextNode(child, index, modifiers);\n }\n\n if (child.type === 'math') {\n return renderMath(child, index, blocks);\n }\n\n if (child.type === 'link') {\n const LinkComp = blocks?.link;\n const linkChildren = child.children.map((textNode, i) =>\n renderTextNode(textNode, i, modifiers)\n );\n\n return LinkComp ? (\n <LinkComp key={index} url={child.url} target={child.target} rel={child.rel}>\n {linkChildren}\n </LinkComp>\n ) : (\n <a key={index} href={child.url} target={child.target} rel={child.rel}>\n {linkChildren}\n </a>\n );\n }\n\n return null;\n });\n}\n\n// ── List Rendering ───────────────────────────────────────────────────\n\nfunction renderListItem(\n node: ListItemNode,\n key: number,\n isTodo: boolean,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const ListItemComp = blocks?.['list-item'];\n const children = renderInlineContent(node.children, blocks, modifiers);\n\n if (ListItemComp) {\n return (\n <ListItemComp key={key} checked={isTodo ? node.checked : undefined}>\n {children}\n </ListItemComp>\n );\n }\n\n if (isTodo) {\n const checked = node.checked ?? false;\n return (\n <li key={key} style={{ listStyle: 'none' }}>\n <input type=\"checkbox\" checked={checked} readOnly style={{ marginRight: '0.5em' }} />\n <span style={checked ? { textDecoration: 'line-through', opacity: 0.6 } : undefined}>\n {children}\n </span>\n </li>\n );\n }\n\n return <li key={key}>{children}</li>;\n}\n\nfunction renderList(\n node: ListNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const ListComp = blocks?.list;\n const indentLevel = node.indentLevel || 0;\n const isTodo = node.format === 'todo';\n const children = node.children.map((child, index) => {\n if (child.type === 'list-item') {\n return renderListItem(child, index, isTodo, blocks, modifiers);\n }\n if (child.type === 'list') {\n return renderList(child, index, blocks, modifiers);\n }\n return null;\n });\n\n if (ListComp) {\n return (\n <ListComp key={key} format={node.format} indentLevel={indentLevel}>\n {children}\n </ListComp>\n );\n }\n\n if (isTodo) {\n return (\n <ul key={key} style={{ listStyle: 'none', paddingLeft: indentLevel > 0 ? '1.5em' : 0 }}>\n {children}\n </ul>\n );\n }\n\n const Tag = node.format === 'ordered' ? 'ol' : 'ul';\n const listStyleType = getListStyleType(node.format as 'ordered' | 'unordered', indentLevel);\n return (\n <Tag key={key} style={{ listStyleType }}>\n {children}\n </Tag>\n );\n}\n\n// ── Table Rendering ──────────────────────────────────────────────────\n\nfunction renderTable(\n block: TableNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const TableComp = blocks?.table;\n const RowComp = blocks?.['table-row'];\n const CellComp = blocks?.['table-cell'];\n const HeaderCellComp = blocks?.['table-header-cell'];\n\n // Leading rows made up entirely of header cells are the table's header; they go\n // in <thead> so screen readers announce them. A merged header (a `rowSpan` label\n // above a split sub-header) spans several such rows, so count them all.\n let headerRowCount = 0;\n while (isHeaderRow(block.children[headerRowCount])) headerRowCount++;\n\n const rows = block.children.map((row, rowIndex) => {\n const inHeader = rowIndex < headerRowCount;\n\n const cells = row.children.map((cell, cellIndex) => {\n const cellChildren = renderInlineContent(cell.children, blocks, modifiers);\n const isHeader = cell.type === 'table-header-cell';\n // Absent means left / 1 — omit rather than emit a redundant attribute.\n const style = cell.align ? { textAlign: cell.align } : undefined;\n const cellProps = {\n align: cell.align,\n colSpan: cell.colSpan,\n rowSpan: cell.rowSpan,\n style,\n };\n\n if (isHeader && HeaderCellComp) {\n return (\n <HeaderCellComp key={cellIndex} {...cellProps}>\n {cellChildren}\n </HeaderCellComp>\n );\n }\n if (!isHeader && CellComp) {\n return (\n <CellComp key={cellIndex} {...cellProps}>\n {cellChildren}\n </CellComp>\n );\n }\n\n return isHeader ? (\n <th\n key={cellIndex}\n // A header cell in the <thead> labels a column; one that appears in a\n // body row is a row header instead.\n scope={inHeader ? 'col' : 'row'}\n colSpan={cell.colSpan}\n rowSpan={cell.rowSpan}\n style={style}\n >\n {cellChildren}\n </th>\n ) : (\n <td key={cellIndex} colSpan={cell.colSpan} rowSpan={cell.rowSpan} style={style}>\n {cellChildren}\n </td>\n );\n });\n\n return RowComp ? <RowComp key={rowIndex}>{cells}</RowComp> : <tr key={rowIndex}>{cells}</tr>;\n });\n\n if (TableComp) {\n return <TableComp key={key}>{rows}</TableComp>;\n }\n\n return (\n <table key={key} className=\"bb-table\">\n {headerRowCount > 0 && <thead>{rows.slice(0, headerRowCount)}</thead>}\n {rows.length > headerRowCount && <tbody>{rows.slice(headerRowCount)}</tbody>}\n </table>\n );\n}\n\nfunction isHeaderRow(row: TableRowNode | undefined): boolean {\n return (\n !!row && row.children.length > 0 && row.children.every((c) => c.type === 'table-header-cell')\n );\n}\n\n// ── Block Rendering ──────────────────────────────────────────────────\n\n/**\n * Renderer-wide settings that only some blocks consume. Passed as one object\n * rather than loose parameters so it can be threaded through the blocks that\n * nest others (callout, details) without widening every signature further.\n */\ntype RenderOptions = {\n codeTheme: string;\n codeCopyButton: boolean;\n};\n\nfunction renderBlock(\n block: BlockNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig,\n options?: RenderOptions\n): ReactNode {\n switch (block.type) {\n case 'paragraph':\n return renderParagraph(block, key, blocks, modifiers);\n case 'heading':\n return renderHeading(block, key, blocks, modifiers);\n case 'list':\n return renderList(block, key, blocks, modifiers);\n case 'quote':\n return renderQuote(block, key, blocks, modifiers);\n case 'code':\n return renderCode(block, key, blocks, options);\n case 'image':\n return renderImage(block, key, blocks);\n case 'horizontal-line':\n return renderHorizontalLine(block, key, blocks);\n case 'table':\n return renderTable(block, key, blocks, modifiers);\n case 'media-embed':\n return renderMediaEmbed(block, key, blocks);\n case 'embed':\n return renderEmbed(block, key, blocks);\n case 'video':\n return renderVideo(block, key, blocks);\n case 'math':\n return renderMath(block, key, blocks);\n case 'diagram':\n return renderDiagram(block, key, blocks);\n case 'callout':\n return renderCallout(block, key, blocks, modifiers, options);\n case 'details':\n return renderDetails(block, key, blocks, modifiers, options);\n case 'button':\n return renderButton(block, key, blocks);\n case 'social-embed':\n return renderSocialEmbed(block, key, blocks);\n case 'audio':\n return renderAudio(block, key, blocks);\n default:\n return null;\n }\n}\n\nfunction getBlockStyle(block: {\n textAlign?: string;\n lineHeight?: string;\n indent?: number;\n}): CSSProperties | undefined {\n // The core returns a neutral style record; React just needs it typed.\n return coreGetBlockStyle(block) as CSSProperties | undefined;\n}\n\nfunction renderParagraph(\n block: ParagraphNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const ParagraphComp = blocks?.paragraph;\n const children = renderInlineContent(block.children, blocks, modifiers);\n const style = getBlockStyle(block);\n\n return ParagraphComp ? (\n <ParagraphComp key={key} style={style}>\n {children}\n </ParagraphComp>\n ) : (\n <p key={key} style={style}>\n {children}\n </p>\n );\n}\n\nfunction renderHeading(\n block: HeadingNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const HeadingComp = blocks?.heading;\n const children = renderInlineContent(block.children, blocks, modifiers);\n const style = getBlockStyle(block);\n\n if (HeadingComp) {\n return (\n <HeadingComp key={key} level={block.level} style={style}>\n {children}\n </HeadingComp>\n );\n }\n\n const Tag = `h${block.level}` as const;\n return (\n <Tag key={key} style={style}>\n {children}\n </Tag>\n );\n}\n\nfunction renderQuote(\n block: QuoteNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig\n): ReactNode {\n const QuoteComp = blocks?.quote;\n const children = renderInlineContent(block.children, blocks, modifiers);\n const style = getBlockStyle(block);\n\n return QuoteComp ? (\n <QuoteComp key={key} style={style}>\n {children}\n </QuoteComp>\n ) : (\n <blockquote key={key} className=\"bb-quote\" style={style}>\n {children}\n </blockquote>\n );\n}\n\nconst DEFAULT_CODE_THEME = 'github-dark';\n\nfunction renderCode(\n block: CodeNode,\n key: number,\n blocks?: CustomBlocksConfig,\n options?: RenderOptions\n): ReactNode {\n const CodeComp = blocks?.code;\n const plainText = getPlainText(block.children);\n\n if (CodeComp) {\n // Custom renderers get the raw editor value, not the Shiki grammar id, so\n // they can map it to whatever highlighter they use.\n return (\n <CodeComp key={key} plainText={plainText} language={block.language}>\n {plainText}\n </CodeComp>\n );\n }\n\n return (\n <CodeBlock\n key={key}\n plainText={plainText}\n language={block.language}\n theme={options?.codeTheme ?? DEFAULT_CODE_THEME}\n copyButton={options?.codeCopyButton ?? false}\n />\n );\n}\n\n// ── GitHub-style Table / Quote / Code Styling ────────────────────────\n//\n// Kept in sync with the Astro renderer so one shared theme covers both. These\n// are classes rather than inline styles for two reasons: `nth-child` striping\n// and `:hover` can't be expressed inline, and inline styles would outrank a\n// consumer's own CSS, defeating the `--bb-*` custom properties below.\n\n// Bordered cells, a shaded header, zebra-striped body rows, and horizontal\n// scroll on overflow. `display:block` is what makes the table scrollable.\nconst TABLE_CSS =\n '.bb-table{display:block;width:max-content;max-width:100%;margin:1rem 0;overflow:auto;' +\n 'border-collapse:collapse;border-spacing:0;border:1px solid var(--bb-table-border,#d0d7de)}' +\n '.bb-table th,.bb-table td{padding:.375rem .8125rem;' +\n 'border:1px solid var(--bb-table-border,#d0d7de);text-align:left}' +\n '.bb-table th{font-weight:600;background:var(--bb-table-header-bg,#f6f8fa)}' +\n '.bb-table tbody tr{background:var(--bb-table-row-bg,#fff)}' +\n '.bb-table tbody tr:nth-child(2n){background:var(--bb-table-stripe-bg,#f6f8fa)}';\n\n// A muted left border with indented, dimmed text — GitHub's markdown quote,\n// which has no background fill.\nconst QUOTE_CSS =\n '.bb-quote{margin:1rem 0;padding:0 1rem;color:var(--bb-quote-fg,#57606a);' +\n 'border-left:.25rem solid var(--bb-quote-border,#d0d7de)}';\n\n// Shiki inlines the theme's background and text colors onto the `<pre>` it\n// generates, so we only add padding, rounding, and typography around it. The\n// pre-highlight fallback `<pre>` has no such inline colors, so it carries its\n// own — defaulting to the github-dark values to match DEFAULT_CODE_THEME.\n// Retheme them alongside `codeTheme` via --bb-code-fallback-*.\nconst CODE_CSS =\n '.bb-code{position:relative;margin:1rem 0}' +\n '.bb-code pre{margin:0;padding:1rem;border-radius:6px;font-size:.875rem;' +\n 'line-height:1.45;tab-size:2;overflow-x:auto}' +\n '.bb-code-pre{background:var(--bb-code-fallback-bg,#24292e);' +\n 'color:var(--bb-code-fallback-fg,#e1e4e8)}' +\n '.bb-code-copy{position:absolute;top:.5rem;right:.5rem;padding:.25rem .5rem;' +\n 'font-size:.75rem;line-height:1;color:var(--bb-code-copy-fg,#e1e4e8);' +\n 'background:var(--bb-code-copy-bg,rgba(110,118,129,.4));' +\n 'border:1px solid var(--bb-code-copy-border,rgba(240,246,252,.1));' +\n 'border-radius:6px;cursor:pointer;opacity:0;transition:opacity .15s ease,background .15s ease}' +\n '.bb-code:hover .bb-code-copy,.bb-code-copy:focus-visible{opacity:1}' +\n '.bb-code-copy:hover{background:var(--bb-code-copy-hover-bg,rgba(110,118,129,.6))}';\n\nfunction renderImage(block: ImageNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const ImageComp = blocks?.image;\n\n if (ImageComp) {\n return (\n <ImageComp\n key={key}\n image={block.image}\n caption={block.caption}\n imageAlign={block.imageAlign}\n />\n );\n }\n\n const align = block.imageAlign || 'center';\n const alignStyle: CSSProperties = {\n textAlign: align,\n };\n\n return (\n <figure key={key} style={alignStyle}>\n <img\n src={block.image.url}\n alt={block.image.alternativeText || ''}\n width={block.image.width}\n height={block.image.height}\n />\n {block.caption && <figcaption>{block.caption}</figcaption>}\n </figure>\n );\n}\n\nfunction renderHorizontalLine(\n _block: HorizontalLineNode,\n key: number,\n blocks?: CustomBlocksConfig\n): ReactNode {\n const HrComp = blocks?.['horizontal-line'];\n return HrComp ? <HrComp key={key} /> : <hr key={key} />;\n}\n\nfunction renderMediaEmbed(\n block: MediaEmbedNode,\n key: number,\n blocks?: CustomBlocksConfig\n): ReactNode {\n const EmbedComp = blocks?.['media-embed'];\n\n if (EmbedComp) {\n return <EmbedComp key={key} url={block.url} originalUrl={block.originalUrl} />;\n }\n\n return (\n <div key={key} style={{ position: 'relative', paddingBottom: '56.25%', height: 0 }}>\n <iframe\n src={block.url}\n style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', border: 0 }}\n allowFullScreen\n title=\"Embedded media\"\n />\n </div>\n );\n}\n\n// ── Embed (Generic iframe) Rendering ─────────────────────────────────\n\n// The sanitized `embedHtml` carries the provider's own width/height, so make\n// the iframe fill the aspect-ratio box instead. Inline styles can't reach into\n// `dangerouslySetInnerHTML` markup, so this ships as a stylesheet — injected\n// only when the content actually renders a default embed.\nconst EMBED_FRAME_CSS =\n '.bb-embed-frame{overflow:hidden}' +\n '.bb-embed-frame iframe{width:100%;height:100%;border:0;display:block}';\n\n/**\n * Renders an `embed` node from its plugin-sanitized `embedHtml` — rebuilt from\n * an attribute allowlist over an https-only `src`, with scripts, event handlers\n * and inline styles stripped. `url` / `iframe` only round-trip the editor UI and\n * are deliberately ignored. Override the `embed` block to render the parsed\n * parts yourself instead of injecting the stored HTML.\n */\nfunction renderEmbed(block: EmbedNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const EmbedComp = blocks?.embed;\n\n if (EmbedComp) {\n return (\n <EmbedComp\n key={key}\n source={block.source}\n url={block.url}\n iframe={block.iframe}\n embedHtml={block.embedHtml}\n embedSrc={block.embedSrc}\n provider={block.provider}\n thumbnail={block.thumbnail}\n aspectRatio={block.aspectRatio}\n customAspectRatio={block.customAspectRatio}\n alignment={block.alignment}\n caption={block.caption}\n title={block.title}\n />\n );\n }\n\n const alignment: MediaAlignment = block.alignment ?? 'center';\n const ratio = getAspectRatio(block.aspectRatio, block.customAspectRatio);\n\n // An embed whose source was cleared has no markup to inject. Fall back to a\n // plain link when there's still a URL, so the block isn't silently lost.\n const body = block.embedHtml ? (\n <div\n className=\"bb-embed-frame\"\n style={getMediaFrameStyle(alignment, ratio)}\n dangerouslySetInnerHTML={{ __html: block.embedHtml }}\n />\n ) : block.url ? (\n <a className=\"bb-embed-fallback\" href={block.url} target=\"_blank\" rel=\"noopener noreferrer\">\n {block.title || block.url}\n </a>\n ) : null;\n\n if (!body) return null;\n\n return (\n <figure\n key={key}\n className={`bb-embed align-${alignment}`}\n style={getMediaFigureStyle(alignment)}\n aria-label={block.title}\n >\n {body}\n {block.caption && (\n <figcaption className=\"bb-embed-caption\" style={MEDIA_CAPTION_STYLE}>\n {block.caption}\n </figcaption>\n )}\n </figure>\n );\n}\n\n// ── Video (Provider-aware Player) Rendering ──────────────────────────\n\nfunction renderVideo(block: VideoNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const VideoComp = blocks?.video;\n\n if (VideoComp) {\n return (\n <VideoComp\n key={key}\n provider={block.provider}\n url={block.url ?? ''}\n assetId={block.assetId}\n playbackId={block.playbackId}\n file={block.file}\n poster={block.poster}\n title={block.title}\n caption={block.caption}\n transcript={block.transcript}\n player={block.player}\n alignment={block.alignment}\n aspectRatio={block.aspectRatio}\n customAspectRatio={block.customAspectRatio}\n />\n );\n }\n\n return (\n <Video\n key={key}\n provider={block.provider}\n url={block.url ?? ''}\n assetId={block.assetId}\n playbackId={block.playbackId}\n file={block.file}\n poster={block.poster}\n title={block.title}\n caption={block.caption}\n transcript={block.transcript}\n player={block.player}\n alignment={block.alignment}\n aspectRatio={block.aspectRatio}\n customAspectRatio={block.customAspectRatio}\n instanceId={block.file?.id ?? key}\n />\n );\n}\n\n// ── Social Embed Rendering ───────────────────────────────────────────\n\nfunction renderSocialEmbed(\n block: SocialEmbedNode,\n key: number,\n blocks?: CustomBlocksConfig\n): ReactNode {\n const SocialComp = blocks?.['social-embed'];\n\n if (SocialComp) {\n return (\n <SocialComp\n key={key}\n platform={block.platform}\n url={block.url}\n embedCode={block.embedCode}\n oembed={block.oembed}\n alignment={block.alignment}\n caption={block.caption}\n />\n );\n }\n\n return (\n <SocialEmbed\n key={key}\n platform={block.platform}\n url={block.url}\n embedCode={block.embedCode}\n oembed={block.oembed}\n alignment={block.alignment}\n caption={block.caption}\n />\n );\n}\n\n// ── Audio (HTML5 Player) Rendering ───────────────────────────────────\n\n// Alignment class → flexbox cross-axis placement of the player within the\n// figure. `none` stretches the player to fill the available width.\nconst AUDIO_ALIGN_ITEMS: Record<AudioAlignment, CSSProperties['alignItems']> = {\n left: 'flex-start',\n center: 'center',\n right: 'flex-end',\n none: 'stretch',\n};\n\nfunction renderAudio(block: AudioNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const AudioComp = blocks?.audio;\n\n if (AudioComp) {\n return (\n <AudioComp\n key={key}\n file={block.file}\n title={block.title}\n caption={block.caption}\n player={block.player ?? {}}\n alignment={block.alignment}\n />\n );\n }\n\n const { file, title, caption } = block;\n const player = block.player ?? {};\n const alignment: AudioAlignment = block.alignment ?? 'center';\n // Stable id linking the caption to the player via aria-describedby. Prefer the\n // Media-Library id, fall back to the file hash, then the block index.\n const capId = caption ? `bb-audio-cap-${file.id ?? file.hash ?? key}` : undefined;\n\n return (\n <figure\n key={key}\n className={`bb-audio align-${alignment}`}\n style={{\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n margin: '1rem 0',\n alignItems: AUDIO_ALIGN_ITEMS[alignment],\n }}\n >\n {title && (\n <figcaption className=\"bb-audio-title\" style={{ fontWeight: 600 }}>\n {title}\n </figcaption>\n )}\n <audio\n className=\"bb-audio-player\"\n src={file.url}\n controls={player.controls ?? true}\n autoPlay={player.autoplay ?? false}\n loop={player.loop ?? false}\n preload={player.preload ?? 'metadata'}\n aria-label={title || 'Audio player'}\n aria-describedby={capId}\n style={{ width: '100%', maxWidth: alignment === 'none' ? '100%' : '32rem' }}\n >\n Your browser does not support the audio element. <a href={file.url}>Download the audio</a>.\n </audio>\n {caption && (\n <figcaption\n id={capId}\n className=\"bb-audio-caption\"\n style={{ fontSize: '0.875rem', color: '#6b7280' }}\n >\n {caption}\n </figcaption>\n )}\n </figure>\n );\n}\n\n// ── Diagram (Mermaid) Rendering ──────────────────────────────────────\n\nfunction renderDiagram(block: DiagramNode, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const DiagramComp = blocks?.diagram;\n const code = block.value ?? '';\n\n if (DiagramComp) {\n return <DiagramComp key={key} code={code} format={block.format} />;\n }\n\n return <MermaidDiagram key={key} value={code} />;\n}\n\n// ── Callout (Admonition) Rendering ───────────────────────────────────\n\n// GitHub-style alert metadata: accent color, default label and octicon path.\nconst CALLOUT_VARIANTS: Record<CalloutVariant, { color: string; label: string; icon: string }> = {\n note: {\n color: '#0969da',\n label: 'Note',\n icon: 'M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z',\n },\n tip: {\n color: '#1a7f37',\n label: 'Tip',\n icon: 'M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z',\n },\n important: {\n color: '#8250df',\n label: 'Important',\n icon: 'M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z',\n },\n warning: {\n color: '#9a6700',\n label: 'Warning',\n icon: 'M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z',\n },\n caution: {\n color: '#d1242f',\n label: 'Caution',\n icon: 'M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .39.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.39.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z',\n },\n};\n\nfunction renderCallout(\n block: CalloutNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig,\n options?: RenderOptions\n): ReactNode {\n const variant: CalloutVariant = CALLOUT_VARIANTS[block.variant] ? block.variant : 'note';\n const meta = CALLOUT_VARIANTS[variant];\n const childNodes = block.children.map((child, index) =>\n renderBlock(child, index, blocks, modifiers, options)\n );\n // Collapse the outer block margins (e.g. a paragraph's default top/bottom\n // margin) so the body sits flush within the callout's padding, keeping the\n // vertical spacing balanced instead of leaving a gap below the content.\n const lastIndex = childNodes.length - 1;\n const children = childNodes.map((node, index) => {\n if (!isValidElement(node)) return node;\n const element = node as ReactElement<{ style?: CSSProperties }>;\n const collapsed: CSSProperties = {};\n if (index === 0) collapsed.marginTop = 0;\n if (index === lastIndex) collapsed.marginBottom = 0;\n return cloneElement(element, { style: { ...element.props.style, ...collapsed } });\n });\n\n const CalloutComp = blocks?.callout;\n if (CalloutComp) {\n return (\n <CalloutComp key={key} variant={variant} title={block.title}>\n {children}\n </CalloutComp>\n );\n }\n\n const title = block.title?.trim() ? block.title : meta.label;\n\n return (\n <aside\n key={key}\n className={`bb-callout bb-callout-${variant}`}\n role=\"note\"\n style={{\n borderLeft: `0.25rem solid ${meta.color}`,\n // Subtle accent-tinted background (~8% opacity) to match the editor preview.\n backgroundColor: `${meta.color}14`,\n padding: '0.5rem 1rem',\n margin: '1rem 0',\n }}\n >\n <p\n className=\"bb-callout-title\"\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '0.5rem',\n // GitHub spacing: tight title line with a 1rem gap before the body.\n lineHeight: 1,\n margin: '0 0 1rem',\n fontWeight: 600,\n color: meta.color,\n }}\n >\n <svg\n className=\"bb-callout-icon\"\n viewBox=\"0 0 16 16\"\n width=\"16\"\n height=\"16\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d={meta.icon} />\n </svg>\n {title}\n </p>\n {children}\n </aside>\n );\n}\n\n// ── Details / Summary (Collapsible) Rendering ────────────────────────\n\nfunction renderDetails(\n block: DetailsNode,\n key: number,\n blocks?: CustomBlocksConfig,\n modifiers?: CustomModifiersConfig,\n options?: RenderOptions\n): ReactNode {\n const children = block.children.map((child, index) =>\n renderBlock(child, index, blocks, modifiers, options)\n );\n\n const DetailsComp = blocks?.details;\n if (DetailsComp) {\n return (\n <DetailsComp key={key} summary={block.summary} defaultOpen={block.defaultOpen}>\n {children}\n </DetailsComp>\n );\n }\n\n return (\n <details key={key} className=\"bb-details\" open={block.defaultOpen}>\n <summary className=\"bb-details-summary\">{block.summary}</summary>\n {children}\n </details>\n );\n}\n\n// ── Button (CTA / File Download) Rendering ───────────────────────────\n\n// Default button styling shipped with the renderer so hover/focus work with\n// zero setup (no stylesheet import, no consumer CSS). Hover colors come from the\n// `--bb-button-hover-*` custom properties getButtonStyle() sets, falling back to\n// the base color props (`--bb-button-*`) so a button without hover colors keeps\n// its colors on hover. `!important` lets these win over the inline base colors.\nconst BUTTON_HOVER_CSS =\n '.bb-button{transition:background-color .15s ease,color .15s ease}' +\n '.bb-button:hover{' +\n 'background-color:var(--bb-button-hover-bg,var(--bb-button-bg))!important;' +\n 'color:var(--bb-button-hover-color,var(--bb-button-color))!important}' +\n '.bb-button:focus-visible{outline:2px solid currentColor;outline-offset:2px}';\n\n// True if any block in the tree has the given type, so the styles that block\n// needs are only injected when it's actually rendered. Recurses into child\n// arrays (e.g. a button nested inside a details block).\nfunction contentHasBlock(nodes: BlockNode[], type: BlockNode['type']): boolean {\n for (const node of nodes) {\n if (node.type === type) return true;\n const children = (node as { children?: unknown }).children;\n if (Array.isArray(children) && contentHasBlock(children as BlockNode[], type)) return true;\n }\n return false;\n}\n\nfunction getButtonStyle(style?: ButtonStyle): CSSProperties {\n // Sensible defaults so an unstyled button still looks like a button. Hover\n // colors can't be set inline, so they ride along as CSS custom properties\n // that the default hover rule (see BUTTON_HOVER_CSS) consumes.\n const out: Record<string, string | number> = {\n display: 'inline-block',\n textDecoration: 'none',\n cursor: 'pointer',\n };\n if (!style) return out as CSSProperties;\n // Mirror the base colors into custom properties too, so the `:hover` rule can\n // fall back to them when no hover color is set (otherwise an unset hover var\n // would compute to inherited/transparent on hover and the button would lose\n // its color).\n if (style.backgroundColor) {\n out.backgroundColor = style.backgroundColor;\n out['--bb-button-bg'] = style.backgroundColor;\n }\n if (style.textColor) {\n out.color = style.textColor;\n out['--bb-button-color'] = style.textColor;\n }\n if (style.borderRadius) out.borderRadius = style.borderRadius;\n if (style.fontSize) out.fontSize = style.fontSize;\n if (style.fontWeight) out.fontWeight = style.fontWeight;\n if (style.padding) out.padding = style.padding;\n if (style.border) out.border = style.border;\n if (style.hoverBackgroundColor) out['--bb-button-hover-bg'] = style.hoverBackgroundColor;\n if (style.hoverTextColor) out['--bb-button-hover-color'] = style.hoverTextColor;\n return out as CSSProperties;\n}\n\n// Forces a real file download instead of letting the browser preview it.\n// The native `download` attribute is ignored for cross-origin URLs (the common\n// case for Strapi/CDN assets), so browsers open renderable files (PDF, video,\n// images) inline. We fetch the asset as a blob and trigger a download from a\n// same-origin object URL. If that fails (e.g. CORS-blocked), we fall back to\n// native navigation so the link still does something.\nfunction handleFileDownload(\n event: ReactMouseEvent<HTMLAnchorElement>,\n url: string,\n name: string\n): void {\n // Respect modifier clicks (open in new tab, etc.) and non-primary buttons.\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n\n event.preventDefault();\n\n void (async () => {\n try {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`Request failed: ${response.status}`);\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = objectUrl;\n anchor.download = name;\n document.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n URL.revokeObjectURL(objectUrl);\n } catch {\n // CORS-blocked or offline — fall back to native navigation.\n window.location.href = url;\n }\n })();\n}\n\nfunction renderButton(block: ButtonElement, key: number, blocks?: CustomBlocksConfig): ReactNode {\n const ButtonComp = blocks?.button;\n if (ButtonComp) {\n return (\n <ButtonComp\n key={key}\n label={block.label}\n buttonType={block.buttonType}\n alignment={block.alignment}\n link={block.link}\n file={block.file}\n showFileSize={block.showFileSize}\n showFileIcon={block.showFileIcon}\n filePreview={block.filePreview}\n style={block.style}\n cssClass={block.cssClass}\n />\n );\n }\n\n const style = getButtonStyle(block.style);\n const className = block.cssClass ? `bb-button ${block.cssClass}` : 'bb-button';\n\n let control: ReactNode;\n if (block.buttonType === 'file' && block.file) {\n const file = block.file;\n const icon = block.showFileIcon ? getFileIcon(file) : null;\n const size =\n block.showFileSize && typeof file.size === 'number' ? formatFileSize(file.size) : null;\n const filePreview = block.filePreview === true;\n control = filePreview ? (\n <a\n href={file.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n aria-label={`Preview ${file.name}`}\n className={className}\n style={style}\n >\n {icon && (\n <span className=\"bb-button-icon\" aria-hidden=\"true\">\n {icon}{' '}\n </span>\n )}\n {block.label}\n {size && <span className=\"bb-button-size\"> ({size})</span>}\n </a>\n ) : (\n <a\n href={file.url}\n download={file.name}\n aria-label={`Download ${file.name}`}\n className={className}\n style={style}\n onClick={(event) => handleFileDownload(event, file.url, file.name)}\n >\n {icon && (\n <span className=\"bb-button-icon\" aria-hidden=\"true\">\n {icon}{' '}\n </span>\n )}\n {block.label}\n {size && <span className=\"bb-button-size\"> ({size})</span>}\n </a>\n );\n } else if (block.link) {\n const link = block.link;\n control = (\n <a\n href={link.url}\n target={link.target}\n rel={link.rel}\n aria-label={link.ariaLabel}\n className={className}\n style={style}\n >\n {block.label}\n </a>\n );\n } else {\n // No link/file payload — render the label as a styled, non-navigating span.\n control = (\n <span className={className} style={style}>\n {block.label}\n </span>\n );\n }\n\n const alignment = block.alignment ?? 'left';\n if (alignment === 'none') {\n return <Fragment key={key}>{control}</Fragment>;\n }\n\n return (\n <div\n key={key}\n className=\"bb-button-wrapper\"\n style={{ textAlign: alignment, margin: '0.5rem 0' }}\n >\n {control}\n </div>\n );\n}\n\n// ── Main Component ───────────────────────────────────────────────────\n\nexport function BlocksRenderer({\n content,\n blocks,\n modifiers,\n codeTheme = DEFAULT_CODE_THEME,\n codeCopyButton = false,\n}: BlocksRendererProps): ReactNode {\n if (!content || !Array.isArray(content) || content.length === 0) {\n return null;\n }\n\n // Ship the styles the built-in blocks need inline, so there's no stylesheet to\n // import. Skipped when the consumer overrides the block, since their markup\n // won't carry the `.bb-*` classes these rules target.\n const needsButtonCss = !blocks?.button && contentHasBlock(content, 'button');\n const needsEmbedCss = !blocks?.embed && contentHasBlock(content, 'embed');\n const needsTableCss = !blocks?.table && contentHasBlock(content, 'table');\n const needsQuoteCss = !blocks?.quote && contentHasBlock(content, 'quote');\n const needsCodeCss = !blocks?.code && contentHasBlock(content, 'code');\n\n const options: RenderOptions = { codeTheme, codeCopyButton };\n\n return (\n <>\n {needsButtonCss && <style>{BUTTON_HOVER_CSS}</style>}\n {needsEmbedCss && <style>{EMBED_FRAME_CSS}</style>}\n {needsTableCss && <style>{TABLE_CSS}</style>}\n {needsQuoteCss && <style>{QUOTE_CSS}</style>}\n {needsCodeCss && <style>{CODE_CSS}</style>}\n {content.map((block, index) => renderBlock(block, index, blocks, modifiers, options))}\n </>\n );\n}\n","import type { CSSProperties } from 'react';\n\nimport type { MediaAlignment } from './types';\n\n/**\n * Aspect-ratio resolution is shared with the Astro renderer and lives in\n * `@qkix/better-blocks-core`. Re-exported so this package's imports are\n * unaffected.\n */\nexport { getAspectRatio } from '@qkix/better-blocks-core';\n\n// Alignment → flexbox cross-axis placement of the media box within its figure.\n// `none` stretches it to fill the available width. Mirrors the `audio` block.\nexport const MEDIA_ALIGN_ITEMS: Record<MediaAlignment, CSSProperties['alignItems']> = {\n left: 'flex-start',\n center: 'center',\n right: 'flex-end',\n none: 'stretch',\n};\n\n// Width cap for aligned media. Without it `left` / `center` / `right` would all\n// look identical, since a full-width box has nowhere to sit. `none` opts out.\nexport const MEDIA_MAX_WIDTH = '48rem';\n\n/** Figure wrapper shared by the `embed` and `video` blocks. */\nexport function getMediaFigureStyle(alignment: MediaAlignment): CSSProperties {\n return {\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n margin: '1rem 0',\n alignItems: MEDIA_ALIGN_ITEMS[alignment],\n };\n}\n\n/** The aspect-ratio box holding the iframe / video element. */\nexport function getMediaFrameStyle(alignment: MediaAlignment, aspectRatio: string): CSSProperties {\n return {\n width: '100%',\n maxWidth: alignment === 'none' ? '100%' : MEDIA_MAX_WIDTH,\n aspectRatio,\n };\n}\n\nexport const MEDIA_CAPTION_STYLE: CSSProperties = {\n fontSize: '0.875rem',\n color: '#6b7280',\n};\n","'use client';\n\nimport { useEffect, useRef, useState, type ReactNode } from 'react';\n\nimport type { BundledLanguage } from 'shiki';\n\nimport { normalizeCodeLang } from './code';\n\ntype Highlighter = Awaited<ReturnType<typeof import('shiki').createHighlighter>>;\n\n// Shiki resolves grammars and themes asynchronously, so it cannot highlight\n// synchronously during SSR the way KaTeX does. We lazy-load it once on the\n// client and cache a highlighter per theme, so the heavy library stays out of\n// the server bundle and is only fetched when a code block is actually rendered.\nconst highlighters = new Map<string, Promise<Highlighter>>();\n\nfunction loadHighlighter(theme: string, lang: string): Promise<Highlighter> {\n let highlighter = highlighters.get(theme);\n\n if (!highlighter) {\n highlighter = import('shiki').then((shiki) =>\n shiki.createHighlighter({ themes: [theme], langs: [lang] })\n );\n highlighters.set(theme, highlighter);\n return highlighter;\n }\n\n // A second block on the page may use a different language than the one the\n // highlighter was built with; load it into the existing instance rather than\n // spinning up another highlighter for the same theme.\n return highlighter.then(async (instance) => {\n if (!instance.getLoadedLanguages().includes(lang)) {\n // normalizeCodeLang only ever returns a bundled grammar id, but it can't\n // be typed as one without leaking Shiki's types into ./code.\n await instance.loadLanguage(lang as BundledLanguage);\n }\n return instance;\n });\n}\n\nfunction CopyButton({ plainText }: { plainText: string }): ReactNode {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (timer.current) clearTimeout(timer.current);\n };\n }, []);\n\n const handleCopy = () => {\n if (!navigator.clipboard) return;\n navigator.clipboard.writeText(plainText).then(() => {\n setCopied(true);\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), 2000);\n });\n };\n\n return (\n <button className=\"bb-code-copy\" type=\"button\" onClick={handleCopy} aria-label=\"Copy code\">\n {copied ? 'Copied!' : 'Copy'}\n </button>\n );\n}\n\n/**\n * Renders a code block, syntax-highlighted with Shiki on the client.\n *\n * SSR / static export and the first client render both emit the raw source in a\n * plain `<pre>` (so hydration matches), then swap in the highlighted markup\n * after mount. If Shiki fails to load or the grammar is unavailable, the plain\n * `<pre>` remains as a graceful fallback instead of crashing the page.\n */\nexport function CodeBlock({\n plainText,\n language,\n theme,\n copyButton,\n}: {\n plainText: string;\n language?: string;\n theme: string;\n copyButton: boolean;\n}): ReactNode {\n const [html, setHtml] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n setHtml(null);\n\n if (!plainText) return;\n\n const lang = normalizeCodeLang(language);\n\n loadHighlighter(theme, lang)\n .then((highlighter) => highlighter.codeToHtml(plainText, { lang, theme }))\n .then((highlighted) => {\n if (!cancelled) setHtml(highlighted);\n })\n .catch(() => {\n // Leave the plain-source fallback in place on load/parse errors.\n if (!cancelled) setHtml(null);\n });\n\n return () => {\n cancelled = true;\n };\n }, [plainText, language, theme]);\n\n return (\n <div className=\"bb-code\">\n {html ? (\n <div dangerouslySetInnerHTML={{ __html: html }} />\n ) : (\n <pre className=\"bb-code-pre\">\n <code>{plainText}</code>\n </pre>\n )}\n {copyButton && <CopyButton plainText={plainText} />}\n </div>\n );\n}\n","/**\n * Code language resolution moved to `@qkix/better-blocks-core`, where the Astro\n * renderer reads it from too. Re-exported so this package's imports — and the\n * tests that pinned the behaviour down — keep working unchanged.\n */\nexport { normalizeCodeLang } from '@qkix/better-blocks-core';\n","'use client';\n\nimport { useEffect, useState, type ReactNode } from 'react';\n\n// Mermaid needs a real DOM to measure text, so it cannot render synchronously\n// during SSR the way KaTeX does. We lazy-load it once on the client and cache\n// the initialized instance so the heavy library stays out of the server bundle\n// and is only fetched when a diagram is actually rendered.\nlet mermaidPromise: Promise<typeof import('mermaid').default> | null = null;\n\nfunction loadMermaid() {\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => {\n const mermaid = mod.default;\n mermaid.initialize({ startOnLoad: false });\n return mermaid;\n });\n }\n return mermaidPromise;\n}\n\n// Unique, deterministic-per-render id for mermaid's temporary render node.\nlet renderCount = 0;\n\n/**\n * Renders a Mermaid diagram to inline SVG on the client.\n *\n * SSR / static export and the first client render both emit the raw Mermaid\n * source inside a `<pre>` (so hydration matches), then swap in the SVG after\n * mount. If Mermaid fails to parse the diagram, the raw source remains as a\n * graceful fallback instead of crashing the page.\n */\nexport function MermaidDiagram({ value }: { value: string }): ReactNode {\n const [svg, setSvg] = useState<string | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n setSvg(null);\n\n if (!value) return;\n\n loadMermaid()\n .then((mermaid) => mermaid.render(`bb-mermaid-${renderCount++}`, value))\n .then(({ svg }) => {\n if (!cancelled) setSvg(svg);\n })\n .catch(() => {\n // Leave the raw-source fallback in place on parse/render errors.\n if (!cancelled) setSvg(null);\n });\n\n return () => {\n cancelled = true;\n };\n }, [value]);\n\n if (svg) {\n return <div className=\"mermaid-diagram\" dangerouslySetInnerHTML={{ __html: svg }} />;\n }\n\n return <pre className=\"mermaid-source\">{value}</pre>;\n}\n","'use client';\n\nimport { useEffect, useMemo, useRef, type CSSProperties, type ReactNode } from 'react';\n\nimport type { SocialEmbedOembed, SocialEmbedAlignment, SocialPlatform } from './types';\n\n// Globals the platform widget scripts expose once loaded. Every hop is optional\n// so we can call the processor defensively without assuming the script is ready.\ninterface SocialWindow {\n twttr?: { widgets?: { load?: () => void } };\n instgrm?: { Embeds?: { process?: () => void } };\n FB?: { XFBML?: { parse?: () => void } };\n tiktokEmbed?: { lib?: { render?: (elements: Element[]) => void } };\n PinUtils?: { build?: () => void };\n}\n\ninterface WidgetScript {\n src: string;\n // Re-runs the platform's embed processor over freshly-mounted markup. Returns\n // `false` when the platform global isn't available (script blocked, or the\n // vendor renamed its API), which makes the caller re-inject the script so it\n // rescans the document on its own.\n process: (container: HTMLElement) => boolean;\n}\n\nconst socialWindow = (): SocialWindow => window as unknown as SocialWindow;\n\n// Widget enhancement scripts per platform. `src` is injected once and, after it\n// loads, `process` upgrades the mounted markup — this also covers remounts and\n// SPA navigations, where the script is long since loaded and only the new\n// markup needs processing. LinkedIn renders a self-contained `<iframe>` and\n// needs no script.\nconst WIDGET_SCRIPTS: Record<SocialPlatform, WidgetScript | null> = {\n twitter: {\n src: 'https://platform.twitter.com/widgets.js',\n process: () => {\n const load = socialWindow().twttr?.widgets?.load;\n load?.();\n return Boolean(load);\n },\n },\n instagram: {\n src: 'https://www.instagram.com/embed.js',\n process: () => {\n const process = socialWindow().instgrm?.Embeds?.process;\n process?.();\n return Boolean(process);\n },\n },\n tiktok: {\n src: 'https://www.tiktok.com/embed.js',\n process: (container) => {\n const render = socialWindow().tiktokEmbed?.lib?.render;\n render?.(Array.from(container.querySelectorAll('.tiktok-embed')));\n return Boolean(render);\n },\n },\n pinterest: {\n src: 'https://assets.pinterest.com/js/pinit.js',\n process: () => {\n const build = socialWindow().PinUtils?.build;\n build?.();\n return Boolean(build);\n },\n },\n facebook: {\n src: 'https://connect.facebook.net/en_US/sdk.js',\n process: () => {\n const parse = socialWindow().FB?.XFBML?.parse;\n parse?.();\n return Boolean(parse);\n },\n },\n linkedin: null,\n};\n\n// Human-readable provider name used for the a11y label and fallback card when\n// the oEmbed payload doesn't supply its own `providerName`.\nconst PLATFORM_LABELS: Record<SocialPlatform, string> = {\n twitter: 'X',\n instagram: 'Instagram',\n facebook: 'Facebook',\n tiktok: 'TikTok',\n linkedin: 'LinkedIn',\n pinterest: 'Pinterest',\n};\n\n// Marks the `<script>` tags we injected ourselves. Deduping on `src` alone is a\n// trap: TikTok's oEmbed payload always ships `<script src=\".../embed.js\">`\n// inline, and a script inserted through `dangerouslySetInnerHTML` sits in the\n// DOM without ever executing — matching it would make us skip the real load and\n// leave the blockquote un-upgraded forever.\nconst SCRIPT_MARKER = 'data-bb-social-script';\n\n// One shared load per script URL, so multiple embeds of the same platform don't\n// inject duplicate `<script>` tags. Keyed by src; resolves once the script has\n// loaded (or immediately if a tag we injected earlier is already present).\nconst scriptPromises = new Map<string, Promise<void>>();\n\n// Platforms whose script we already re-injected once after `process` reported a\n// missing global — the retry gets a single shot, so a permanently blocked\n// script can't trigger a request per mount.\nconst reinjected = new Set<SocialPlatform>();\n\nfunction injectScript(src: string, platform: SocialPlatform): Promise<void> {\n const promise = new Promise<void>((resolve) => {\n const script = document.createElement('script');\n script.src = src;\n script.async = true;\n script.setAttribute(SCRIPT_MARKER, platform);\n // Resolve on error too: the embed degrades to the raw platform markup\n // instead of the script rejection bubbling up unhandled.\n script.addEventListener('load', () => resolve());\n script.addEventListener('error', () => resolve());\n document.body.appendChild(script);\n });\n scriptPromises.set(src, promise);\n return promise;\n}\n\nfunction loadWidgetScript(src: string, platform: SocialPlatform): Promise<void> {\n if (typeof document === 'undefined') return Promise.resolve();\n\n const cached = scriptPromises.get(src);\n if (cached) return cached;\n\n const existing = document.querySelector(`script[${SCRIPT_MARKER}=\"${platform}\"]`);\n if (existing) {\n const resolved = Promise.resolve();\n scriptPromises.set(src, resolved);\n return resolved;\n }\n\n return injectScript(src, platform);\n}\n\n// Last resort when the platform exposes no processor we can call: drop our\n// script and inject a fresh copy so the widget rescans the document.\nfunction reinjectWidgetScript(src: string, platform: SocialPlatform): void {\n if (typeof document === 'undefined' || reinjected.has(platform)) return;\n reinjected.add(platform);\n\n document.querySelectorAll(`script[${SCRIPT_MARKER}=\"${platform}\"]`).forEach((script) => {\n script.remove();\n });\n scriptPromises.delete(src);\n void injectScript(src, platform);\n}\n\n// `<script>` tags injected via `dangerouslySetInnerHTML` never execute, so the\n// platform script inline in an oEmbed payload (TikTok always, hand-pasted\n// Instagram snippets often) is dead weight that only confuses script dedupe.\n// We inject the widget script ourselves instead.\nconst SCRIPT_TAG = /<script\\b[^>]*>[\\s\\S]*?<\\/script\\s*>/gi;\n\nfunction stripScripts(html: string | undefined): string | undefined {\n return html?.replace(SCRIPT_TAG, '');\n}\n\n// ── Fallback Link Card ───────────────────────────────────────────────\n\n// Rendered when neither `embedCode` nor `oembed.html` is available: a plain,\n// always-working link to the source post, enriched with the oEmbed thumbnail\n// and author when present.\nfunction FallbackCard({\n url,\n oembed,\n providerName,\n}: {\n url?: string;\n oembed?: SocialEmbedOembed;\n providerName: string;\n}): ReactNode {\n // Title first, author second, and only then the bare \"View on X\" prompt. The\n // provider line is dropped in that last case — with no title and no author it\n // would just repeat the prompt back (\"View on X\" over \"X\").\n const title = oembed?.title ?? (oembed?.author ? `Post by ${oembed.author}` : null);\n\n const style: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: '0.75rem',\n padding: '0.75rem 1rem',\n border: '1px solid #d0d7de',\n borderRadius: '0.5rem',\n textDecoration: 'none',\n color: 'inherit',\n textAlign: 'left',\n };\n\n // An embed saved with a manual `embedCode` and no source URL has nothing to\n // link to — render the same card as a plain element rather than an `href=\"\"`\n // anchor, which would reload the current page.\n const Tag = url ? 'a' : 'div';\n const linkProps = url ? { href: url, target: '_blank', rel: 'noopener noreferrer' } : {};\n\n return (\n <Tag className=\"bb-social-embed-fallback\" style={style} {...linkProps}>\n {oembed?.thumbnailUrl && (\n <img\n className=\"bb-social-embed-fallback-thumb\"\n src={oembed.thumbnailUrl}\n alt=\"\"\n loading=\"lazy\"\n width={48}\n height={48}\n style={{ borderRadius: '0.25rem', objectFit: 'cover', flexShrink: 0 }}\n />\n )}\n <span className=\"bb-social-embed-fallback-body\">\n <span\n className=\"bb-social-embed-fallback-title\"\n style={{ display: 'block', fontWeight: 600 }}\n >\n {title ?? `View on ${providerName}`}\n </span>\n {title && (\n <span\n className=\"bb-social-embed-fallback-provider\"\n style={{ opacity: 0.7, fontSize: '0.875em' }}\n >\n {providerName}\n </span>\n )}\n </span>\n </Tag>\n );\n}\n\n// ── Social Embed ─────────────────────────────────────────────────────\n\n/**\n * Renders a Better Blocks `social-embed` node.\n *\n * HTML source priority: `embedCode` (manual override) → `oembed.html` →\n * a graceful fallback link card. The embed HTML is injected via\n * `dangerouslySetInnerHTML` with only its `<script>` tags removed (they never\n * execute there anyway; the widget script is loaded separately). It is NOT\n * otherwise sanitized, because social embeds rely on `<iframe>`/`<blockquote>`\n * markup that a sanitizer would strip. Trust boundary: this markup originates\n * from the platform's oEmbed API or a manual override pasted by a trusted\n * editor, so treat the CMS content as trusted.\n */\nexport function SocialEmbed({\n platform,\n url,\n embedCode,\n oembed,\n alignment = 'center',\n caption,\n}: {\n platform: SocialPlatform;\n url?: string;\n embedCode?: string;\n oembed?: SocialEmbedOembed;\n alignment?: SocialEmbedAlignment;\n caption?: string;\n}): ReactNode {\n const containerRef = useRef<HTMLDivElement>(null);\n const html = useMemo(() => stripScripts(embedCode ?? oembed?.html), [embedCode, oembed?.html]);\n\n useEffect(() => {\n if (!html) return;\n\n // Any iframe the platform ships in its markup (e.g. LinkedIn) should lazy-load.\n containerRef.current?.querySelectorAll('iframe').forEach((iframe) => {\n if (!iframe.hasAttribute('loading')) iframe.setAttribute('loading', 'lazy');\n });\n\n const widget = WIDGET_SCRIPTS[platform];\n if (!widget) return;\n\n let cancelled = false;\n void loadWidgetScript(widget.src, platform).then(() => {\n const container = containerRef.current;\n if (cancelled || !container) return;\n if (!widget.process(container)) reinjectWidgetScript(widget.src, platform);\n });\n\n return () => {\n cancelled = true;\n };\n }, [platform, html]);\n\n const providerName = oembed?.providerName ?? PLATFORM_LABELS[platform];\n const ariaLabel = oembed?.author\n ? `${providerName} post by ${oembed.author}`\n : `${providerName} post`;\n\n const figureStyle: CSSProperties = {\n margin: alignment === 'center' ? '1rem auto' : '1rem 0',\n textAlign: alignment,\n };\n\n return (\n <figure\n className={`bb-social-embed bb-social-embed-${platform} social-embed align-${alignment}`}\n aria-label={ariaLabel}\n style={figureStyle}\n >\n {html ? (\n <div\n ref={containerRef}\n className=\"bb-social-embed-html\"\n dangerouslySetInnerHTML={{ __html: html }}\n />\n ) : (\n <FallbackCard url={url} oembed={oembed} providerName={providerName} />\n )}\n {caption && <figcaption className=\"bb-social-embed-caption\">{caption}</figcaption>}\n </figure>\n );\n}\n","'use client';\n\nimport {\n useEffect,\n useRef,\n useState,\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n} from 'react';\n\nimport {\n MEDIA_CAPTION_STYLE,\n getAspectRatio,\n getMediaFigureStyle,\n getMediaFrameStyle,\n} from './media';\nimport type { AspectRatio, MediaAlignment, VideoFile, VideoPlayer, VideoProvider } from './types';\n\n// ── Optional HLS support ─────────────────────────────────────────────\n\n// Minimal structural type for the bits of hls.js we touch. Declaring it here\n// keeps the package free of an `hls.js` dependency (and of a build-time import\n// that consumers who don't need it would have to install anyway).\ntype HlsInstance = {\n loadSource: (url: string) => void;\n attachMedia: (media: HTMLMediaElement) => void;\n destroy: () => void;\n};\n\ntype HlsConstructor = {\n new (): HlsInstance;\n isSupported?: () => boolean;\n};\n\n// hls.js is picked up from `window.Hls` — set either by its UMD build loaded\n// from a <script> tag, or by a one-liner in the consumer's app entry:\n// import Hls from 'hls.js'; window.Hls = Hls;\n// Resolving it at runtime rather than importing it keeps the dependency\n// genuinely optional: apps that never render an HLS video ship nothing extra,\n// and apps that don't install it at all still build.\ntype HlsWindow = { Hls?: HlsConstructor };\n\nconst HLS_MANIFEST = /\\.m3u8(?:[?#]|$)/i;\nconst DASH_MANIFEST = /\\.mpd(?:[?#]|$)/i;\n\n// `<mux-player>` typed as a component so the dashed attributes type-check. The\n// element is only rendered once the consumer's bundle has registered it.\nconst MuxPlayer = 'mux-player' as unknown as ComponentType<Record<string, unknown>>;\n\n/**\n * Renders a Better Blocks `video` node.\n *\n * Direct file URLs (`provider: \"local\"` / `\"custom\"`) use a native `<video>`.\n * HLS/DASH manifests — most Mux assets — only play natively in Safari, so this\n * component opportunistically upgrades them without taking on a dependency:\n *\n * - `provider: \"mux\"` with a `playbackId` renders `<mux-player>` when the\n * consumer has registered that custom element (`@mux/mux-player`).\n * - `.m3u8` sources attach `hls.js` when the consumer exposes it as\n * `window.Hls` and the browser supports it (everywhere but iOS WebKit).\n * - Otherwise playback falls back to the native element, which works in Safari\n * and shows the `poster` elsewhere.\n *\n * Pass your own `video` block renderer for full control over the player.\n */\nexport function Video({\n provider,\n url,\n playbackId,\n poster,\n title,\n caption,\n transcript,\n player,\n alignment = 'center',\n aspectRatio,\n customAspectRatio,\n instanceId,\n}: {\n provider?: VideoProvider;\n url: string;\n assetId?: string;\n playbackId?: string;\n file?: VideoFile;\n poster?: string;\n title?: string;\n caption?: string;\n transcript?: string;\n player?: VideoPlayer;\n alignment?: MediaAlignment;\n aspectRatio?: AspectRatio;\n customAspectRatio?: string;\n /** Disambiguates the generated caption id when several videos share a page. */\n instanceId?: string | number;\n}): ReactNode {\n const videoRef = useRef<HTMLVideoElement>(null);\n\n const wantsMux = provider === 'mux' && Boolean(playbackId);\n const [muxDefined, setMuxDefined] = useState(false);\n\n // `<mux-player>` is usually registered by a side-effect import that resolves\n // after first paint, so watch for the definition instead of checking once.\n useEffect(() => {\n if (!wantsMux || typeof window === 'undefined' || !window.customElements) return;\n if (window.customElements.get('mux-player')) {\n setMuxDefined(true);\n return;\n }\n\n let cancelled = false;\n void window.customElements.whenDefined('mux-player').then(() => {\n if (!cancelled) setMuxDefined(true);\n });\n\n return () => {\n cancelled = true;\n };\n }, [wantsMux]);\n\n const isHls = HLS_MANIFEST.test(url);\n const isStreaming = isHls || DASH_MANIFEST.test(url);\n const useMux = wantsMux && muxDefined;\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video || !isHls) return;\n\n // Prefer hls.js wherever it runs, and only fall through to native playback\n // when it can't (iOS WebKit, which has no MSE but does play the manifest\n // straight from `src`). Sniffing `canPlayType` first looks tempting and is\n // wrong: Chrome answers \"maybe\" for `application/vnd.apple.mpegurl` and\n // then fails to play anything.\n const Hls = (window as unknown as HlsWindow).Hls;\n if (!Hls || Hls.isSupported?.() === false) return;\n\n let instance: HlsInstance | undefined;\n try {\n instance = new Hls();\n instance.loadSource(url);\n instance.attachMedia(video);\n } catch {\n // A broken/incompatible build shouldn't take the page down — the poster\n // stays visible, same as when hls.js isn't present at all.\n instance?.destroy();\n instance = undefined;\n }\n\n return () => instance?.destroy();\n }, [isHls, url, useMux]);\n\n const capId = caption ? `bb-video-cap-${instanceId ?? playbackId ?? 0}` : undefined;\n const ratio = getAspectRatio(aspectRatio, customAspectRatio);\n const frameStyle = getMediaFrameStyle(alignment, ratio);\n const fillStyle: CSSProperties = { width: '100%', height: '100%', display: 'block' };\n\n return (\n <figure\n className={`bb-video align-${alignment}`}\n style={getMediaFigureStyle(alignment)}\n aria-label={title ? `Video: ${title}` : undefined}\n >\n {title && (\n <figcaption className=\"bb-video-title\" style={{ fontWeight: 600 }}>\n {title}\n </figcaption>\n )}\n <div className=\"bb-video-frame\" style={frameStyle}>\n {useMux ? (\n <MuxPlayer\n className=\"bb-video-player\"\n playback-id={playbackId}\n poster={poster}\n metadata-video-title={title}\n autoplay={player?.autoplay ? true : undefined}\n loop={player?.loop ? true : undefined}\n muted={player?.muted ? true : undefined}\n aria-describedby={capId}\n style={fillStyle}\n />\n ) : (\n <video\n ref={videoRef}\n className=\"bb-video-player\"\n src={url}\n poster={poster}\n controls={player?.controls ?? true}\n autoPlay={player?.autoplay ?? false}\n loop={player?.loop ?? false}\n muted={player?.muted ?? false}\n playsInline\n // Streaming manifests are handed to hls.js / the provider player,\n // so don't let the browser prefetch a source it can't parse.\n preload={isStreaming ? 'none' : 'metadata'}\n aria-label={title || 'Video player'}\n aria-describedby={capId}\n style={fillStyle}\n >\n {transcript && <track kind=\"captions\" src={transcript} label=\"Captions\" />}\n Your browser does not support the video element. <a href={url}>Download the video</a>.\n </video>\n )}\n </div>\n {caption && (\n <figcaption id={capId} className=\"bb-video-caption\" style={MEDIA_CAPTION_STYLE}>\n {caption}\n </figcaption>\n )}\n </figure>\n );\n}\n"],"mappings":"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","names":["useEffect","useState","jsx","svg","useEffect","useRef","jsx","jsxs","_a","useEffect","useRef","useState","jsx","jsxs","useRef","useState","useEffect","_a","Fragment","jsx","jsxs"]}
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "@qkix/better-blocks-react-renderer",
3
+ "version": "0.18.0",
4
+ "description": "React renderer for Strapi v5 Blocks content with full Better Blocks plugin support — colors, tables, to-do lists, media embeds, alignment, and more",
5
+ "type": "commonjs",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.mts",
13
+ "default": "./dist/index.mjs"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "dependencies": {
25
+ "katex": "^0.16.11",
26
+ "mermaid": "^11.4.0",
27
+ "shiki": "^1.24.0",
28
+ "@qkix/better-blocks-core": "^0.2.0"
29
+ },
30
+ "peerDependencies": {
31
+ "react": ">=17.0.0",
32
+ "react-dom": ">=17.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@testing-library/dom": "^10.4.1",
36
+ "@testing-library/jest-dom": "^6.6.3",
37
+ "@testing-library/react": "^16.1.0",
38
+ "@types/katex": "^0.16.7",
39
+ "@types/react": "^19.0.0",
40
+ "@types/react-dom": "^19.0.0",
41
+ "eslint": "^10.8.1",
42
+ "jsdom": "^25.0.1",
43
+ "prettier": "^3.4.2",
44
+ "react": "^19.0.0",
45
+ "react-dom": "^19.0.0",
46
+ "tsup": "^8.3.5",
47
+ "typescript": "^5.7.2",
48
+ "vitest": "^2.1.8",
49
+ "@qkix/eslint-config": "0.0.0",
50
+ "@qkix/tsconfig": "0.0.0"
51
+ },
52
+ "keywords": [
53
+ "strapi",
54
+ "strapi-v5",
55
+ "blocks",
56
+ "blocks-renderer",
57
+ "react",
58
+ "renderer",
59
+ "rich-text",
60
+ "better-blocks",
61
+ "color",
62
+ "highlight",
63
+ "text-color",
64
+ "background-color",
65
+ "katex",
66
+ "latex",
67
+ "math",
68
+ "mermaid",
69
+ "diagram",
70
+ "cms",
71
+ "headless-cms"
72
+ ],
73
+ "license": "MIT",
74
+ "author": "qkix",
75
+ "repository": {
76
+ "type": "git",
77
+ "url": "git+https://github.com/qkix/strapi-plugin-better-blocks.git",
78
+ "directory": "packages/better-blocks-react-renderer"
79
+ },
80
+ "bugs": {
81
+ "url": "https://github.com/qkix/strapi-plugin-better-blocks/issues"
82
+ },
83
+ "homepage": "https://github.com/qkix/strapi-plugin-better-blocks#readme",
84
+ "engines": {
85
+ "node": ">=20.0.0 <=24.x.x",
86
+ "npm": ">=6.0.0"
87
+ },
88
+ "nx": {
89
+ "tags": [
90
+ "scope:renderer"
91
+ ]
92
+ },
93
+ "publishConfig": {
94
+ "access": "public"
95
+ },
96
+ "scripts": {
97
+ "build": "tsup",
98
+ "lint": "eslint . && prettier --check .",
99
+ "format": "prettier --write .",
100
+ "test": "vitest run",
101
+ "test:watch": "vitest",
102
+ "test:ts": "tsc --noEmit",
103
+ "typecheck": "tsc --noEmit"
104
+ }
105
+ }