feat(web): generate OG images, redesign share flow, and overhaul stack builder DX#1097
Conversation
…k builder dx OG images: - add per-stack dynamic og image route (/og/stack) rendering the project name and color-coded tech chips from the share url params - add designed og cards for home, /new, /showcase, /analytics (/og/site/[page]) replacing the misdeclared 3166x1909 raw screenshot - extract shared OgShell terminal frame and refactor the docs og route onto it; /stack metadata now emits the per-stack image, stack summary as description, and keeps query params in og:url Share flow: - rebuild ShareDialog: live social-card preview, copy link + copy cli command rows, native share, better X post text, on-demand QR - normalize project names (spaces to dashes) consistently across the dialog, /stack page, and builder hook Stack builder: - collapse cli command to one line with a flags toggle - flatten selected-stack badges into a chip cloud; badges now jump to their category section (X removes) - add sticky category rail with scroll-spy active state and jump-to-section - consolidate actions into one panel; inline YOLO toggle, drop the single-item settings menu and "no saved stack" placeholder - responsive fixes: narrower sidebar until lg, mobile rail, a11y wiring for the project name error Compatibility: - mirror the cli rule blocking tauri with convex better-auth on next.js or tanstack start (disable reason + auto-adjust) - add property test: 3000 random stacks converge and contain no disabled selections after adjustment
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR adds shared Open Graph rendering primitives and new OG image routes, updates page metadata to reference new OG image URLs, introduces stack-utils helpers for project-name formatting and selected-tech derivation, rewrites the share dialog, reworks stack builder UI (category navigation, badge flattening, action buttons, CLI command expansion), and adds a Tauri/Convex/Better-Auth compatibility rule with tests. ChangesOG Image Revamp and Stack Builder UI
Tauri/Convex/Better-Auth Compatibility Rule
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. 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. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/app/(home)/stack/page.tsx (1)
17-46: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winNormalize
projectNamebefore building/stackmetadata
loadStackParams()preserves raw query values, so hand-edited or legacy/stack?name=...URLs can render unformatted titles and OG alt text here. Formatparams.projectNamethe same way asShareDialogand the builder to keep previews consistent.
🧹 Nitpick comments (5)
apps/web/src/lib/og.tsx (1)
65-93: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueOptional: dedupe the traffic-light dots.
The three colored dot
divs only differ by color; could be mapped from a small array for less repetition. Purely cosmetic.♻️ Optional dedup
- <div style={{ display: "flex", gap: "8px" }}> - <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.red, display: "flex" }} /> - <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.yellow, display: "flex" }} /> - <div style={{ width: "12px", height: "12px", borderRadius: "50%", background: ogColors.green, display: "flex" }} /> - </div> + <div style={{ display: "flex", gap: "8px" }}> + {[ogColors.red, ogColors.yellow, ogColors.green].map((c) => ( + <div key={c} style={{ width: "12px", height: "12px", borderRadius: "50%", background: c, display: "flex" }} /> + ))} + </div>apps/web/src/app/og/site/[page]/route.tsx (1)
57-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCommand-line block duplicates the one in the stack OG route.
This
$ <command>block (dollar sign span + monospace command text) is nearly identical to the one inapps/web/src/app/og/stack/route.tsx(lines 58-71), minus the blinking-cursordiv. Could be extracted into a small sharedOgCommandLineprimitive inapps/web/src/lib/og.tsxalongsideOgShellto keep styling consistent as more OG pages are added.apps/web/src/app/(home)/new/_components/stack-builder/category-nav.tsx (2)
45-73: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer tracking category alongside element instead of re-parsing from id.
sectionsis built by mappingprogressto elements and losing the category association; the loop then recovers it viasection.id.slice(idPrefix.length + 1) as typeof current(line 64), requiring an unchecked cast. Zipping{ category, el }pairs up front would avoid the string-slicing/cast round-trip.♻️ Proposed refactor
- const sections = progress - .map(({ category }) => document.getElementById(`${idPrefix}-${category}`)) - .filter((el): el is HTMLElement => !!el); - const viewport = sections[0] ? getScrollViewport(sections[0]) : null; + const sections = progress + .map(({ category }) => ({ + category, + el: document.getElementById(`${idPrefix}-${category}`), + })) + .filter((s): s is { category: TechCategory; el: HTMLElement } => !!s.el); + const viewport = sections[0] ? getScrollViewport(sections[0].el) : null; @@ - let current = progress[0]?.category ?? null; - for (const section of sections) { - if (section.getBoundingClientRect().top - viewportTop <= 80) { - current = section.id.slice(idPrefix.length + 1) as typeof current; - } - } + let current = progress[0]?.category ?? null; + for (const section of sections) { + if (section.el.getBoundingClientRect().top - viewportTop <= 80) { + current = section.category; + } + }
94-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
aria-currentto the active category chip.The active chip is only conveyed visually (ring styling); screen-reader users have no indication which category is currently active while scrolling.
♻️ Proposed fix
<button key={category} type="button" data-category={category} + aria-current={isActive ? "true" : undefined} onClick={() => scrollToCategorySection(idPrefix, category)}apps/web/src/lib/stack-utils.ts (1)
56-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated sentinel-filtering logic vs.
generateStackSummary.The
"none"/"false"/git|install|auth === "true"skip rules here duplicate the identical filtering already implemented ingenerateStackSummary(same file, lines 81-105). If one is updated without the other, badge counts and summary text will silently diverge.Consider extracting a shared
isSelectableTechId(category, id)helper used by both functions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f2325371-b6a0-4e20-a059-9c9609cef2a3
📒 Files selected for processing (22)
apps/web/src/app/(home)/analytics/page.tsxapps/web/src/app/(home)/new/_components/action-buttons.tsxapps/web/src/app/(home)/new/_components/share-button.tsxapps/web/src/app/(home)/new/_components/stack-builder/category-nav.tsxapps/web/src/app/(home)/new/_components/stack-builder/index.tsxapps/web/src/app/(home)/new/_components/stack-builder/selected-stack-badges.tsxapps/web/src/app/(home)/new/_components/stack-builder/tech-categories.tsxapps/web/src/app/(home)/new/_components/stack-builder/use-stack-builder.tsapps/web/src/app/(home)/new/_components/utils.tsapps/web/src/app/(home)/new/_components/yolo-toggle.tsxapps/web/src/app/(home)/new/page.tsxapps/web/src/app/(home)/showcase/page.tsxapps/web/src/app/(home)/stack/_components/stack-display.tsxapps/web/src/app/(home)/stack/page.tsxapps/web/src/app/layout.tsxapps/web/src/app/og/docs/[...slug]/route.tsxapps/web/src/app/og/site/[page]/route.tsxapps/web/src/app/og/stack/route.tsxapps/web/src/components/ui/share-dialog.tsxapps/web/src/lib/og.tsxapps/web/src/lib/stack-utils.tsapps/web/test/stack-compatibility-invariant.test.ts
💤 Files with no reviewable changes (1)
- apps/web/src/app/(home)/new/_components/yolo-toggle.tsx
|
@coderabbitai review |
✅ Action performedReview finished.
|
OG images
The site-wide
og:imagepointed at a 3166×1909 raw homepage screenshot on R2, declared as 1200×630, and shared stacks all rendered the same generic preview./og/stack— dynamic per-stack OG card: project name,$ <pm> create better-t-stack@latest <name>prompt line, and color-coded chips for every selected tech (capped at 15 + "+N more"). Params parse through the same sanitizing nuqs loader as/stack./og/site/[page]— designed 1200×630 cards for home,/new,/showcase,/analytics(allowlisted, statically generated).OgShellterminal frame (Catppuccin palette); the docs OG route is refactored onto it so all three generators stay visually in sync./stackmetadata now emits the per-stack image, uses the tech summary as the OG description, and keeps query params inog:url(previously dropped).Share flow
ShareDialogrebuilt: live social-card preview (the actual/og/stackrender), one-click copy rows for the share link and the CLI command, native share sheet when available, stack-aware X post text, and an on-demand QR toggle. Project names with spaces are now normalized identically across the dialog, the/stackpage, and the builder hook.Stack builder DX
lg, mobile rail,aria-describedbyon the project-name error, focus/active rings no longer clipped by the rail.CLI parity + tests
Audited every web compatibility rule against
apps/clivalidation. One gap found and fixed: the CLI rejectstauriwith Convex Better Auth on Next.js/TanStack Start; the web now disables it with the same reason and auto-adjusts.New property test: 3,000 seeded random stacks run through sanitize + the adjust cascade to a fixpoint, asserting convergence and that no selected option remains in a disabled state — i.e. every copyable command is CLI-valid.
Test plan
bun testinapps/web(21 tests, incl. new invariant + regression tests)bun run checkclean;tsc --noEmitclean apart from pre-existingbun:testresolutionSummary by CodeRabbit
New Features
Bug Fixes