Skip to content

feat(posts): embed per-episode Slidev decks#72

Merged
AnnatarHe merged 1 commit into
masterfrom
feat/episode-slides-embed
Jun 21, 2026
Merged

feat(posts): embed per-episode Slidev decks#72
AnnatarHe merged 1 commit into
masterfrom
feat/episode-slides-embed

Conversation

@AnnatarHe

Copy link
Copy Markdown
Member

What

Embed an interactive Slidev deck on an episode page when the episode opts in via a new hasSlides flag (or an explicit slidesUrl override). The deck URL is derived from the episode slug + a configurable SLIDES_BASE_URL, pointing at the companion AsyncTalk/video-slides project (hosted on GitHub Pages).

Changes

  • content.config.ts — optional hasSlides + slidesUrl
  • constants.tsSLIDES_BASE_URL
  • SlidesEmbed.astro — responsive aspect-video iframe + open-in-new-tab link (mirrors VideoEmbed.astro)
  • posts/[...slug].astro — conditional embed after the video block
  • ep68.mdx — opts in with hasSlides: true

Test plan

  • astro build → 75 pages built
  • /posts/ep68 embeds …/video-slides/ep68/; /posts/ep67 (no flag) has no iframe

Note

The embed is blank until the companion video-slides repo is deployed to GitHub Pages. Decks are authored/built there (see its PR). Remove hasSlides: true on ep68 if you want to hold the embed back until then.

🤖 Generated with Claude Code

Render an interactive Slidev deck on an episode page when the episode opts in via a new `hasSlides` flag (or an explicit `slidesUrl`). The deck URL is derived from the episode slug and a configurable `SLIDES_BASE_URL`, pointing at the separate video-slides project hosted on GitHub Pages.

- content.config.ts: add optional `hasSlides` and `slidesUrl`
- constants.ts: add `SLIDES_BASE_URL`
- SlidesEmbed.astro: responsive aspect-video iframe + open-in-new-tab link
- posts/[...slug].astro: conditional embed after the video block
- ep68.mdx: opt in with `hasSlides: true`

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@AnnatarHe AnnatarHe temporarily deployed to feat/episode-slides-embed - asynctalk-website PR #72 June 21, 2026 06:20 — with Render Destroyed
@claude

claude Bot commented Jun 21, 2026

Copy link
Copy Markdown

Code Review — feat(posts): embed per-episode Slidev decks

Overall: This is a clean, well-scoped feature. The component design mirrors VideoEmbed.astro, the opt-in approach via hasSlides is sensible, and lazy-loading the iframe is good for performance. A few things worth addressing before merge:


Security

Missing rel="noopener" on target="_blank" link (SlidesEmbed.astro, line 21)

<a
  href={src}
  target="_blank"
  referrerpolicy="no-referrer"   <!-- privacy handled, but opener is not -->
  ...
>

Without rel="noopener", the opened page can access window.opener and navigate the parent window. Modern cross-origin navigations default to noopener, but it's best to be explicit:

rel="noopener noreferrer"

Bug / Logic

slidesSrc is always computed, even for episodes without slides ([...slug].astro, lines 48–50)

const slug = post.id.replace(/^\/?posts\//, "");
const slidesSrc =
  frontData.slidesUrl ?? `${SLIDES_BASE_URL}/${slug}/`;

Two minor issues here:

  1. In Astro 5, post.id for a file at src/content/posts/ep68.mdx is already ep68 — the regex replace(/^\/?posts\//, "") is a no-op. No harm done, but it's confusing. If there's a reason to keep it (e.g., defensive guard for nested paths), a comment would help.

  2. slidesSrc is computed for every episode page regardless of hasSlides / slidesUrl. This is harmless in a static build, but computing it conditionally (or inside the JSX block) would make the intent clearer and avoid constructing dead URLs in page scope.


Minor observations

  • iframe has no sandbox attribute. For first-party GitHub Pages content this is a reasonable trade-off (Slidev needs JS to function), but worth noting explicitly in the PR description or a code comment so future maintainers don't add sandbox without knowing why it was omitted.

  • allowfullscreen="true" (string) vs allowfullscreen (boolean attribute): Both work in browsers and Astro handles the serialisation, but allowfullscreen={true} is more idiomatic in JSX/Astro.

  • title fallback is English ('Slides'): The rest of the UI is Chinese. Low priority, but '幻灯片' would be more consistent.


What's good

  • hasSlides opt-in keeps the embed off all existing episodes by default — safe rollout.
  • loading="lazy" prevents the iframe from blocking page load.
  • aspect-video class gives a responsive 16:9 layout matching the video block.
  • slidesUrl override is a nice escape hatch for episodes hosted elsewhere.
  • Content schema validation uses z.string().url() — correct, and stricter than the existing biliUrl field.

Summary: One security fix is needed (add rel="noopener noreferrer"). The slug-regex no-op is worth a quick comment or cleanup. Everything else is style/polish. Good PR overall.

@AnnatarHe AnnatarHe merged commit 6a7c73a into master Jun 21, 2026
1 check passed
@AnnatarHe AnnatarHe deleted the feat/episode-slides-embed branch June 21, 2026 06:32
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