Skip to content

fix(dashboard): render code-tabs lines as blocks + auto-activate sole org#391

Merged
duyet merged 2 commits into
mainfrom
claude/fix-landing-codeblock-and-org-scope
Jul 17, 2026
Merged

fix(dashboard): render code-tabs lines as blocks + auto-activate sole org#391
duyet merged 2 commits into
mainfrom
claude/fix-landing-codeblock-and-org-scope

Conversation

@duyet

@duyet duyet commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Summary

Two user-reported fixes, both deployed to production already.

1. Landing hero code block collapsed to one line

Each highlighted code line was an inline <span> with no newline between siblings, so inside <pre> the whole snippet flowed onto a single horizontal row (see the broken TypeScript/Vercel AI/LangGraph tabs). Each line now renders as a block.

While here, the active-tab highlight was also dead: Astro <style> blocks are static CSS, so the ${uid} selectors never interpolated. Rewrote them with :nth-of-type correlation (no uid needed).

2. Dashboard shows no data for a user's org

A user who belongs to exactly one org still starts on Clerk's personal account (no o_id claim), so verifyDashboardSession resolves to personal:<userId> — an org with no row — and the dashboard renders empty while the org's projects sit intact under the real org id. This matches the org-tenancy-model history (#387/#389).

Fix: auto-activate the sole org when none is active, guarded by sessionStorage so the post-activation window.location.reload() can't loop if the active org fails to persist.

Verification

  • bun run build (dashboard) ✓
  • Deployed to production; /, /api, /dashboard/ all return 200
  • Confirmed class="block" spans present in production landing HTML

Notes

The data was never lost — 9 projects / 101 conversations remain under org eHyHEJ4YQnplY5WHvJJjP (Clerk org_3B0cAAPs5mwolujADLk7UdIWHuI). This makes single-org users land on their data instead of an empty personal scope.

Co-Authored-By: Duyet Le me@duyet.net
Co-Authored-By: duyetbot bot@duyet.net

Summary by Sourcery

Fix landing page code tabs rendering and improve dashboard org activation behavior for single-org users.

Bug Fixes:

  • Render each code-tab code line as a block so multi-line snippets display correctly in the landing hero.
  • Restore visual highlighting of the active code tab without relying on dynamic CSS selectors.
  • Auto-activate a user’s sole organization in the dashboard so they land on org-scoped data instead of an empty personal scope.

Summary by CodeRabbit

  • New Features

    • Automatically activates the only available organization for eligible users, avoiding an empty personal-scope dashboard on first load.
  • Bug Fixes

    • Improved code tab rendering and active-tab highlighting for more consistent display across tab selections.

… org

Two landing/dashboard fixes:

- code-tabs: each highlighted code line was an inline <span> with no newline
  between siblings, so inside <pre> the whole snippet collapsed onto one
  horizontal row. Render each line as a block. Also rewrite the active-tab
  style selectors — Astro <style> is static CSS, so the `${uid}` selectors
  never interpolated and the active tab never highlighted; use :nth-of-type
  correlation instead.

- app-shell: a user who belongs to exactly one org still starts on Clerk's
  personal account (no `o_id` claim), so the session resolves to
  `personal:<userId>` and the dashboard renders empty while the org's data
  sits intact under the real org. Auto-activate the sole org when none is
  active, guarded by sessionStorage so the post-activation reload can't loop.

Co-Authored-By: Duyet Le <me@duyet.net>
Co-Authored-By: duyetbot <bot@duyet.net>
@sourcery-ai

sourcery-ai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR fixes two user-facing issues: code tabs on the landing hero now render each highlighted line as a block and correctly highlight the active tab via static CSS, and the dashboard auto-activates a user’s sole organization when no org is active, ensuring they land on org-scoped data instead of an empty personal scope.

Sequence diagram for auto-activating a sole organization in the dashboard

sequenceDiagram
  actor User
  participant Clerk
  participant SidebarOrgScope
  participant sessionStorage
  participant window

  User->>Clerk: Authenticate
  Clerk-->>SidebarOrgScope: isLoaded, organizations, activeOrg, setActive
  SidebarOrgScope->>SidebarOrgScope: useEffect
  alt [isLoaded && setActive && !activeOrg && organizations.length === 1]
    SidebarOrgScope->>sessionStorage: getItem(agentstate:auto-activated-org)
    alt [no guard item]
      SidebarOrgScope->>sessionStorage: setItem(agentstate:auto-activated-org, 1)
      SidebarOrgScope->>Clerk: setActive({ organization: organizations[0].id })
      Clerk-->>SidebarOrgScope: Promise resolved
      SidebarOrgScope->>window: location.reload()
      window-->>User: Reloaded dashboard with active org scope
    else [guard already set]
      SidebarOrgScope-->>User: Dashboard remains in current scope
    end
  else [conditions not met]
    SidebarOrgScope-->>User: No auto-activation
  end
Loading

File-Level Changes

Change Details Files
Render code-tab lines as block elements and update tab highlight logic to use static, position-based CSS selectors.
  • Add a block display class to each highlighted code line span in the code tabs component so lines stack vertically inside the
    .
  • Replace uid-interpolated CSS selectors with :has + :nth-of-type correlation between checked inputs and tab labels, making active tab highlighting work with Astro’s static <style> blocks.
packages/dashboard/src/components/home/code-tabs.astro
Auto-activate the sole organization for a user when none is active, guarded to prevent reload loops.
  • Introduce a useEffect in the sidebar org scope that detects sessions with exactly one organization and no active org.
  • Use sessionStorage as a guard key so auto-activation runs at most once per browser session.
  • Call setActive with the single org id and reload the page after successful activation to render org-scoped dashboard data.
packages/dashboard/src/components/app-shell.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@duyet, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 372bee33-2a66-4ef1-8152-b8604d70cc64

📥 Commits

Reviewing files that changed from the base of the PR and between 8948e6c and a15f4f7.

📒 Files selected for processing (1)
  • packages/dashboard/src/components/app-shell.tsx
📝 Walkthrough

Walkthrough

The PR adds automatic activation for a user’s only organization when no active organization exists, and updates code tab line markup and active-tab CSS selectors.

Changes

Organization scope activation

Layer / File(s) Summary
Single-organization activation
packages/dashboard/src/components/app-shell.tsx
SidebarOrgScope checks organization and session state, activates the sole organization once through sessionStorage, and reloads the page.

Code tab rendering

Layer / File(s) Summary
Code tab markup and selectors
packages/dashboard/src/components/home/code-tabs.astro
Highlighted lines render in block-level spans, while active-tab labels use positional checked-input selectors for up to five tabs.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SidebarOrgScope
  participant ClerkSession
  participant sessionStorage
  participant Browser
  SidebarOrgScope->>ClerkSession: Check organization state
  SidebarOrgScope->>sessionStorage: Check activation guard
  SidebarOrgScope->>ClerkSession: Activate sole organization
  SidebarOrgScope->>Browser: Reload dashboard
Loading

Possibly related issues

  • duyet/agentstate#387 — Addresses the same single-organization activation behavior in app-shell.tsx.

Possibly related PRs

  • duyet/agentstate#390 — Modifies the same SidebarOrgScope flow using setActive(...) and a page reload.

Poem

A bunny found one org alone,
And guided it to dashboard home.
Tabs stood tall in blocks of light,
Their labels clicked into place just right.
Hop, hop—the scope now blooms!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures both main dashboard fixes: block rendering for code tabs and auto-activating a sole organization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-landing-codeblock-and-org-scope

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The :nth-of-type selectors in code-tabs.astro hard-code support for five tabs and assume the inputs and labels remain in strict positional sync; consider deriving these rules from the tabs array or using a class-based correlation to avoid breakage if the number or order of tabs changes.
  • The auto-activation side effect in SidebarOrgScope runs a full window.location.reload() after setting the active org; you might want to guard against unnecessary reloads when the active org is already correct or handle failures from setActive more explicitly (e.g., clearing the sessionStorage guard) to avoid leaving users stuck in an unexpected state.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `:nth-of-type` selectors in `code-tabs.astro` hard-code support for five tabs and assume the inputs and labels remain in strict positional sync; consider deriving these rules from the tabs array or using a class-based correlation to avoid breakage if the number or order of tabs changes.
- The auto-activation side effect in `SidebarOrgScope` runs a full `window.location.reload()` after setting the active org; you might want to guard against unnecessary reloads when the active org is already correct or handle failures from `setActive` more explicitly (e.g., clearing the sessionStorage guard) to avoid leaving users stuck in an unexpected state.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces auto-activation of a single organization on Clerk session start to prevent rendering an empty dashboard, and fixes Astro static CSS interpolation in code-tabs.astro by using :nth-of-type selectors. Feedback was provided on the useEffect in app-shell.tsx regarding an unstable dependency array (organizations) and potential infinite reload loops or crashes if sessionStorage is blocked (e.g., in private browsing). A refactored version using a stable primitive dependency and a try-catch block was suggested.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +282 to +290
useEffect(() => {
if (!isLoaded || !setActive || activeOrg || organizations.length !== 1) return;
const GUARD = "agentstate:auto-activated-org";
if (sessionStorage.getItem(GUARD)) return;
sessionStorage.setItem(GUARD, "1");
void setActive({ organization: organizations[0].id }).then(() => {
window.location.reload();
});
}, [isLoaded, setActive, activeOrg, organizations]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There are two issues with this useEffect implementation:

  1. Unstable Dependency Array / Performance: The organizations array returned by useOrganizationsList() is computed dynamically on every render (via .map() and .filter()), meaning its reference identity changes constantly. This causes the useEffect to execute on every single render of the SidebarOrgScope component.
  2. Robustness / Potential Infinite Loop: Accessing sessionStorage can throw a SecurityError or DOMException in environments where storage is disabled or blocked (e.g., strict private browsing modes). If it throws, the app shell will crash on mount. Furthermore, if we simply catch the error and proceed with setActive and window.location.reload(), the page will reload and run the effect again, leading to an infinite reload loop.

Recommendation:

  • Extract a stable primitive dependency like singleOrgId (which is a string or null) to prevent unnecessary effect runs.
  • Wrap the sessionStorage operations in a try-catch block, and if it throws, return early to safely prevent both crashes and infinite reload loops.
  const singleOrgId = organizations.length === 1 ? organizations[0].id : null;

  useEffect(() => {
    if (!isLoaded || !setActive || activeOrg || !singleOrgId) return;
    const GUARD = "agentstate:auto-activated-org";
    try {
      if (sessionStorage.getItem(GUARD)) return;
      sessionStorage.setItem(GUARD, "1");
    } catch {
      // If sessionStorage is unavailable (e.g., private mode or blocked cookies),
      // do not auto-activate to prevent an infinite reload loop.
      return;
    }
    void setActive({ organization: singleOrgId }).then(() => {
      window.location.reload();
    });
  }, [isLoaded, setActive, activeOrg, singleOrgId]);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/dashboard/src/components/app-shell.tsx`:
- Around line 282-290: Update the auto-activation useEffect around setActive so
it only proceeds when activeOrg is explicitly null, not undefined, preserving
the loading-state distinction. Handle setActive failures with a catch, clear the
"agentstate:auto-activated-org" sessionStorage guard on rejection, and retain
the existing reload behavior on success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fc57b97e-e41d-44ee-a5f0-dba79f078c2d

📥 Commits

Reviewing files that changed from the base of the PR and between 5ff291a and 8948e6c.

📒 Files selected for processing (2)
  • packages/dashboard/src/components/app-shell.tsx
  • packages/dashboard/src/components/home/code-tabs.astro

Comment thread packages/dashboard/src/components/app-shell.tsx Outdated
Harden the sole-org auto-activation effect per CodeRabbit/Gemini review:

- Gate on useOrganization().isLoaded and check activeOrg === null via the
  loaded flag, so the effect no longer fires during the `undefined` loading
  window (fixes the race where the org list loads before the active org and
  triggers a spurious reload).
- Depend on a derived stable `soleOrgId` primitive instead of the
  `organizations` array whose reference changes every render.
- Wrap sessionStorage access in try/catch (throws in private mode) and add a
  .catch() to setActive that clears the guard and skips the reload on failure.

Co-Authored-By: Duyet Le <me@duyet.net>
Co-Authored-By: duyetbot <bot@duyet.net>
@duyet
duyet merged commit 9a12ed2 into main Jul 17, 2026
6 checks passed
@duyet
duyet deleted the claude/fix-landing-codeblock-and-org-scope branch July 17, 2026 16:17
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