Skip to content

feat(docs): embed StackBlitz examples per package#300

Open
stephansama wants to merge 3 commits into
mainfrom
stephansama/ste-189-embed-stackblitz-examples-into-documentation-website
Open

feat(docs): embed StackBlitz examples per package#300
stephansama wants to merge 3 commits into
mainfrom
stephansama/ste-189-embed-stackblitz-examples-into-documentation-website

Conversation

@stephansama

Copy link
Copy Markdown
Owner

Closes STE-189

Adds a reusable <StackblitzEmbed> Astro component and per-package example MDX pages under www/docs/src/content/docs/examples/. Each page embeds the sandbox via the StackBlitz "open on GitHub" URL — no SDK dep, pure URL construction.

Files

  • www/docs/src/components/StackblitzEmbed.astro — props: repo, dir, branch?, openFile?, view?, height?, title?. Renders an iframe + an "Open in StackBlitz" link.
  • www/docs/src/content/docs/examples/:
    • astro-iconify-svgmap.mdx — astro example
    • remark-asciinema.mdx — astro example
    • svelte-social-share-links/{astro,svelte-kit}.mdx
    • typed-events/{react,vanilla}.mdx

Acceptance criteria

  • <StackblitzEmbed> ships at www/docs/src/components/StackblitzEmbed.astro
  • Each examples/ package has an MDX page that embeds its sandbox
  • Rendered output contains <iframe src="https://stackblitz.com/github/..." markup (URL-based, no SDK)
  • No new runtime dependency
  • Starlight sidebar grouping for "Examples" — Starlight auto-derives the route hierarchy from content/docs/examples/, so the sidebar entry surfaces automatically; explicit grouping in astro.config.ts can be added later if a custom label is needed

@vercel

vercel Bot commented May 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
packages Ready Ready Preview, Comment May 18, 2026 6:18am

@changeset-bot

changeset-bot Bot commented May 18, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5fa6482

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a new Examples section to the documentation featuring live, interactive code examples
    • Integrated StackBlitz embeds to run and test code directly in the browser
  • Chores

    • Automated example page generation and setup during documentation builds

Walkthrough

This PR adds StackBlitz embeds to documentation examples by expanding the example generator to produce framework, openFile, and title metadata plus MDX pages; introducing a StackblitzEmbed component that renders embeds from example slugs; and configuring the docs sidebar and build lifecycle to integrate the generated examples.

Changes

StackBlitz Example Embeds in Docs

Layer / File(s) Summary
Example generation pipeline with framework and metadata detection
scripts/src/generate-examples.ts
The generator now adds framework detection (from dependencies), openFile path resolution with package.json overrides, title generation from slug, and writes per-example MDX pages alongside an index. The Example interface expands to include slug, framework, openFile, and title.
StackblitzEmbed Astro component
www/docs/src/components/StackblitzEmbed.astro
New reusable component that accepts either a slug (resolved from generated examples) or explicit dir/repo/branch props, validates that inputs resolve, constructs StackBlitz embed and base URLs, and renders an iframe with lazy loading plus an "Open in StackBlitz" link.
Documentation sidebar, lifecycle hooks, and build artifacts
www/docs/astro.config.ts, www/docs/package.json, .gitignore
Astro sidebar adds a collapsed Examples group that auto-generates from the examples directory; package.json predev and prebuild hooks run setup:examples to generate examples before dev/build; generated examples docs are excluded from version control.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • stephansama/packages#257: Modifies scripts/src/generate-examples.ts output handling and directory management for generated examples.
  • stephansama/packages#285: Establishes the Starlight-based www/docs/astro.config.ts sidebar configuration that this PR extends with the Examples entry.

Poem

🐰 Embeds now spring to life with Blitz,
Each example framework neatly splits,
Slugs compute their paths with care,
MDX pages bloom everywhere,
Sidebar links to every share!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature being added—embedding StackBlitz examples per package in the documentation.
Description check ✅ Passed The PR description is comprehensive, includes the closing issue, lists all affected files, specifies acceptance criteria status, and explains the implementation approach clearly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stephansama/ste-189-embed-stackblitz-examples-into-documentation-website

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

www/docs/astro.config.ts

Oops! Something went wrong! :(

ESLint: 10.2.1

Error: Cannot find module '/node_modules/@stephansama/eslint-config/dist/index.mjs'
at createEsmNotFoundErr (node:internal/modules/cjs/loader:1513:15)
at finalizeEsmResolution (node:internal/modules/cjs/loader:1502:9)
at resolveExports (node:internal/modules/cjs/loader:685:14)
at Module._findPath (node:internal/modules/cjs/loader:752:31)
at Module._resolveFilename (node:internal/modules/cjs/loader:1461:27)
at wrapResolveFilename (node:internal/modules/cjs/loader:1049:27)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1094:12)
at require.resolve (node:internal/modules/helpers:171:31)
at jitiResolve (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:148703)
at jitiRequire (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:150290)
at import (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:158307)
at /eslint.config.ts:1:223
at eval

... [truncated 540 characters] ...

265:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:588:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:369:19)
at async Promise.all (index 0)
at async findFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint-helpers.js:637:25)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint.js:1004:21)
at async Object.execute (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/cli.js:386:14)
at async main (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/bin/eslint.js:175:19)

scripts/src/generate-examples.ts

Oops! Something went wrong! :(

ESLint: 10.2.1

Error: Cannot find module '/node_modules/@stephansama/eslint-config/dist/index.mjs'
at createEsmNotFoundErr (node:internal/modules/cjs/loader:1513:15)
at finalizeEsmResolution (node:internal/modules/cjs/loader:1502:9)
at resolveExports (node:internal/modules/cjs/loader:685:14)
at Module._findPath (node:internal/modules/cjs/loader:752:31)
at Module._resolveFilename (node:internal/modules/cjs/loader:1461:27)
at wrapResolveFilename (node:internal/modules/cjs/loader:1049:27)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1094:12)
at require.resolve (node:internal/modules/helpers:171:31)
at jitiResolve (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:148703)
at jitiRequire (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:150290)
at import (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:158307)
at /eslint.config.ts:1:223
at eval

... [truncated 540 characters] ...

265:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:588:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:369:19)
at async Promise.all (index 0)
at async findFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint-helpers.js:637:25)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint.js:1004:21)
at async Object.execute (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/cli.js:386:14)
at async main (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/bin/eslint.js:175:19)

www/docs/src/components/StackblitzEmbed.astro

Oops! Something went wrong! :(

ESLint: 10.2.1

Error: Cannot find module '/node_modules/@stephansama/eslint-config/dist/index.mjs'
at createEsmNotFoundErr (node:internal/modules/cjs/loader:1513:15)
at finalizeEsmResolution (node:internal/modules/cjs/loader:1502:9)
at resolveExports (node:internal/modules/cjs/loader:685:14)
at Module._findPath (node:internal/modules/cjs/loader:752:31)
at Module._resolveFilename (node:internal/modules/cjs/loader:1461:27)
at wrapResolveFilename (node:internal/modules/cjs/loader:1049:27)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1094:12)
at require.resolve (node:internal/modules/helpers:171:31)
at jitiResolve (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:148703)
at jitiRequire (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:150290)
at import (/node_modules/.pnpm/jiti@2.6.1/node_modules/jiti/dist/jiti.cjs:1:158307)
at /eslint.config.ts:1:223
at eval

... [truncated 540 characters] ...

265:11)
at async ConfigLoader.calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:588:23)
at async #calculateConfigArray (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/config/config-loader.js:369:19)
at async Promise.all (index 0)
at async findFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint-helpers.js:637:25)
at async ESLint.lintFiles (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/eslint/eslint.js:1004:21)
at async Object.execute (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/lib/cli.js:386:14)
at async main (/node_modules/.pnpm/eslint@10.2.1_jiti@2.6.1/node_modules/eslint/bin/eslint.js:175:19)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new StackblitzEmbed Astro component to facilitate embedding interactive code examples directly from GitHub into the documentation. It also adds several example pages for various packages, including astro-iconify-svgmap, remark-asciinema, and typed-events, utilizing this new component. Review feedback suggests optimizing the URL parameter logic for the StackBlitz view, updating the iframe's allow attribute to support clipboard access while removing non-standard directives, and implementing internationalization for UI text to support multiple locales.

Comment on lines +25 to +26
const params = new URLSearchParams({ embed: "1", view });
if (openFile) params.set("file", openFile);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The view parameter is currently included in the URL even when set to "default". StackBlitz expects either "editor" or "preview", and omitting the parameter defaults to the combined view. It's cleaner to only set the parameter if a specific view is requested.

const params = new URLSearchParams({ embed: "1" });
if (view !== "default") params.set("view", view);
if (openFile) params.set("file", openFile);

src={embedUrl}
title={frameTitle}
loading="lazy"
allow="cross-origin-isolated"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The allow attribute should include clipboard-write to enable users to copy code from the StackBlitz editor. Additionally, cross-origin-isolated is not a standard directive for the Permissions Policy (the allow attribute). Cross-origin isolation for iframes is typically managed via COOP/COEP headers or the credentialless attribute.

		allow="clipboard-write"

></iframe>
<p class="stackblitz-open">
<a href={openUrl} target="_blank" rel="noreferrer noopener">
Open in StackBlitz →

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text "Open in StackBlitz →" is hardcoded. For a documentation site, it's best to use the project's internationalization system (e.g., Starlight's built-in i18n) to ensure the UI can be translated across different locales.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/src/generate-examples.ts`:
- Around line 8-22: The file violates the project's perfectionist/sort-modules
ordering: move the exported interface Example so it appears before the local
interface StackblitzOverrides, and reorder the keys inside Example (and any
other object literal/interface declarations between lines 33-58) to match the
configured key ordering used by the project—e.g., ensure Example's properties
appear in the expected sequence (description, name, relativeDir, version, slug,
framework, openFile, title) and apply the same ordering to any other
interfaces/objects flagged by CI.
- Around line 91-101: humanizeTitle returns raw kebab-case for single-segment
slugs; update the function so the single-part path is normalized the same way as
multi-part frameworks: take parts[0] (head), replace hyphens with spaces and
apply the same title-casing regex used for `framework` (/(^|\s|-)([a-z])/g) to
capitalize letters, then return that transformed head instead of the raw
value—modify the `humanizeTitle` function (variables `parts`, `head`, `tail`,
`framework`) so both single- and multi-segment slugs are humanized consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 23f583f3-094b-47f0-bdec-cdcaae64a359

📥 Commits

Reviewing files that changed from the base of the PR and between 9a84b42 and 5fa6482.

📒 Files selected for processing (5)
  • .gitignore
  • scripts/src/generate-examples.ts
  • www/docs/astro.config.ts
  • www/docs/package.json
  • www/docs/src/components/StackblitzEmbed.astro

Comment on lines +8 to +22
interface StackblitzOverrides {
openFile?: string;
title?: string;
}

export interface Example {
description: string | undefined;
name: string;
relativeDir: string;
version: string;
slug: string;
framework: string;
openFile: string | undefined;
title: string;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix perfectionist/sort-modules ordering violations blocking CI.

This file still violates the active sort rules (already failing in CI). Please reorder declarations/object keys to match the configured order (e.g., export interface Example before local interfaces, and object keys in expected order).

Suggested ordering fix (example)
-interface StackblitzOverrides {
-	openFile?: string;
-	title?: string;
-}
-
 export interface Example {
 	description: string | undefined;
+	framework: string;
 	name: string;
+	openFile: string | undefined;
 	relativeDir: string;
-	version: string;
 	slug: string;
-	framework: string;
-	openFile: string | undefined;
 	title: string;
+	version: string;
+}
+
+interface StackblitzOverrides {
+	openFile?: string;
+	title?: string;
 }
 
 const FRAMEWORK_OPEN_FILES: Array<{
-	framework: string;
-	deps: string[];
 	candidates: string[];
+	deps: string[];
+	framework: string;
 }> = [
 	{
-		framework: "sveltekit",
-		deps: ["`@sveltejs/kit`"],
 		candidates: ["src/routes/+page.svelte"],
+		deps: ["`@sveltejs/kit`"],
+		framework: "sveltekit",
 	},

Also applies to: 33-58

🧰 Tools
🪛 GitHub Actions: 🦋 Changesets Release / 1_Examples _ Test.txt

[error] 13-13: ESLint (perfectionist/sort-modules): Expected "Example" (export-interface) to come before "StackblitzOverrides" (interface).

🪛 GitHub Actions: 🦋 Changesets Release / Examples _ Test

[error] 13-13: ESLint perfectionist/sort-modules: Expected "Example" (export-interface) to come before "StackblitzOverrides" (interface).

🪛 GitHub Check: Examples / Test

[failure] 19-19:
Expected "framework" to come before "slug"


[failure] 18-18:
Expected "slug" to come before "version"


[failure] 13-13:
Expected "Example" (export-interface) to come before "StackblitzOverrides" (interface)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/src/generate-examples.ts` around lines 8 - 22, The file violates the
project's perfectionist/sort-modules ordering: move the exported interface
Example so it appears before the local interface StackblitzOverrides, and
reorder the keys inside Example (and any other object literal/interface
declarations between lines 33-58) to match the configured key ordering used by
the project—e.g., ensure Example's properties appear in the expected sequence
(description, name, relativeDir, version, slug, framework, openFile, title) and
apply the same ordering to any other interfaces/objects flagged by CI.

Comment on lines +91 to +101
function humanizeTitle(slug: string): string {
const parts = slug.split("/");
if (parts.length === 1) return parts[0]!;
const [head, ...tail] = parts;
const framework = tail
.join(" ")
.replace(
/(^|\s|-)([a-z])/g,
(_, sep, letter) => `${sep}${letter.toUpperCase()}`,
);
return `${head} — ${framework}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Humanize single-segment example titles too.

humanizeTitle() returns raw kebab-case for one-part slugs, so cards/pages can show unformatted titles.

Suggested fix
 function humanizeTitle(slug: string): string {
+	const humanize = (value: string) =>
+		value
+			.replace(/-/g, " ")
+			.replace(/\b([a-z])/g, (m) => m.toUpperCase());
+
 	const parts = slug.split("/");
-	if (parts.length === 1) return parts[0]!;
+	if (parts.length === 1) return humanize(parts[0]!);
 	const [head, ...tail] = parts;
 	const framework = tail
 		.join(" ")
 		.replace(
 			/(^|\s|-)([a-z])/g,
 			(_, sep, letter) => `${sep}${letter.toUpperCase()}`,
 		);
-	return `${head} — ${framework}`;
+	return `${humanize(head)} — ${framework}`;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/src/generate-examples.ts` around lines 91 - 101, humanizeTitle
returns raw kebab-case for single-segment slugs; update the function so the
single-part path is normalized the same way as multi-part frameworks: take
parts[0] (head), replace hyphens with spaces and apply the same title-casing
regex used for `framework` (/(^|\s|-)([a-z])/g) to capitalize letters, then
return that transformed head instead of the raw value—modify the `humanizeTitle`
function (variables `parts`, `head`, `tail`, `framework`) so both single- and
multi-segment slugs are humanized consistently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant