Add public homepage with live demo and auth-aware routing#31
Conversation
Defines the public-facing homepage for Wafflebase with hero, live demo iframe, feature cards, developer section (REST API/CLI), open source CTA, and footer with theme toggle. Co-Authored-By: Claude Opus 4.6 <[email protected]>
12-task plan covering theme infrastructure, routing, 6 section components, and final composition. Also adds docs/specs/ as the spec location in CLAUDE.md. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Allows external control of theme for iframe embedding. Skips localStorage when theme is set via URL parameter. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Unauthenticated users see landing page at / with hero, live demo iframe, feature cards, developer API/CLI examples, open source CTA, and footer with theme toggle. Authenticated users redirect to their workspace. Co-Authored-By: Claude Opus 4.6 <[email protected]>
REST API examples now show range queries, formula writes, and batch updates with delete (null). CLI examples use actual wafflebase command names with --formula, --format, and batch via stdin. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Tokenizes shell code into comments, strings, flags, commands, HTTP methods, and prompts with distinct colors. No external dependencies — uses a simple line-based tokenizer. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Remove batch update and output format examples to keep the code blocks concise and scannable. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Remove workspace redirect from HomeOrRedirect so the homepage is always rendered at /. When logged in, "Get Started" buttons change to "Go to Workspace" linking to the user's workspace. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Hide nav anchor links and footer links on small screens. Reduce padding (px-4 on mobile, px-12 on md+), scale down hero title and section spacing for narrow viewports. Co-Authored-By: Claude Opus 4.6 <[email protected]>
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughIntroduces a new public-facing homepage with seven section components, updates root routing to conditionally render the homepage or redirect authenticated users, enhances the theme provider to support external control via URL parameters and postMessage, and removes the legacy WorkspaceRedirect component. Changes
Sequence DiagramssequenceDiagram
participant User
participant Browser
participant HomeOrRedirect as HomeOrRedirect
participant AuthAPI as Auth API
participant HomePage
User->>Browser: Navigate to /
Browser->>HomeOrRedirect: Render HomeOrRedirect
HomeOrRedirect->>AuthAPI: fetchMeOptional (no retries)
AuthAPI-->>HomeOrRedirect: User data | null
alt User Authenticated
HomeOrRedirect->>AuthAPI: fetchWorkspaces
AuthAPI-->>HomeOrRedirect: Workspaces[]
HomeOrRedirect->>HomeOrRedirect: Compute workspacePath from first workspace
HomeOrRedirect->>HomePage: Render HomePage with workspacePath
HomePage-->>Browser: Display homepage with workspace link
else User Not Authenticated
HomeOrRedirect->>HomePage: Render HomePage with workspacePath=null
HomePage-->>Browser: Display homepage with "Get Started" link to /login
end
sequenceDiagram
participant Page as Page Load
participant ThemeProvider
participant URLParams as URL & Storage
participant IFrame as Demo IFrame
participant External as External Source
Page->>ThemeProvider: Initialize
ThemeProvider->>URLParams: Check ?theme= parameter
alt ?theme= parameter present
URLParams-->>ThemeProvider: theme value
ThemeProvider->>ThemeProvider: Set externallyControlled=true
ThemeProvider->>ThemeProvider: Use URL theme (skip localStorage)
else No parameter
ThemeProvider->>URLParams: Read from localStorage/default
end
ThemeProvider->>ThemeProvider: Setup postMessage listener
External->>ThemeProvider: postMessage {type: "theme-change", theme}
ThemeProvider->>ThemeProvider: Validate origin & update theme
ThemeProvider->>IFrame: Send postMessage with new theme
IFrame-->>ThemeProvider: Acknowledge
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 |
Verification: verify:selfResult: ✅ PASS in 96.3s
Verification: verify:integrationResult: ✅ PASS |
No longer needed since HomeOrRedirect replaced it at /. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
packages/frontend/src/app/home/features-section.tsx (1)
44-49: Preferfeatureoverfin the list renderer.Using a full name makes this block easier to scan and maintain.
As per coding guidelines: "Use descriptive variable and function names that clearly convey intent and purpose".♻️ Suggested change
- {features.map((f) => ( - <div key={f.title} className="bg-homepage-bg border border-homepage-accent/30 rounded-xl p-7"> - <div className="text-3xl mb-3">{f.icon}</div> - <h3 className="text-lg font-semibold text-homepage-text mb-2">{f.title}</h3> - <p className="text-sm text-muted-foreground leading-relaxed">{f.description}</p> + {features.map((feature) => ( + <div key={feature.title} className="bg-homepage-bg border border-homepage-accent/30 rounded-xl p-7"> + <div className="text-3xl mb-3">{feature.icon}</div> + <h3 className="text-lg font-semibold text-homepage-text mb-2">{feature.title}</h3> + <p className="text-sm text-muted-foreground leading-relaxed">{feature.description}</p> </div> ))}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/features-section.tsx` around lines 44 - 49, The map callback currently uses a short, non-descriptive parameter `f` in the features list renderer; rename it to `feature` in the `features.map(...)` callback and update all uses inside the returned JSX (e.g., `key={feature.title}`, `{feature.icon}`, `{feature.title}`, `{feature.description}`) so the code reads clearly and follows naming guidelines while preserving existing behavior.packages/frontend/src/app/home/opensource-section.tsx (1)
16-22: Use a descriptive map variable name instead ofb.This improves readability in the badge renderer.
As per coding guidelines: "Use descriptive variable and function names that clearly convey intent and purpose".♻️ Suggested change
- {badges.map((b) => ( + {badges.map((badge) => ( <span - key={b} + key={badge} className="bg-homepage-bg border border-homepage-accent/30 rounded-lg px-6 py-3 text-sm text-homepage-text font-semibold" > - {b} + {badge} </span> ))}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/opensource-section.tsx` around lines 16 - 22, The map callback in badges.map currently uses a non-descriptive variable name `b`; update the callback to use a descriptive name (e.g., `badge`) in the badges.map call and inside the JSX span (the element rendering the badge) so the purpose is clear—look for the badges.map(...) usage in the OpenSourceSection/opensource-section component and replace occurrences of `b` with `badge` (or another clear name) in the key, className usage, and the rendered content.packages/frontend/src/app/home/developer-section.tsx (1)
118-132: Consider memoizing the highlighted code blocks.Since
restApiCodeandcliCodeare static constants,highlightCodewill produce identical output on every render. Consider usinguseMemoor computing the highlighted JSX once at module level to avoid redundant tokenization on re-renders.♻️ Example: compute once at module level
+const highlightedRestApi = highlightCode(restApiCode); +const highlightedCli = highlightCode(cliCode); export function DeveloperSection() { return ( // ... <pre className="text-sm font-mono leading-7 whitespace-pre"> - {highlightCode(restApiCode)} + {highlightedRestApi} </pre>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/home/developer-section.tsx` around lines 118 - 132, highlightCode currently re-tokenizes static strings on every render; compute the highlighted JSX once and reuse it by either calling highlightCode(restApiCode) and highlightCode(cliCode) at module scope (e.g. highlightedRestApi / highlightedCli constants) or by wrapping those calls in useMemo inside the component (depend on []), then render those memoized values instead of calling highlightCode directly; reference highlightCode, restApiCode and cliCode to locate and replace the repeated calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/superpowers/specs/2026-03-14-homepage-design.md`:
- Around line 133-151: Update the spec to reflect the implemented routing:
instead of mandating a HomeOrRedirect component outside PrivateRoute that
redirects authenticated users from "/", document that the app currently renders
the homepage at "/" for both authenticated and unauthenticated users and adjusts
CTA state based on auth (useOptionalAuth / fetchMeOptional-driven UI
adaptation), and update the latency mitigation note to explain that
fetchMeOptional is used to avoid forced redirects and to allow the homepage to
render while auth state resolves (mention useOptionalAuth, fetchMeOptional,
PrivateRoute, and the CTA behavior on HomePage/LoadingSpinner/Navigate where
relevant).
In `@packages/frontend/src/app/home-or-redirect.tsx`:
- Around line 16-27: The homepage computes workspacePath too early while the
useQuery for workspaces (queryFn: fetchWorkspaces) is still unresolved, causing
a temporary null that can trigger a login CTA for an authenticated user; update
the logic that derives workspacePath (the const workspacePath) to only decide
between a real path or an explicit “no workspace” value after the workspaces
query has finished (use the query's isLoading or isFetching flag returned by
useQuery or check workspaces !== undefined) so that when user is truthy and
workspaces are still loading you neither return null nor show the login CTA;
reference useQuery, fetchWorkspaces, userLoading, Loader, and workspacePath when
making the change.
In `@packages/frontend/src/app/home/demo-section.tsx`:
- Line 4: Replace the hardcoded DEMO_TOKEN constant by reading it from an
environment variable (e.g., process.env.NEXT_PUBLIC_DEMO_TOKEN or
import.meta.env.VITE_DEMO_TOKEN depending on build system) inside the
demo-section module; update references to use the new token variable and add a
fail-fast check during module initialization or component mount that throws or
logs a clear error and prevents rendering when the env var is missing/empty so
token rotation and secret management are possible. Ensure the symbol DEMO_TOKEN
is removed and replaced consistently where used in demo-section.tsx and validate
the env var presence early.
In `@packages/frontend/src/app/home/footer.tsx`:
- Around line 15-17: The "Docs" and "API" anchor elements in
packages/frontend/src/app/home/footer.tsx (the <a> elements with innerText
"Docs" and "API") currently point to the same GitHub URL; update their href
attributes to the correct documentation and API reference URLs (or add a clear
TODO comment next to those <a> elements indicating the correct links to be
added) so they no longer duplicate the "GitHub" link (you can locate them by
searching for the anchors with className containing "hidden md:inline
text-stone-400" and innerText "Docs" and "API").
In `@packages/frontend/src/components/theme-provider.tsx`:
- Around line 36-45: The externallyControlled flag (created via useState in
ThemeProvider) is computed once from window.location.search and never updates,
causing URL-sourced themes to block future localStorage writes; fix by tracking
the source of theme changes instead: introduce a small source enum/state (e.g.,
'user' | 'external') or a boolean like isUserInitiated and update setThemeState
to accept/record the source, call setThemeState(..., 'external') when applying
theme from URL/postMessage and setThemeState(..., 'user') from UI interactions,
and only write to localStorage (using storageKey) when the source is 'user' so
user toggles persist while URL/postMessage origins do not prevent future
persistence.
---
Nitpick comments:
In `@packages/frontend/src/app/home/developer-section.tsx`:
- Around line 118-132: highlightCode currently re-tokenizes static strings on
every render; compute the highlighted JSX once and reuse it by either calling
highlightCode(restApiCode) and highlightCode(cliCode) at module scope (e.g.
highlightedRestApi / highlightedCli constants) or by wrapping those calls in
useMemo inside the component (depend on []), then render those memoized values
instead of calling highlightCode directly; reference highlightCode, restApiCode
and cliCode to locate and replace the repeated calls.
In `@packages/frontend/src/app/home/features-section.tsx`:
- Around line 44-49: The map callback currently uses a short, non-descriptive
parameter `f` in the features list renderer; rename it to `feature` in the
`features.map(...)` callback and update all uses inside the returned JSX (e.g.,
`key={feature.title}`, `{feature.icon}`, `{feature.title}`,
`{feature.description}`) so the code reads clearly and follows naming guidelines
while preserving existing behavior.
In `@packages/frontend/src/app/home/opensource-section.tsx`:
- Around line 16-22: The map callback in badges.map currently uses a
non-descriptive variable name `b`; update the callback to use a descriptive name
(e.g., `badge`) in the badges.map call and inside the JSX span (the element
rendering the badge) so the purpose is clear—look for the badges.map(...) usage
in the OpenSourceSection/opensource-section component and replace occurrences of
`b` with `badge` (or another clear name) in the key, className usage, and the
rendered content.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 048bf7cd-bd0f-49d1-ad6d-aa1b1368c689
📒 Files selected for processing (16)
CLAUDE.mddocs/specs/2026-03-14-homepage-plan.mddocs/superpowers/specs/2026-03-14-homepage-design.mdpackages/frontend/src/App.tsxpackages/frontend/src/api/auth.tspackages/frontend/src/app/home-or-redirect.tsxpackages/frontend/src/app/home/demo-section.tsxpackages/frontend/src/app/home/developer-section.tsxpackages/frontend/src/app/home/features-section.tsxpackages/frontend/src/app/home/footer.tsxpackages/frontend/src/app/home/hero-section.tsxpackages/frontend/src/app/home/nav-bar.tsxpackages/frontend/src/app/home/opensource-section.tsxpackages/frontend/src/app/home/page.tsxpackages/frontend/src/components/theme-provider.tsxpackages/frontend/src/index.css
- Show loader while workspaces query resolves for authenticated users - Use VITE_DEMO_SHARED_TOKEN env var for demo token with fallback - Scope theme persistence skip to iframe context only (not entire session) - Remove placeholder Docs/API footer links that pointed to GitHub Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Slides: text-edit + adjust drag entry on grouped elements (#22, #31) Two view-layer paths assumed `slide.elements` is flat: - `enterEditMode` resolved the click target via `Array.prototype.find`, so double-click on text/shape elements nested inside a group silently no-op'd. Slide 22 of the Yorkie deck (text "웹/모바일..." in the right column) reproduced this — `Selection.doubleClick` drilled in correctly but the mount path bailed. - `startAdjustmentDrag` used the same flat `find`, so the yellow adjustment diamond on a grouped shape armed but never wrote. Slide 31's Dogfooding pentagonArrow reproduced this. The render-side mask for the editing element and the live preview for adjustments had the matching flat `slide.elements.map` bug — even if the entry paths were fixed in isolation, the canvas would still ghost-paint the element under the in-place editor / miss the live preview for grouped shapes. Both paths now resolve elements via the existing recursive helpers (`findElement`, `buildElementWorldLookup`) and pass world-frame elements where downstream code expects world coords (overlay text-box mount, world↔local adjustment conversion). The store-side mutation APIs (`requireElement`, `updateElementFrame`, `updateElementData`, `withTextElement`, `withShapeText`) already walk the tree via `findElementPath`, so the writes work transparently. Live-verified on slide 22 (text edit enters at the correct world position, no canvas ghost) and slide 31 (Dogfooding arrow's adjustments commit after drag). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * Slides: skip autofit-grow write when ancestor group is transformed Code review caught a silent miswrite hole in the prior commit: text-edit autofit-grow at commit time writes the editor-reported content height (world / canvas-logical coords) straight into `frame.h` via `store.updateElementFrame`, which expects LOCAL coords. For top-level elements local === world; for groups with rotation 0 + unit scale, w/h/rotation also match. But for a rotated or non-unit-scale group, the world height would be stored as local height, scrambling the element's frame. Detect transformed ancestor by comparing local vs world frame (w/h/rotation) at enter time and skip the autofit-grow write when they diverge. Properly composing the inverse ancestor transform is the right long-term fix, but until then this guard prevents the silent corruption. Scope is unchanged: slide 22's groups have rotation 0 + refSize === frame, so the autofit-grow path still runs there. Also annotate the todo's out-of-scope section with the matching `startConnectorEndpointDrag` flat-find pattern (editor.ts:3135) so future audits don't miss it. PPTX import can produce connectors inside groups; the editor-side group op forbids it, so the user-reachable failure mode is import-only — deferred. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Summary
/with hero, live demo iframe, feature cards, developer API/CLI examples, open source CTA, and footer with theme toggle?theme=query param andpostMessagefor iframe theme sync/; authenticated users see "Go to Workspace" CTA, unauthenticated see "Get Started"Test plan
/while logged out — homepage renders with all sections/while logged in — homepage renders with "Go to Workspace" button/login(not homepage)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Authentication & Routing