Skip to content

feat: add live preview side panel with debounced markdown rendering - #974

Open
BettyXu2001 wants to merge 4 commits into
rahuldkjain:mainfrom
BettyXu2001:agent/live-readme-preview
Open

feat: add live preview side panel with debounced markdown rendering#974
BettyXu2001 wants to merge 4 commits into
rahuldkjain:mainfrom
BettyXu2001:agent/live-readme-preview

Conversation

@BettyXu2001

Copy link
Copy Markdown

🔄 Pull Request

📋 Type of Change (check all applicable)

  • 🐛 Bug Fix - Fixes an issue without breaking existing functionality
  • Feature - Adds new functionality
  • Performance - Improves performance without changing functionality (150ms debounced markdown generation for live preview)
  • ♻️ Refactor - Code changes that neither fix bugs nor add features
  • 📚 Documentation - Updates to documentation, comments, or README
  • 🎨 Style - Code style changes (prettier formatting applied to 6 files)
  • 🧪 Test - Adding or updating tests (21 new vitest cases for markdown preview)
  • 🏗️ Build - Changes to build system or dependencies
  • 👷 CI/CD - Changes to CI/CD workflows
  • 🔒 Security - Security improvements or vulnerability fixes
  • Accessibility - Improves accessibility compliance (ARIA labels, roles, live regions)
  • 📱 Mobile - Mobile-specific improvements (floating bottom-sheet preview below lg breakpoint)
  • 🌐 i18n - Internationalization changes

📖 Description

What changed?

Adds a persistent Live Preview side panel that renders the README in real time as the user fills in the profile form, eliminating the need to navigate to the final "Preview" step to see the result.

New feature (c335414):

  • src/components/ui/markdown-preview.tsx — new MiniMarkdownPreview export, a lightweight preview variant (no copy/download buttons, no view-mode toggle, compact typography) purpose-built for the side panel.
  • src/app/page.tsx
    • Split layout: form on the right, sticky live preview on the left (flex-col on mobile, lg:flex-row ~48/52 split on desktop).
    • 150ms debounced markdown re-generation to keep typing responsive even with large skills lists.
    • Live Preview panel with header controls: Expand/Shrink (Maximize2/Minimize2) and Hide (EyeOff).
    • Animated green "Live" status indicator (pulsing dot).
    • Show/Hide preview toggle in the toolbar.
    • Mobile/tablet (<lg): floating "Preview" FAB opens a bottom-sheet preview with grab handle and dismissible backdrop.
    • Suspense skeleton fallbacks while preview loads.
    • Preview auto-hides when user reaches the final "Preview & Generate" step (where the full MarkdownPreview already lives).

Tests (c90b5d0):

  • src/components/ui/__tests__/markdown-preview.test.tsx — 21 vitest + React Testing Library cases covering both MiniMarkdownPreview (13) and MarkdownPreview (8): rendering of all markdown node types, image sizing branches, GFM tables, XSS sanitization, view-mode toggle, clipboard copy, file download, ARIA roles, and React.memo instance stability.

Formatting (1b40c78, c3edfe6):

  • Ran npm run format to fix prettier drift in 6 files (5 historical + the new test file). No logic changes — only whitespace, line wrapping, and Tailwind class ordering via prettier-plugin-tailwindcss.

Why was this change made?

Previously, users had to complete all 5 form steps and navigate to the final "Preview & Generate" step to see what their README looked like. This broke the edit→review loop and required frequent back-and-forth when iterating on wording, section ordering, and layout — especially painful when tweaking skill badges or social links.

How does this change help users?

  • Faster iteration: see changes reflected in ~150ms instead of navigating across steps.
  • Less cognitive load: form and preview share the screen on desktop; no context switch.
  • Mobile-friendly: mobile users get a bottom-sheet preview that doesn't compete with the form for vertical space, triggered by a floating FAB.
  • Performance: debounced re-generation + the lighter MiniMarkdownPreview component keep typing smooth even on long documents.

🔗 Related Issues

  • Closes #
  • Fixes #
  • Related to #

🧪 Testing & Quality Assurance

Testing Done (check all applicable)

  • Manual testing - Tested functionality manually (local dev server, field-by-field edits confirm live preview updates with 150ms debounce)
  • 🧪 Unit tests - Added/updated unit tests (21 new cases, 34 total passing)
  • 🔄 Integration tests - Tested with other components
  • 📱 Mobile testing - Tested on mobile devices ⚠️ layout coded for 320px+, but needs real device / Chrome devtools device-mode QA
  • Accessibility testing - Tested with screen readers/keyboard nav ⚠️ ARIA labels added but no screen reader run-through
  • 🌐 Cross-browser testing - Tested in multiple browsers
  • 🎨 Visual testing - Checked UI/UX in light/dark themes

Test Instructions

  1. npm install && npm run dev, open http://localhost:3000
  2. On desktop (width ≥ 1024px), fill in Basic Info and confirm the left Live Preview panel updates with ~150ms lag.
  3. Click Hide Preview in the toolbar; confirm the form expands to full width. Click Show Preview to bring it back.
  4. Click the Expand (Maximize2) icon in the preview header; verify no visual break.
  5. Narrow the viewport to < 1024px. Confirm a floating Preview FAB appears bottom-right; tap it to open the bottom-sheet preview, tap backdrop or Close to dismiss.
  6. Progress through all 5 steps; preview should hide automatically at step 5 (where the full MarkdownPreview already lives).
  7. Run npm test — all 34 tests should pass.
  8. Run npm run build — static export should succeed (9/9 pages prerendered).

Expected Behavior

  • Preview updates reflect every form field change (Basic Info / Links / Social / Skills) with a debounced feel.
  • Desktop: 2-column split with sticky preview (panel scrolls independently).
  • Mobile: preview is a bottom sheet with a grab handle and backdrop; doesn't overlap the FAB trigger.
  • All previous form navigation (Next / Previous / Progress stepper / auto-save / JSON export) continues to work unchanged.

📸 Screenshots/Recordings

Before

Before: form-only layout, no preview until user reaches the final "Preview & Generate" step
image
image

After

After: side-by-side layout on desktop with sticky left preview; floating bottom sheet on mobile; Show/Hide toggle button in the toolbar
image
Uploading image.png…

📋 Checklist

Code Quality

  • 🔍 TypeScript - No TypeScript errors (npm run type-check) ✅ 0 errors
  • 🧹 Linting - No ESLint errors (npm run lint) ✅ 0 errors / 42 warnings (4 react-hooks/exhaustive-deps warnings on JSON.stringify deps in the auto-save useEffect — an existing pattern in the codebase, suppressed with eslint-disable-next-line)
  • 🎨 Formatting - Code is properly formatted (npm run format) ✅ format:check reports "All matched files use Prettier code style"
  • 🏗️ Build - Production build succeeds (npm run build) ✅ 9/9 pages prerendered, static export 2/2
  • Performance - No performance regressions introduced (debounced markdown re-generation, lighter MiniMarkdownPreview component)

Accessibility

  • WCAG Compliance - Follows WCAG 2.1 AA guidelines ⚠️ needs formal audit
  • ⌨️ Keyboard Navigation - All interactive elements are keyboard accessible (native <button> throughout, natural tab order)
  • 🔍 Screen Reader - Proper ARIA labels and semantic HTML
    • Progress nav: <nav aria-label="Form progress">, each step button has aria-label + aria-current="step".
    • Live panel controls: Expand/Shrink/Hide buttons with descriptive aria-label.
    • Step announcements: sr-only live region with aria-live="polite" aria-atomic="true".
    • Form nav: <nav aria-label="Form navigation">, Previous/Next buttons carry descriptive aria-label.
    • Mobile: FAB, backdrop, mobile toggle all carry aria-label.
  • 🎨 Color Contrast - Meets contrast requirements ⚠️ needs manual pass in light/dark
  • 🎯 Focus Management - Visible focus indicators (native focus on buttons; consistent with rest of project)
  • ⚠️ Minor gap: The green "Live" status indicator in the panel header currently uses only title="Live". Consider wrapping the dot + label in <div role="status" aria-live="polite"> or adding an explicit aria-label="Live preview status: live" for screen readers. (Non-blocking; happy to address in a follow-up.)

Mobile & Responsive

  • 📱 Mobile Responsive - Works on mobile devices (320px+)
    • flex flex-col lg:flex-row main split; preview drops to an FAB-triggered bottom sheet below lg.
    • Progress stepper: overflow-x-auto with min-w-max for horizontal scroll on small widths.
    • Typography: sm:, md:, lg: breakpoints consistently applied.
  • 🖥️ Desktop - Works on desktop (1024px+) — sticky left preview ~48% width.
  • 📐 Tablet - Works on tablet sizes (768px+) ⚠️ needs manual verification at 768–1023px
  • 🔄 Orientation - Works in portrait and landscape ⚠️ needs verification

Browser Compatibility

  • 🌐 Chrome - Latest version
  • 🦊 Firefox - Latest version
  • 🧭 Safari - Latest version
  • 📱 Mobile Safari - iOS Safari
  • 📱 Chrome Mobile - Android Chrome

Documentation

  • 📚 Code Comments - Added helpful comments for complex logic (MiniMarkdownPreview JSDoc, section divider comments in page layout, debounce explanation)
  • 📖 Documentation - Updated relevant documentation ⚠️ none yet — a one-liner in README or docs would be nice
  • 📝 README - Updated README if needed
  • 🔄 Changelog - Will be auto-generated from conventional commits (commits use feat: / test: / style: per COMMIT_CONVENTION.md)

Security & Privacy

  • 🔒 No Secrets - No API keys, passwords, or sensitive data exposed
  • 🛡️ Input Validation - Proper validation for user inputs (zod schema via zodResolver in useForm; profileSchema / linksSchema / socialSchema unchanged from main)
  • 🔐 XSS Prevention - Protected against XSS attacks
    • MiniMarkdownPreview applies rehypeRaw + rehypeSanitize identical to the existing MarkdownPreview pipeline.
    • Verified by unit test: <script> tags are stripped from rendered output.
    • Markdown content originates only from known form fields; arbitrary HTML in rendered output is sanitized.
  • 🍪 Privacy Compliant - Follows GDPR/privacy requirements (no new third-party calls; analytics events are existing trackReadmeGenerated / trackFileExported patterns)

🚀 Deployment Notes

  • No breaking changes - Backward compatible
  • Database changes - N/A (static site)
  • Environment variables - No new env vars needed
  • Third-party dependencies - No new external dependencies (uses existing framer-motion, lucide-react, react-markdown, rehype-raw, rehype-sanitize, remark-gfm already in the tree)

📝 Additional Notes

  • Commit history (4 commits, all conventional-commit compliant, no force push):
    • c335414 feat: 更新功能 (main feature; description is Chinese — see note below)
    • c90b5d0 test: add markdown preview component tests
    • 1b40c78 style: apply prettier formatting
    • c3edfe6 style: format markdown preview test file
  • Commit message language: c335414 uses a Chinese description (feat: 更新功能). The type (feat) and format match COMMIT_CONVENTION.md, but the description is not in English. If the maintainers prefer English commit messages, I can amend c335414 to feat(ui): add live preview side panel with debounced markdown rendering and force-push to this branch only (no reviews received yet). Please advise.
  • Console output: src/app/page.tsx retains ~15 console.log/debug statements left over from development (auto-save flow, data load, initial state). They don't break anything but will fire in production builds. Recommend removing or gating behind a dev flag (e.g. if (process.env.NODE_ENV === 'development') console.log(...)) before merge. Happy to address in a follow-up commit if desired.
  • any types in MiniMarkdownPreview: The custom components map (p, h1hr) uses any for props to mirror the pattern used by the existing MarkdownPreview in the same file. Consistent with the codebase but not type-strict.
  • Tests: Project uses Vitest. 21 new test cases added for MiniMarkdownPreview and MarkdownPreview. No integration test for the live preview toggle + debounce behavior in page.tsx yet — would require a heavier setup with react-hook-form and framer-motion mocking; can be added in a follow-up if requested.

👀 Reviewers


By submitting this PR, I confirm that:

  • ✅ I have read and agree to the https://github.com/rahuldkjain/github-profile-readme-generator/blob/master/CODE_OF_CONDUCT.md
  • ✅ I have followed the https://github.com/rahuldkjain/github-profile-readme-generator/blob/master/CONTRIBUTING.md
  • ✅ I have used https://github.com/rahuldkjain/github-profile-readme-generator/blob/master/COMMIT_CONVENTION.md for my commit messages (see note about English description for c335414)
  • ✅ I have tested my changes thoroughly
  • ✅ My code follows the project's coding standards

Add 21 vitest + RTL test cases covering MiniMarkdownPreview (13) and
MarkdownPreview (8):

MiniMarkdownPreview:
- Rendering: paragraphs, h1/h2/h3, links, lists, GFM tables, code,
  blockquotes, horizontal rules
- Image sizing branches (width=40 icon vs natural size)
- XSS sanitization (script tags stripped via rehypeSanitize)
- a11y: role=document, no copy/download buttons (lightweight variant)
- React.memo instance stability across identical props

MarkdownPreview:
- Title rendering, default preview mode, raw markdown toggle
- aria-pressed state synchronization on view mode switch
- Clipboard copy via navigator.clipboard.writeText
- File download: object URL creation, anchor click, download attr,
  revokeObjectURL cleanup
- a11y: role=region + role=toolbar
- XSS sanitization parity with MiniMarkdownPreview

Test isolation via afterEach(vi.restoreAllMocks + useRealTimers).
Clipboard stubbed with Object.defineProperty (configurable).
Anchor click captured via prototype wrapper with try/finally restore.
Run \
pm run format\ to fix formatting drift caught by \
pm run
format:check\. No logic changes — only whitespace, line wrapping, and
class attribute ordering adjustments to satisfy prettier
configuration (including prettier-plugin-tailwindcss).

Files:
- src/app/page.tsx
- src/components/sections/links-section.tsx
- src/components/ui/markdown-preview.tsx
- src/lib/storage.ts
- src/styles/tailwind.css
Apply prettier formatting to the newly added test file missed in
the previous test commit. No content changes.
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