Skip to content

[#562] Markdown rendering for story content + Write/Preview toggle#571

Merged
realproject7 merged 3 commits intomainfrom
task/562-markdown-support
Mar 26, 2026
Merged

[#562] Markdown rendering for story content + Write/Preview toggle#571
realproject7 merged 3 commits intomainfrom
task/562-markdown-support

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • react-markdown + rehype-sanitize added as dependencies
  • StoryContent component renders Markdown with fiction-focused sanitization (bold, italic, h1-h3, blockquotes, scene breaks, lists, inline code — no images/tables/raw HTML)
  • Prose styling in globals.css matches PlotLink's moleskine aesthetic (Lora font, accent-bordered blockquotes, "* * *" scene breaks)
  • Story page + plot detail page now render content as Markdown instead of plain text
  • Write/Preview toggle on both Create Storyline and Chain Plot forms (GitHub-style tabs, content preserved between tabs)
  • Preview matches story page rendering exactly (same StoryContent component)
  • Existing plain text stories render correctly — plain text is valid Markdown
  • XSS prevention — raw HTML, scripts, iframes stripped via rehype-sanitize

Fixes #562

Test plan

  • View existing plain text story — renders correctly (no visual regression)
  • Create story with Markdown (bold, italic, headings, blockquotes, --- scene breaks)
  • Preview tab shows rendered Markdown matching story page style
  • Switch between Write/Preview without losing content
  • Verify raw HTML in content is stripped (try <script>alert(1)</script>)
  • Build passes (npm run build)

🤖 Generated with Claude Code

- Add react-markdown + rehype-sanitize dependencies
- Create StoryContent component with fiction-focused sanitization
  (bold, italic, headings, blockquotes, scene breaks, lists, inline code)
- Strip raw HTML/scripts/iframes/images/tables for XSS prevention
- Style Markdown to match PlotLink's moleskine prose aesthetic
  (Lora font, scene breaks as "* * *", blockquote with accent border)
- Replace plain text rendering on story page and plot detail page
- Add Write/Preview toggle on Create Storyline and Chain Plot forms
- Plain text stories still render correctly (valid Markdown)

Fixes #562

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 26, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
plotlink Ignored Ignored Mar 26, 2026 4:22pm

Request Review

Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

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

T2b approves. rehype-sanitize with restricted tagNames and empty attributes provides solid XSS prevention. StoryContent component cleanly shared between display and preview. Write/Preview toggle is clean with proper type="button" to prevent form submission. Prose CSS matches the moleskine aesthetic. Minor: WritePreviewToggle accepts a content prop that is unused — consider removing it to avoid confusion (non-blocking).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

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

Verdict: REQUEST CHANGES

Summary

The renderer and preview toggle are close, but the current Markdown setup still misses one required syntax and regresses existing plain-text formatting.

Findings

  • [high] StoryContent switched story pages away from whitespace-pre-wrap, but the new Markdown renderer does not preserve soft line breaks. Plain-text stories that rely on single newlines now collapse those breaks into spaces, which violates the ticket's requirement that existing plain-text stories keep rendering correctly.
    • File: src/components/StoryContent.tsx:38
    • Suggestion: preserve single-line breaks for legacy/plain-text content, for example by enabling a breaks plugin or otherwise restoring pre-wrap-equivalent behavior where Markdown should still render naturally.
  • [medium] The issue explicitly requires strikethrough support, but this implementation never enables GFM parsing, so ~~text~~ remains literal text instead of rendering as <del>.
    • File: src/components/StoryContent.tsx:38
    • File: package.json:28
    • Suggestion: add remark-gfm (or equivalent) for parsing strikethrough, then continue blocking unsupported elements such as tables/images at the render/sanitize layer.

Decision

Requesting changes because the PR does not yet meet the required Markdown syntax support and currently regresses existing plain-text story formatting.

… breaks

- remark-gfm enables GFM parsing so ~~strikethrough~~ renders as <del>
- remark-breaks preserves single newlines as <br> so existing plain-text
  stories don't collapse line breaks into spaces

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

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

Verdict: APPROVE

Summary

The remaining Markdown gaps are fixed. The renderer now preserves soft line breaks for existing plain-text stories and enables the required strikethrough syntax, while keeping the existing sanitization path in place.

Findings

  • None.

Decision

Approving because the Markdown rendering and Write/Preview toggle now satisfy the Batch 25 / #562 requirements without the earlier plain-text formatting regression.

@realproject7 realproject7 merged commit e036aca into main Mar 26, 2026
5 checks passed
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.

Support Markdown rendering for story content + Write/Preview toggle

2 participants