feat(driver-versions): add foil effects, Mesa/GNOME cards, CodeBlock refactor#694
Conversation
- custom.css: add global padding-right (3.5rem) on .theme-code-block pre so code text never slides under the absolutely-positioned copy button; add :focus-visible reveal for keyboard accessibility - DriverVersionsCatalog.module.css: remove always-visible opacity:1 override (was inconsistent with every other widget); change commandBlock from inline-block/width:auto to block/width:100% - FeedItems.module.css: add .copyButton:focus-visible rule so keyboard users can discover and use the copy button - ImagesCatalog: add CopyableCode component — hover-reveal copy button with same visual language as FeedItems (border, icon ⎘/✓, 1.5s feedback); replace all bare <code> command blocks in StreamList and security tabs with it; add copyableCode + copyableCodeBtn CSS with proper padding-right so text never overlaps the button Assisted-by: Claude Sonnet 4.6 via GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The primary bootc switch commands rendered inside the Tabs component were using bare <code> elements with no copy affordance. Only the secondary StreamList (testing streams) had CopyableCode. Replace both entry.command and entry.nvidiaCommand bare <code> tags in the tab render path with CopyableCode so all bootc switch lines have the hover-reveal copy button. Assisted-by: Claude Sonnet 4.6 via OpenCode
Swap the hand-rolled CopyableCode component (⎘/✓ hover-reveal button) for the standard Docusaurus <CodeBlock language="bash"> widget used by DriverVersionsCatalog, giving all bootc switch commands and security commands the same syntax-highlighted, built-in copy button appearance site-wide. Remove the now-dead .copyableCode/.copyableCodeBtn CSS rules. Assisted-by: Claude Sonnet 4.6 via OpenCode
Apply the same gold and silver foil treatment from GitHubProfileCard to the driver version bump indicators: - Major bump cards: gold border (#e6c200), gold gradient background, gold holographic ::after overlay (mix-blend-mode: color-dodge), gold pill tag with warm-dark text - Minor bump cards: silver border (#b8c6d8), silver gradient background, silver holographic ::after overlay, silver pill tag with cool-dark text - Add z-index: 2 to .majorVersionLabel, .majorVersionValue so text renders above the foil overlay layer Assisted-by: Claude Sonnet 4.6 via OpenCode
- nvidiaMajorBump: green foil (border #5a9900, gradient bg, green holographic ::after shimmer) matching the gold/silver pattern - nvidiaMinorBump: lighter green foil treatment - Add .nvidiaBumpTag (solid #76b900 bg, dark green text #1a3600) and .nvidiaMinorTag (light green #c8e090, dark text) — use these on NVIDIA cards so pill color matches card color rather than gold - Fix .bumpTag and .minorTag: swap semi-transparent gradient backgrounds for solid opaque colors (#ffd700 / #c8d8e8) so text is legible in both light and dark mode Assisted-by: Claude Sonnet 4.6 via OpenCode
- Add gnome field to VersionSet interface and buildRowFromApiRelease - Add Mesa and GNOME version cards to driver versions timeline - Reorder cards: Kernel → HWE Kernel → NVIDIA → Mesa → GNOME - Fix HWE Kernel label (was 'HWE', now 'HWE Kernel') - Add Mesa foil (warm orange) and GNOME foil (GNOME blue) with major/minor bump detection, matching existing kernel/NVIDIA patterns - Add mesaCard, gnomeCard base styles with brand-tinted backgrounds - Add mesaBumpTag, mesaMinorTag, gnomeBumpTag, gnomeMinorTag pill styles Assisted-by: Claude Sonnet 4.6 via OpenCode
- Remove !emphasize guard from all minor bump tags and foil classes so minor bumps render on the latest/hero card (e.g. NVIDIA 595.58) - Hide HWE Kernel card entirely when value is null (Stable stream) Assisted-by: Claude Sonnet 4.6 via OpenCode
Add a gitignore exception and empty seed file so the Docusaurus Webpack bundler can resolve the @site/static/data/sbom-attestations.json import even when the GHA data cache is cold (first build on a new branch or after a cache eviction). The real data is populated by the update-sbom-cache workflow; this seed acts as a fallback so builds don't fail with 'Cannot find module' when the cache hasn't been warmed yet. Assisted-by: Claude Sonnet 4.6 via GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces visual and functional enhancements to the Driver Versions Catalog, adding version tracking and bump indicators for GNOME and Mesa. It refactors the Images Catalog to use Docusaurus CodeBlock components for command snippets and improves accessibility by ensuring copy buttons are visible on keyboard focus. Additionally, the SBOM attestation logic was simplified and a placeholder data file was added. A review comment identified significant indentation inconsistencies in the ImagesCatalog.tsx file that should be addressed to maintain code quality.
| const tone = | ||
| product.artwork === "dakotaraptor" | ||
| ? styles.cardDakota | ||
| : product.artwork === "achillobator" | ||
| ? styles.cardLts | ||
| : styles.cardBluefin; | ||
| const digestShort = product.metadata?.digestShort || "Unavailable"; | ||
| const digestFull = product.metadata?.digest || null; | ||
| const digestLink = product.metadata?.digestLink; | ||
| const ostreeShort = product.metadata?.labels?.ostreeCommit?.slice(0, 12); | ||
| const releaseUrl = assetsLink(product.versions?.release?.url); | ||
| const lastValidated = formatDate(catalog.generatedAt || null); | ||
| const lastPublished = formatDate(product.lastPublishedAt || null); | ||
| const hasNvidiaVariant = | ||
| product.streams.some((entry) => Boolean(entry.nvidiaCommand)) || | ||
| product.testingStreams.some((entry) => Boolean(entry.nvidiaCommand)); | ||
| const nvidiaEnabled = Boolean(nvidiaModeByProduct[product.id]); | ||
|
|
||
| return ( | ||
| <article key={product.id} className={`${styles.card} ${tone}`}> | ||
| <header className={styles.cardHeader}> | ||
| <Heading as="h2" className={styles.cardTitle}> | ||
| {product.name} | ||
| </Heading> | ||
| <span className={styles.registryBadge}>{product.org}</span> | ||
| </header> | ||
| return ( | ||
| <article key={product.id} className={`${styles.card} ${tone}`}> | ||
| <header className={styles.cardHeader}> | ||
| <Heading as="h2" className={styles.cardTitle}> | ||
| {product.name} | ||
| </Heading> | ||
| <span className={styles.registryBadge}>{product.org}</span> | ||
| </header> | ||
|
|
||
| <section className={styles.linkRow}> | ||
| <Link | ||
| to={product.packagePageUrl} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| Package Page | ||
| </Link> | ||
| {product.isoSectionLink && ( | ||
| <> | ||
| <span>·</span> | ||
| <Link to={product.isoSectionLink}>Download ISO</Link> | ||
| </> | ||
| )} | ||
| {releaseUrl && ( | ||
| <> | ||
| <span>·</span> | ||
| <Link | ||
| to={releaseUrl} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| Release Assets | ||
| </Link> | ||
| </> | ||
| )} | ||
| {digestLink ? ( | ||
| <> | ||
| <span>·</span> | ||
| <Link | ||
| to={digestLink} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| title={digestFull || digestShort} | ||
| > | ||
| Digest {digestShort} | ||
| </Link> | ||
| </> | ||
| ) : ( | ||
| <> | ||
| <span>·</span> | ||
| <span>Digest {digestShort}</span> | ||
| </> | ||
| )} | ||
| {ostreeShort && ( | ||
| <> | ||
| <span>·</span> | ||
| <span>OSTree {ostreeShort}</span> | ||
| </> | ||
| )} | ||
| </section> | ||
| <section className={styles.linkRow}> | ||
| <Link to={product.packagePageUrl} target="_blank" rel="noopener noreferrer"> | ||
| Package Page | ||
| </Link> | ||
| {product.isoSectionLink && ( | ||
| <> | ||
| <span>·</span> | ||
| <Link to={product.isoSectionLink}>Download ISO</Link> | ||
| </> | ||
| )} | ||
| {releaseUrl && ( | ||
| <> | ||
| <span>·</span> | ||
| <Link to={releaseUrl} target="_blank" rel="noopener noreferrer"> | ||
| Release Assets | ||
| </Link> | ||
| </> | ||
| )} | ||
| {digestLink ? ( | ||
| <> | ||
| <span>·</span> | ||
| <Link | ||
| to={digestLink} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| title={digestFull || digestShort} | ||
| > | ||
| Digest {digestShort} | ||
| </Link> | ||
| </> | ||
| ) : ( | ||
| <> | ||
| <span>·</span> | ||
| <span>Digest {digestShort}</span> | ||
| </> | ||
| )} | ||
| {ostreeShort && ( | ||
| <> | ||
| <span>·</span> | ||
| <span>OSTree {ostreeShort}</span> | ||
| </> | ||
| )} | ||
| </section> | ||
|
|
||
| <p className={styles.summary}>{product.summary}</p> | ||
| <p className={styles.summary}>{product.summary}</p> | ||
|
|
||
| <div className={styles.statsRow}> | ||
| <span className={styles.statChip}> | ||
| <strong>Pulls:</strong> {product.downloads.display} | ||
| </span> | ||
| <span className={sourceClass(product.downloads.source)}> | ||
| {sourceText(product.downloads.source, "Downloads")} | ||
| </span> | ||
| <span className={sourceClass(product.metadataSource)}> | ||
| {sourceText(product.metadataSource, "Metadata")} | ||
| </span> | ||
| </div> | ||
| <div className={styles.statsRow}> | ||
| <span className={styles.statChip}> | ||
| <strong>Pulls:</strong> {product.downloads.display} | ||
| </span> | ||
| <span className={sourceClass(product.downloads.source)}> | ||
| {sourceText(product.downloads.source, "Downloads")} | ||
| </span> | ||
| <span className={sourceClass(product.metadataSource)}> | ||
| {sourceText(product.metadataSource, "Metadata")} | ||
| </span> | ||
| </div> | ||
|
|
||
| <p className={styles.validationMeta}> | ||
| Last validated: <strong>{lastValidated}</strong> · Last published:{" "} | ||
| <strong>{lastPublished}</strong> | ||
| </p> | ||
| <p className={styles.validationMeta}> | ||
| Last validated: <strong>{lastValidated}</strong> · Last published: <strong>{lastPublished}</strong> | ||
| </p> | ||
|
|
||
| <section | ||
| className={`${styles.section} ${styles.focusSection} ${styles.streamsSection}`} | ||
| > | ||
| <div className={styles.sectionHeader}> | ||
| <Heading as="h3" className={styles.sectionTitle}> | ||
| Streams | ||
| </Heading> | ||
| {hasNvidiaVariant && ( | ||
| <div className={styles.nvidiaControl}> | ||
| <p className={styles.nvidiaToggleLabel}>Graphics Drivers</p> | ||
| <p className={styles.nvidiaToggleQuestion}> | ||
| Add Nvidia driver? | ||
| </p> | ||
| <div | ||
| className={styles.nvidiaToggleGroup} | ||
| role="group" | ||
| aria-label="Nvidia driver toggle" | ||
| <section className={`${styles.section} ${styles.focusSection} ${styles.streamsSection}`}> | ||
| <div className={styles.sectionHeader}> | ||
| <Heading as="h3" className={styles.sectionTitle}> | ||
| Streams | ||
| </Heading> | ||
| {hasNvidiaVariant && ( | ||
| <div className={styles.nvidiaControl}> | ||
| <p className={styles.nvidiaToggleLabel}>Graphics Drivers</p> | ||
| <p className={styles.nvidiaToggleQuestion}>Add Nvidia driver?</p> | ||
| <div className={styles.nvidiaToggleGroup} role="group" aria-label="Nvidia driver toggle"> | ||
| <button | ||
| type="button" | ||
| className={`button button--sm ${!nvidiaEnabled ? "button--primary" : "button--secondary"}`} | ||
| aria-pressed={!nvidiaEnabled} | ||
| onClick={() => | ||
| setNvidiaModeByProduct((current) => ({ | ||
| ...current, | ||
| [product.id]: false, | ||
| })) | ||
| } | ||
| > | ||
| No | ||
| </button> | ||
| <button | ||
| type="button" | ||
| className={`button button--sm ${nvidiaEnabled ? "button--primary" : "button--secondary"}`} | ||
| aria-pressed={nvidiaEnabled} | ||
| onClick={() => | ||
| setNvidiaModeByProduct((current) => ({ | ||
| ...current, | ||
| [product.id]: true, | ||
| })) | ||
| } | ||
| > | ||
| <button | ||
| type="button" | ||
| className={`button button--sm ${!nvidiaEnabled ? "button--primary" : "button--secondary"}`} | ||
| aria-pressed={!nvidiaEnabled} | ||
| onClick={() => | ||
| setNvidiaModeByProduct((current) => ({ | ||
| ...current, | ||
| [product.id]: false, | ||
| })) | ||
| } | ||
| > | ||
| No | ||
| </button> | ||
| <button | ||
| type="button" | ||
| className={`button button--sm ${nvidiaEnabled ? "button--primary" : "button--secondary"}`} | ||
| aria-pressed={nvidiaEnabled} | ||
| onClick={() => | ||
| setNvidiaModeByProduct((current) => ({ | ||
| ...current, | ||
| [product.id]: true, | ||
| })) | ||
| } | ||
| > | ||
| Yes | ||
| </button> | ||
| </div> | ||
| Yes | ||
| </button> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </div> | ||
| )} | ||
| </div> | ||
|
|
||
| {product.streams.length > 0 ? ( | ||
| <Tabs | ||
| groupId={`streams-${product.id}`} | ||
| values={product.streams.map((entry) => ({ | ||
| label: entry.label, | ||
| value: tabValue(entry.tag), | ||
| }))} | ||
| > | ||
| {product.streams.map((entry) => ( | ||
| <TabItem key={entry.tag} value={tabValue(entry.tag)}> | ||
| <p className={styles.tabCopy}> | ||
| Use this command to switch to the{" "} | ||
| <strong>{entry.label.toLowerCase()}</strong> channel for | ||
| this image. It is the quickest way to stay on that | ||
| release stream. | ||
| </p> | ||
| {nvidiaEnabled ? ( | ||
| entry.nvidiaCommand ? ( | ||
| <> | ||
| <div className={styles.streamVersionPills}> | ||
| <span className={styles.versionPill}> | ||
| <strong>GNOME</strong>{" "} | ||
| {entry.versions?.gnome || "Unknown"} | ||
| </span> | ||
| <span className={styles.versionPill}> | ||
| <strong>Linux</strong>{" "} | ||
| {entry.versions?.kernel || "Unknown"} | ||
| </span> | ||
| <span className={styles.versionPill}> | ||
| <strong>NVIDIA</strong>{" "} | ||
| {entry.versions?.nvidia || "Unknown"} | ||
| </span> | ||
| </div> | ||
| <code>{entry.nvidiaCommand}</code> | ||
| </> | ||
| ) : ( | ||
| <p className={styles.emptyText}> | ||
| No Nvidia variant published for this stream tag. | ||
| </p> | ||
| ) | ||
| ) : ( | ||
| {product.streams.length > 0 ? ( | ||
| <Tabs | ||
| groupId={`streams-${product.id}`} | ||
| values={product.streams.map((entry) => ({ | ||
| label: entry.label, | ||
| value: tabValue(entry.tag), | ||
| }))} | ||
| > | ||
| {product.streams.map((entry) => ( | ||
| <TabItem key={entry.tag} value={tabValue(entry.tag)}> | ||
| <p className={styles.tabCopy}> | ||
| Use this command to switch to the <strong>{entry.label.toLowerCase()}</strong> channel for this image. | ||
| It is the quickest way to stay on that release stream. | ||
| </p> | ||
| {nvidiaEnabled ? ( | ||
| entry.nvidiaCommand ? ( | ||
| <> | ||
| <div className={styles.streamVersionPills}> | ||
| <span className={styles.versionPill}> | ||
| <strong>GNOME</strong>{" "} | ||
| {entry.versions?.gnome || "Unknown"} | ||
| <strong>GNOME</strong> {entry.versions?.gnome || "Unknown"} | ||
| </span> | ||
| <span className={styles.versionPill}> | ||
| <strong>Linux</strong> {entry.versions?.kernel || "Unknown"} | ||
| </span> | ||
| <span className={styles.versionPill}> | ||
| <strong>Linux</strong>{" "} | ||
| {entry.versions?.kernel || "Unknown"} | ||
| <strong>NVIDIA</strong> {entry.versions?.nvidia || "Unknown"} | ||
| </span> | ||
| {entry.versions?.nvidia && ( | ||
| <span className={styles.versionPill}> | ||
| <strong>NVIDIA</strong> {entry.versions.nvidia} | ||
| </span> | ||
| )} | ||
| </div> | ||
| <code>{entry.command}</code> | ||
| <CodeBlock language="bash">{entry.nvidiaCommand}</CodeBlock> | ||
| </> | ||
| )} | ||
| </TabItem> | ||
| ))} | ||
| </Tabs> | ||
| ) : ( | ||
| <p className={styles.emptyText}>No active tags.</p> | ||
| )} | ||
| ) : ( | ||
| <p className={styles.emptyText}>No Nvidia variant published for this stream tag.</p> | ||
| ) | ||
| ) : ( | ||
| <> | ||
| <div className={styles.streamVersionPills}> | ||
| <span className={styles.versionPill}> | ||
| <strong>GNOME</strong> {entry.versions?.gnome || "Unknown"} | ||
| </span> | ||
| <span className={styles.versionPill}> | ||
| <strong>Linux</strong> {entry.versions?.kernel || "Unknown"} | ||
| </span> | ||
| {entry.versions?.nvidia && ( | ||
| <span className={styles.versionPill}> | ||
| <strong>NVIDIA</strong> {entry.versions.nvidia} | ||
| </span> | ||
| )} | ||
| </div> | ||
| <CodeBlock language="bash">{entry.command}</CodeBlock> | ||
| </> | ||
| )} | ||
| </TabItem> | ||
| ))} | ||
| </Tabs> | ||
| ) : ( | ||
| <p className={styles.emptyText}>No active tags.</p> | ||
| )} | ||
|
|
||
| <details className={styles.testingDetails}> | ||
| <summary> | ||
| Testing Branches ({product.testingStreams.length}) | ||
| </summary> | ||
| <StreamList | ||
| streams={product.testingStreams} | ||
| preferNvidia={nvidiaEnabled} | ||
| /> | ||
| </details> | ||
| </section> | ||
| <details className={styles.testingDetails}> | ||
| <summary>Testing Branches ({product.testingStreams.length})</summary> | ||
| <StreamList streams={product.testingStreams} preferNvidia={nvidiaEnabled} /> | ||
| </details> | ||
| </section> | ||
|
|
||
| <section | ||
| className={`${styles.section} ${styles.focusSection} ${styles.securitySection}`} | ||
| <section className={`${styles.section} ${styles.focusSection} ${styles.securitySection}`}> | ||
| <Heading as="h3" className={styles.sectionTitle}> | ||
| Signing and SBOM | ||
| </Heading> | ||
| {product.security?.cosignKeyUrl ? ( | ||
| <p className={styles.securityText}> | ||
| Key: <code>{product.security.cosignKeyUrl}</code> | ||
| </p> | ||
| ) : ( | ||
| <p className={styles.securityText}>No published cosign key URL in this catalog.</p> | ||
| )} | ||
|
|
||
| <Tabs | ||
| groupId={`security-${product.id}`} | ||
| values={[ | ||
| { label: "Verify Signature", value: "verify-signature" }, | ||
| { label: "Verify Provenance", value: "verify-provenance" }, | ||
| { label: "Generate SBOM", value: "generate-sbom" }, | ||
| ]} | ||
| > | ||
| <Heading as="h3" className={styles.sectionTitle}> | ||
| Signing and SBOM | ||
| </Heading> | ||
| {product.security?.cosignKeyUrl ? ( | ||
| <p className={styles.securityText}> | ||
| Key: <code>{product.security.cosignKeyUrl}</code> | ||
| <TabItem value="verify-signature"> | ||
| <p className={styles.tabCopy}> | ||
| Signature verification confirms this image was signed by the expected maintainers and helps detect tampering before deployment. | ||
| {" "} | ||
| <Link to="https://docs.sigstore.dev/cosign/verifying/verify/" target="_blank" rel="noopener noreferrer"> | ||
| Learn more | ||
| </Link> | ||
| . | ||
| </p> | ||
| ) : ( | ||
| <p className={styles.securityText}> | ||
| No published cosign key URL in this catalog. | ||
| {product.security?.verifyCommand && <CodeBlock language="bash">{product.security.verifyCommand}</CodeBlock>} | ||
| </TabItem> | ||
| <TabItem value="verify-provenance"> | ||
| <p className={styles.tabCopy}> | ||
| Provenance attestation lets you validate how the image was built in CI so you can make trust decisions from evidence. | ||
| {" "} | ||
| <Link to="https://slsa.dev/" target="_blank" rel="noopener noreferrer"> | ||
| Learn more | ||
| </Link> | ||
| . | ||
| </p> | ||
| )} | ||
|
|
||
| <Tabs | ||
| groupId={`security-${product.id}`} | ||
| values={[ | ||
| { label: "Verify Signature", value: "verify-signature" }, | ||
| { label: "Verify Provenance", value: "verify-provenance" }, | ||
| { label: "Generate SBOM", value: "generate-sbom" }, | ||
| ]} | ||
| > | ||
| <TabItem value="verify-signature"> | ||
| {product.security?.attestCommand && <CodeBlock language="bash">{product.security.attestCommand}</CodeBlock>} | ||
| {product.security?.attestCommand && product.security.hasAttestation === false && ( | ||
| <p className={styles.tabCopy}> | ||
| Signature verification confirms this image was signed by the | ||
| expected maintainers and helps detect tampering before | ||
| deployment.{" "} | ||
| <Link | ||
| to="https://docs.sigstore.dev/cosign/verifying/verify/" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| Learn more | ||
| </Link> | ||
| . | ||
| Note: attestations are not yet published for this image. The command is provided for when they are. | ||
| </p> | ||
| {product.security?.verifyCommand && ( | ||
| <code>{product.security.verifyCommand}</code> | ||
| )} | ||
| </TabItem> | ||
| <TabItem value="verify-provenance"> | ||
| <p className={styles.tabCopy}> | ||
| Provenance attestation lets you validate how the image was | ||
| built in CI so you can make trust decisions from evidence.{" "} | ||
| <Link | ||
| to="https://slsa.dev/" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| Learn more | ||
| </Link> | ||
| . | ||
| </p> | ||
| {latestAttestation && ( | ||
| <p className={styles.tabCopy}> | ||
| {latestAttestation.attestation.verified | ||
| ? `Latest release (${latestAttestation.tag}): attestation verified.` | ||
| : latestAttestation.attestation.present | ||
| ? `Latest release (${latestAttestation.tag}): attestation present but verification failed.` | ||
| : `Latest release (${latestAttestation.tag}): no attestation found.`} | ||
| </p> | ||
| )} | ||
| {product.security?.attestCommand && ( | ||
| <code>{product.security.attestCommand}</code> | ||
| )} | ||
| {product.security?.attestCommand && | ||
| product.security.hasAttestation === false && | ||
| !latestAttestation?.attestation.present && ( | ||
| <p className={styles.tabCopy}> | ||
| Note: attestations are not yet published for this image. | ||
| The command is provided for when they are. | ||
| </p> | ||
| )} | ||
| </TabItem> | ||
| <TabItem value="generate-sbom"> | ||
| <p className={styles.tabCopy}> | ||
| SBOM generation gives you a component inventory for audits, | ||
| policy checks, and vulnerability triage workflows.{" "} | ||
| <Link | ||
| to="https://github.com/anchore/syft" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| > | ||
| Learn more | ||
| </Link> | ||
| . | ||
| </p> | ||
| {product.security?.sbomCommand && ( | ||
| <code>{product.security.sbomCommand}</code> | ||
| )} | ||
| </TabItem> | ||
| </Tabs> | ||
| </section> | ||
| </article> | ||
| ); | ||
| )} | ||
| </TabItem> | ||
| <TabItem value="generate-sbom"> | ||
| <p className={styles.tabCopy}> | ||
| SBOM generation gives you a component inventory for audits, policy checks, and vulnerability triage workflows. | ||
| {" "} | ||
| <Link to="https://github.com/anchore/syft" target="_blank" rel="noopener noreferrer"> | ||
| Learn more | ||
| </Link> | ||
| . | ||
| </p> | ||
| {product.security?.sbomCommand && <CodeBlock language="bash">{product.security.sbomCommand}</CodeBlock>} | ||
| </TabItem> | ||
| </Tabs> | ||
| </section> | ||
| </article> | ||
| ); | ||
| }); |
There was a problem hiding this comment.
The entire renderCards map function has pervasive indentation issues. The block starting here is indented by 6 spaces instead of the 8 spaces required to be consistent with the 2-space indentation used throughout the rest of the project. This significantly impacts the readability and maintainability of the component.
The JSON type inferred by TypeScript from the seed file does not have a 'gnome' field in the versions object, causing the direct 'as DriverCatalog' cast to fail with TS2352. Use 'as unknown as DriverCatalog' to bridge the type gap — the runtime shape is validated by the component logic. Assisted-by: Claude Sonnet 4.6 via GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
CopyableCodewidgets with Docusaurus built-inCodeBlockfor consistencysbom-attestations.jsonseed file to unblock cold-cache CI buildsTesting
CI passed on fork PR castrojo/documentation 27.
Assisted-by: Claude Sonnet 4.6 via GitHub Copilot
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com