1. ScheduleTab + ChannelsTab: wrap toggle/delete in try/catch with
error feedback (was silently swallowing API failures)
2. MemoryTab: "+Add" button now auto-expands Advanced section
3. SidePanel: keyboard-navigated tabs scroll into view
4. TracesTab: emoji aria-hidden, env-var hint in <details>
5. page.tsx: show Spinner while hydrating instead of flash of EmptyState
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- #554 CRITICAL: Add hydrationError state to Zustand store; catch handler now
calls setHydrationError instead of silent console.error; page renders a
full-screen zinc-950 error banner with a Retry button that reloads the page
- #556 MEDIUM: Add roving tabIndex + ArrowDown/Up/Left/Right keyboard handler
to the tier radio group in CreateWorkspaceDialog (WCAG 2.1 compliant)
- #557 MEDIUM: Add "Zoom to Team" menu item to ContextMenu (visible only when
node has children); dispatches molecule:zoom-to-team for keyboard accessibility
- Bonus: add missing 'use client' directive to RevealToggle.tsx
Co-authored-by: Molecule AI Frontend Engineer <frontend-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a public /pricing route the apex + tenant canvas can both serve.
Three-tier plan cards (Free, Starter, Pro) with per-plan CTA buttons
that dispatch correctly regardless of the user's state:
Free → redirect to signup
Anonymous + paid → redirect to signup (Stripe opens post-auth)
Authed + paid → POST /cp/billing/checkout, redirect to Stripe URL
No tenant slug → inline error ("pick an org first")
Network failures → surfaced in an ARIA alert banner
Files:
- src/lib/billing.ts — plan metadata + startCheckout + openBillingPortal
wrappers over /cp/billing/{checkout,portal}
- src/components/PricingTable.tsx — client component, lazy session
probe on first CTA click (no probe for anonymous browsers)
- src/app/pricing/page.tsx — server-rendered shell with SEO metadata,
links to legal pages in the footer
- Tests: 10 billing helper tests + 9 PricingTable tests (17 total,
additional ones cover the plan-list canonical order)
Design notes:
- The pricing data (features + prices) is a static const in billing.ts,
not fetched from the API. Changing prices requires a deploy — which
we'd need to do anyway for tier definition changes.
- PLAN_ID 'starter' is flagged highlighted=true so the middle card gets
the 'Most popular' visual treatment. One source of truth; test locks it.
- Session probe is lazy (first CTA click, not mount) so anonymous
visitors don't generate a /cp/auth/me request just to read the page.
AuthGate interaction:
- On apex (no tenant slug), AuthGate passthrough — /pricing renders freely
- On tenant subdomain, AuthGate still bounces anonymous users to login
before reaching /pricing — this is the correct UX for the "I'm already
logged in and want to upgrade my own org" flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a GDPR/ePrivacy-compliant cookie banner to the canvas root layout.
Privacy-preserving default: no optional cookies are considered accepted
until the user clicks "Accept all". Clicking "Necessary only" or
dismissing records "rejected" and the banner does not re-appear until
the cookie-policy version bumps.
- New CookieConsent component wired into src/app/layout.tsx so it
renders on every canvas route
- Persists decision to localStorage as {decision, decidedAt, version}
- Versioned schema: bumping CURRENT_VERSION re-prompts every user
- Exports hasConsent() helper for feature code that needs to gate
analytics / functional cookies on user choice
- ARIA: role=dialog + aria-labelledby/aria-describedby so screen
readers announce it as a dialog
- Same storage key + schema as the control-plane legal-page banner
(see molecule-controlplane PR #XX) so a user who accepts on one
surface does not re-see the banner on the other
Tests: 12 Vitest cases covering first-visit render, accept/reject
persistence, version re-prompt, invalid-JSON recovery, privacy link
attrs, ARIA markup, and the hasConsent helper under every state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wraps the canvas root so every tenant-subdomain request checks for a
valid session and bounces to app.moleculesai.app/cp/auth/login with a
return_to pointing back at the current URL. Local dev + vercel preview
URLs + apex pass through unchanged.
Files:
- canvas/src/lib/auth.ts: fetchSession() probes /cp/auth/me
(credentials:include for cross-origin cookie); returns Session on 200,
null on 401 (anonymous, no throw), throws on 5xx so transient
outages don't leak the UI.
- canvas/src/lib/auth.ts: redirectToLogin() builds the cp login URL
with window.location.href as return_to; CP's isSafeReturnTo check
rejects cross-domain bounces.
- canvas/src/components/AuthGate.tsx: client component wrapping
children. State machine: loading → authenticated | anonymous. In
non-SaaS mode (no tenant slug) skips the gate entirely.
- canvas/src/app/layout.tsx: wraps the root body in <AuthGate>.
Tests: +6 auth.ts (200 / 401 null / 5xx throw / credentials:include /
redirectToLogin href + signup variant). Full suite 453 green (was 447).
Pairs with molecule-controlplane PR #16 (return_to cookie handshake
on the cp side).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
globals.css: append @media (prefers-reduced-motion: reduce) block that zeroes
animation/transition durations, disables .animate-in/.slide-in-from-* entry
animations (Toaster, ApprovalBanner, SidePanel slide), strips dashdraw and
node-appear keyframes from React Flow elements.
Components: replace all bare animate-pulse (13 occurrences across WorkspaceNode,
StatusDot, Toolbar, SidePanel, Legend, SearchDialog, TerminalTab, TemplatePalette)
with motion-safe:animate-pulse so status indicator pulsing stops for users with
vestibular disorders. Replace 3 animate-bounce occurrences in ChatTab typing
indicator with motion-safe:animate-bounce.
Tests: new canvas/src/__tests__/reduced-motion.test.ts (12 tests) verifies the
@media block is present in globals.css and that every component file uses the
motion-safe: variant rather than bare animation classes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swap in the new molecular-graph icon across canvas favicon, in-app logo,
and README branding paths. Add HANDOFF.md as the cross-session context
doc carried over from the Starfire→Molecule AI migration. Fix stale
"Starfire" reference in the pre-commit hook header.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>