Skip to content

feat: unified storybook + PR preview pipeline#35

Merged
joshuapare merged 9 commits into
mainfrom
feat/unified-storybook
Mar 18, 2026
Merged

feat: unified storybook + PR preview pipeline#35
joshuapare merged 9 commits into
mainfrom
feat/unified-storybook

Conversation

@joshuapare
Copy link
Copy Markdown
Contributor

@joshuapare joshuapare commented Mar 18, 2026

Summary

  • Unified Storybook config at repo root merging stories from all three packages (base-ui, ai-ui, editors)
  • pnpm storybook now launches the full component catalog; per-package scripts still available
  • Production storybook deploys to storybook.omniview.dev on push to main
  • PR preview storybook deploys to storybook-preview.omniview.dev/pr-<number>/ with bot comment
  • Cleanup workflow removes previews on PR close + weekly cron safety net

Infrastructure

S3 + CloudFront infrastructure provisioned via tf-infra-registry (PR #1, merged). GitHub secrets/variables configured.

Test plan

  • Run pnpm storybook locally — verify all three packages render
  • Verify editor stories work (Monaco workers)
  • Verify theme/density/motion toolbar controls
  • Merge and verify storybook.omniview.dev deploys
  • Open a test PR touching a component — verify preview URL comment appears
  • Close the test PR — verify cleanup runs

Summary by CodeRabbit

  • New Features

    • Automatic Storybook previews for each pull request with posted preview links/comments.
    • Storybook UI now exposes theme, density, and motion controls for previews.
  • Bug Fixes

    • Eliminated noisy source-map warnings during Storybook builds.
  • Chores

    • Switched Storybook deployment to a static hosting + CDN workflow and added automated cleanup of PR previews.
    • Updated Storybook dev/build scripts and added accessibility/dev tooling and related dependency updates.

Root-level .storybook/ config needs these packages directly available,
not just hoisted from individual packages.
Pulls stories from base-ui, ai-ui, and editors into a single build.
Merges Vite configs: @omniview/base-ui alias for cross-package imports,
monaco-editor optimizations, and STORYBOOK_BASE env var for PR previews.
Superset of all package previews: theme/density/motion controls,
ThemeProvider decorator, base-ui CSS, and Monaco worker setup.
Editors stories now gain density/motion controls they previously lacked.
Root 'pnpm storybook' now launches all packages. Per-package scripts
available as storybook:base, storybook:ai, storybook:editors.
Replaces GitHub Pages deployment with S3 sync + CloudFront invalidation.
Deploys unified storybook (all packages) to storybook.omniview.dev.
Builds unified storybook with path prefix, deploys to S3, posts
preview URL as a comment on the PR. Skips forks, uses concurrency
groups to prevent race conditions.
Event-driven cleanup on PR close + weekly cron safety net.
Cron lists all pr-* prefixes, checks PR status via GitHub API,
deletes stale ones.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Switches Storybook production deployment from GitHub Pages to AWS S3 + CloudFront, adds PR preview deployment and automated cleanup workflows, and updates Storybook/Vite configuration, dependencies, and preview UI settings.

Changes

Cohort / File(s) Summary
Production Deployment Workflow
.github/workflows/deploy-storybook.yml
Replaced GitHub Pages deployment with an AWS-based flow: configure AWS creds, sync storybook-static/ to S3 (${{ vars.STORYBOOK_S3_BUCKET }}/main/) and create CloudFront invalidation; updated workflow name, triggers, permissions, and concurrency.
PR Preview Workflows
.github/workflows/pr-storybook-preview.yml, .github/workflows/pr-storybook-cleanup.yml
Added PR preview deployment: build Storybook with base /pr-<num>/, sync to S3 per-PR path, invalidate CloudFront, and post/update a PR comment. Added cleanup workflow to remove S3 prefixes for closed/stale PR previews and invalidate CloudFront; runs on PR close and on schedule.
Storybook Config (Vite + Preview)
.storybook/main.ts, .storybook/preview.tsx
Added Vite viteFinal hook and plugin to strip broken source maps, configure aliases (e.g., @omniview/base-ui), optimize monaco-editor handling, and apply env-driven base paths. Added Storybook preview with globals (Theme, Density, Motion), decorator wrapping stories, Monaco worker setup, and layout/controls params.
Package manifest & scripts
package.json
Updated Storybook scripts (dev and build), added targeted storybook scripts, upgraded/added Storybook-related devDependencies and React/typing deps to support Storybook v10+ and Vite integration.

Sequence Diagram(s)

sequenceDiagram
  participant GH as GitHub Actions
  participant Repo as Repository (actions)
  participant AWS as AWS (STS → S3/CloudFront)
  participant GitHubAPI as GitHub API
  rect rgba(135,206,250,0.5)
    GH->>Repo: checkout, install, build Storybook (base path set)
    GH->>AWS: assume role (configure-aws-credentials)
    GH->>AWS: aws s3 sync storybook-static/ -> s3://bucket/pr-<num>/ or /main/
    GH->>AWS: aws cloudfront create-invalidation --paths "/*" or "/pr-<num>/*"
  end
  rect rgba(144,238,144,0.5)
    GH->>GitHubAPI: post/update PR comment with preview URL (for PR previews)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through builds and CI delight,

Pushed storybooks to cloud so bright,
PR paths bloom, then quietly clear,
S3 cradles pages, CloudFront brings cheer,
A rabbit's nod—deployments now take flight.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: unified storybook + PR preview pipeline' directly and accurately summarizes the main changes: introducing a unified Storybook configuration at the repository root and adding PR preview deployment infrastructure.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/unified-storybook
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@github-actions
Copy link
Copy Markdown

Storybook Preview
https://storybook-preview.omniview.dev/pr-35/

Updated: ba742cc

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy-storybook.yml:
- Around line 6-9: The workflow path filter under the paths key is missing
package.json so changes to scripts/devDependencies can bypass Storybook deploy;
update the paths array used in the deploy-storybook.yml workflow to include the
root and package-level package.json (e.g., add 'package.json' and/or
'packages/**/package.json') so that any edits to package.json will trigger the
Storybook deploy; ensure the added entries align with existing patterns like
'packages/**' and '.storybook/**'.

In @.github/workflows/pr-storybook-cleanup.yml:
- Around line 71-75: The current check uses state=$(gh api
"repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || echo "unknown") and
then deletes if [ "$state" != "open" ], which causes deletions on transient
failures; change the logic to only remove the S3 preview when state is exactly
"closed" and skip/log for "unknown" or other states. Update the conditional
around the aws s3 rm call (referencing the state variable, the gh api lookup,
and the prefix/aws s3 rm invocation) so it performs deletion only when
state=="closed" and otherwise echoes a skip message without deleting.
- Around line 18-26: The cleanup-pr job runs on all pull_request events and
tries to assume AWS credentials for forks that lack secrets; add the same fork
guard used in deploy-preview by adding the conditional if:
github.event.pull_request.head.repo.full_name == github.repository to the job
definition so the job (the block that includes uses:
aws-actions/configure-aws-credentials@v4 and env: PR_NUMBER) only runs for
non-fork PRs and will not attempt to access secrets for forked repositories.

In @.github/workflows/pr-storybook-preview.yml:
- Around line 6-9: The workflow's paths filter omits the root package manifest
so changes to scripts or deps won't trigger the job; update the workflow's paths
array (the "paths:" list in the YAML) to include "package.json" (and optionally
"packages/**/package.json" if you want package-level manifest changes to also
trigger) so storybook preview builds run when package manifests change.
- Around line 58-67: The workflow currently uses
peter-evans/create-or-update-comment@v4 with a now-unsupported comment-tag,
causing new comments each run; update the job to first run
peter-evans/find-comment@v4 to locate the existing "storybook-preview" comment
(use the same body marker or search term) and pass the returned comment id into
create-or-update-comment@v4 via its comment-id input (keep issue-number: ${{
env.PR_NUMBER }} and the body content, and include _Updated: ${{ github.sha
}}_), so the existing PR comment is updated instead of creating duplicates.

In @.storybook/main.ts:
- Around line 49-53: The current merge of config.resolve.alias always treats
aliases as an object which breaks when Vite provides an array; update the logic
in .storybook/main.ts to detect Array.isArray(config.resolve.alias) and handle
both shapes: if it's an array, push or unshift a { find: '@omniview/base-ui',
replacement: path.resolve(__dirname, '../packages/base-ui/src/index.ts') } entry
(preserving intended order), and if it's an object (or undefined) merge by
spreading into config.resolve.alias as before; ensure you still set
config.resolve = config.resolve ?? {} and only mutate config.resolve.alias in
the matching branch so existing array entries keep their numeric order and
object entries keep their properties.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56ec5a46-9b9f-4884-9e2e-40ab42334759

📥 Commits

Reviewing files that changed from the base of the PR and between e5e7f6a and 21cb34c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .github/workflows/deploy-storybook.yml
  • .github/workflows/pr-storybook-cleanup.yml
  • .github/workflows/pr-storybook-preview.yml
  • .storybook/main.ts
  • .storybook/preview.tsx
  • package.json

Comment on lines +6 to +9
paths:
- 'packages/**'
- '.storybook/**'
- 'pnpm-lock.yaml'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Include package.json in the deploy triggers.

Production deploy now depends on the root Storybook scripts and devDependencies. Without package.json in this path filter, script-only changes there can merge to main without ever rebuilding or redeploying Storybook.

Suggested fix
     paths:
       - 'packages/**'
       - '.storybook/**'
+      - 'package.json'
       - 'pnpm-lock.yaml'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
paths:
- 'packages/**'
- '.storybook/**'
- 'pnpm-lock.yaml'
paths:
- 'packages/**'
- '.storybook/**'
- 'package.json'
- 'pnpm-lock.yaml'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/deploy-storybook.yml around lines 6 - 9, The workflow path
filter under the paths key is missing package.json so changes to
scripts/devDependencies can bypass Storybook deploy; update the paths array used
in the deploy-storybook.yml workflow to include the root and package-level
package.json (e.g., add 'package.json' and/or 'packages/**/package.json') so
that any edits to package.json will trigger the Storybook deploy; ensure the
added entries align with existing patterns like 'packages/**' and
'.storybook/**'.

Comment on lines +18 to +26
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-2
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find .github/workflows -name "*.yml" -o -name "*.yaml" | head -20

Repository: omniviewdev/ui

Length of output: 213


🏁 Script executed:

cat -n .github/workflows/pr-storybook-cleanup.yml

Repository: omniviewdev/ui

Length of output: 3061


🏁 Script executed:

cat -n .github/workflows/pr-storybook-preview.yml

Repository: omniviewdev/ui

Length of output: 2396


Mirror the fork guard from deploy-preview in cleanup-pr.

.github/workflows/pr-storybook-preview.yml already uses if: github.event.pull_request.head.repo.full_name == github.repository (line 23) to skip forks, since fork PRs do not receive repository secrets. The cleanup-pr job currently runs on all pull request events and will attempt AWS credential setup even though fork PRs cannot access secrets.AWS_ROLE_ARN, causing the job to fail unnecessarily.

Suggested fix
  cleanup-pr:
-   if: github.event_name == 'pull_request'
+   if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-2
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-2
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-storybook-cleanup.yml around lines 18 - 26, The
cleanup-pr job runs on all pull_request events and tries to assume AWS
credentials for forks that lack secrets; add the same fork guard used in
deploy-preview by adding the conditional if:
github.event.pull_request.head.repo.full_name == github.repository to the job
definition so the job (the block that includes uses:
aws-actions/configure-aws-credentials@v4 and env: PR_NUMBER) only runs for
non-fork PRs and will not attempt to access secrets for forked repositories.

Comment on lines +71 to +75
state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || echo "unknown")

if [ "$state" != "open" ]; then
echo "Cleaning up $prefix (PR #$pr_num is $state)"
aws s3 rm "s3://$BUCKET/$prefix" --recursive
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Do not delete previews when the PR-state lookup fails.

gh api ... || echo "unknown" currently falls into the deletion branch. A transient GitHub API/auth failure would therefore purge previews for still-open PRs. Only delete on an explicit closed state, and skip/log anything else.

Suggested fix
-            state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || echo "unknown")
-
-            if [ "$state" != "open" ]; then
-              echo "Cleaning up $prefix (PR #$pr_num is $state)"
-              aws s3 rm "s3://$BUCKET/$prefix" --recursive
-            else
-              echo "Keeping $prefix (PR #$pr_num is still open)"
-            fi
+            state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || true)
+
+            if [ "$state" = "open" ]; then
+              echo "Keeping $prefix (PR #$pr_num is still open)"
+            elif [ "$state" = "closed" ]; then
+              echo "Cleaning up $prefix (PR #$pr_num is closed)"
+              aws s3 rm "s3://$BUCKET/$prefix" --recursive
+            else
+              echo "Skipping $prefix (could not determine PR #$pr_num state)"
+            fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || echo "unknown")
if [ "$state" != "open" ]; then
echo "Cleaning up $prefix (PR #$pr_num is $state)"
aws s3 rm "s3://$BUCKET/$prefix" --recursive
state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null || true)
if [ "$state" = "open" ]; then
echo "Keeping $prefix (PR #$pr_num is still open)"
elif [ "$state" = "closed" ]; then
echo "Cleaning up $prefix (PR #$pr_num is closed)"
aws s3 rm "s3://$BUCKET/$prefix" --recursive
else
echo "Skipping $prefix (could not determine PR #$pr_num state)"
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-storybook-cleanup.yml around lines 71 - 75, The current
check uses state=$(gh api "repos/$REPO/pulls/$pr_num" --jq '.state' 2>/dev/null
|| echo "unknown") and then deletes if [ "$state" != "open" ], which causes
deletions on transient failures; change the logic to only remove the S3 preview
when state is exactly "closed" and skip/log for "unknown" or other states.
Update the conditional around the aws s3 rm call (referencing the state
variable, the gh api lookup, and the prefix/aws s3 rm invocation) so it performs
deletion only when state=="closed" and otherwise echoes a skip message without
deleting.

Comment on lines +6 to +9
paths:
- 'packages/**'
- '.storybook/**'
- 'pnpm-lock.yaml'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add package.json to the trigger paths.

This workflow now builds via the root pnpm build-storybook, and the Storybook CLI/addon versions plus scripts live in the root manifest. A script-only or dependency-only change in package.json will currently skip preview generation entirely.

Suggested fix
     paths:
       - 'packages/**'
       - '.storybook/**'
+      - 'package.json'
       - 'pnpm-lock.yaml'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
paths:
- 'packages/**'
- '.storybook/**'
- 'pnpm-lock.yaml'
paths:
- 'packages/**'
- '.storybook/**'
- 'package.json'
- 'pnpm-lock.yaml'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-storybook-preview.yml around lines 6 - 9, The
workflow's paths filter omits the root package manifest so changes to scripts or
deps won't trigger the job; update the workflow's paths array (the "paths:" list
in the YAML) to include "package.json" (and optionally
"packages/**/package.json" if you want package-level manifest changes to also
trigger) so storybook preview builds run when package manifests change.

Comment on lines +58 to +67
- name: Post or update PR comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ env.PR_NUMBER }}
body: |
**Storybook Preview**
https://storybook-preview.omniview.dev/pr-${{ env.PR_NUMBER }}/

_Updated: ${{ github.sha }}_
comment-tag: storybook-preview
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

peter-evans/create-or-update-comment v4 comment-tag parameter

💡 Result:

The peter-evans/create-or-update-comment GitHub Action in version 4 (v4) does not have a 'comment-tag' input parameter. The official action.yml and documentation list inputs such as token, repository, issue-number, comment-id, body, body-path, body-file (deprecated), edit-mode, append-separator, reactions, and reactions-edit-mode, but no 'comment-tag'. A feature request to add 'comment-tag' support for create-or-update functionality exists in issue #429 (opened Aug 2025), with a related commit pushed but the PR/issue still open as of Jan 2026, indicating it is not yet implemented in any released version, including v4. The latest release is v5.0.0 (Oct 2025). To update or find comments, use the companion peter-evans/find-comment action with parameters like body-includes or comment-author to locate the comment-id, then pass it to create-or-update-comment.

Citations:


🌐 Web query:

peter-evans/create-or-update-comment v4 comment-id update pattern

💡 Result:

To update an existing comment using peter-evans/create-or-update-comment@v4 with a known comment-id, use the comment-id input along with the new body (and optionally edit-mode and reactions). Example workflow step from official documentation and user examples: - name: Update comment uses: peter-evans/create-or-update-comment@v4 with: comment-id: 557858210 # Replace with actual comment ID body: | Edit: Some additional info reactions: eyes edit-mode: replace # Optional: 'replace' or 'append' (default 'append') Key points for v4 (behavior unchanged from v5, per official repo and issues): - comment-id: Required for update. The numeric ID of the comment (e.g., from previous creation step output ${{ steps.create.outputs.comment-id }} or peter-evans/find-comment). - Without comment-id, it creates a new comment using issue-number. - edit-mode: replace fully replaces the body; append adds to it. - Obtain comment-id typically via: 1. Output from prior create-or-update-comment step (give it an id: stepname). 2. peter-evans/find-comment@v3 action, searching by author/body. Example full pattern to find-or-create-update: - name: Find Comment uses: peter-evans/find-comment@v3 id: fc with: issue-number: ${{ github.event.pull_request.number }} body-includes: "unique marker text" - name: Create or update comment uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | Updated content edit-mode: replace If no comment found, comment-id is empty string; action creates new. Note: Current latest is v5 (Oct 2025), but v4 works identically for this. Requires permissions: { issues: write, pull-requests: write } or equivalent token.

Citations:


🌐 Web query:

peter-evans/find-comment v4 GitHub Action

💡 Result:

peter-evans/find-comment@v4 is a GitHub Action that finds a comment in an issue or pull request and outputs its details like ID, body, author, and creation date. Usage example: - name: Find Comment uses: peter-evans/find-comment@v4 id: fc with: issue-number: 1 body-includes: search string Key inputs: - token: GITHUB_TOKEN (default) - repository: github.repository (default) - issue-number: Required, number of issue/PR - comment-author: Filter by author - body-includes: String to search in body - body-regex: Regex to match body - direction: first or last (default: first) - nth: 0-indexed position (default: 0) Outputs (empty if not found): - comment-id - comment-node-id - comment-body - comment-author - comment-created-at Latest release: v4.0.0 on 2025-10-01. Runs on Node 24 (requires runner v2.327.1+ for self-hosted). MIT licensed. Repository last updated 2026-03-06.

Citations:


Use comment-id to update the preview comment; comment-tag is ignored here.

The create-or-update-comment@v4 action does not support comment-tag as an input parameter. Without comment-id, the action will create a new comment on each synchronize run instead of updating the existing one. Use peter-evans/find-comment@v4 to locate the existing comment and pass its ID to the action for proper updates.

Suggested fix
+      - name: Find existing preview comment
+        id: preview_comment
+        uses: peter-evans/find-comment@v4
+        with:
+          issue-number: ${{ env.PR_NUMBER }}
+          comment-author: github-actions[bot]
+          body-includes: '<!-- storybook-preview -->'
+
       - name: Post or update PR comment
         uses: peter-evans/create-or-update-comment@v4
         with:
+          comment-id: ${{ steps.preview_comment.outputs.comment-id }}
           issue-number: ${{ env.PR_NUMBER }}
           body: |
+            <!-- storybook-preview -->
             **Storybook Preview**
             https://storybook-preview.omniview.dev/pr-${{ env.PR_NUMBER }}/
 
             _Updated: ${{ github.sha }}_
-          comment-tag: storybook-preview
+          edit-mode: replace
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Post or update PR comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ env.PR_NUMBER }}
body: |
**Storybook Preview**
https://storybook-preview.omniview.dev/pr-${{ env.PR_NUMBER }}/
_Updated: ${{ github.sha }}_
comment-tag: storybook-preview
- name: Find existing preview comment
id: preview_comment
uses: peter-evans/find-comment@v4
with:
issue-number: ${{ env.PR_NUMBER }}
comment-author: github-actions[bot]
body-includes: '<!-- storybook-preview -->'
- name: Post or update PR comment
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.preview_comment.outputs.comment-id }}
issue-number: ${{ env.PR_NUMBER }}
body: |
<!-- storybook-preview -->
**Storybook Preview**
https://storybook-preview.omniview.dev/pr-${{ env.PR_NUMBER }}/
_Updated: ${{ github.sha }}_
edit-mode: replace
🧰 Tools
🪛 actionlint (1.7.11)

[error] 67-67: input "comment-tag" is not defined in action "peter-evans/create-or-update-comment@v4". available inputs are "append-separator", "body", "body-file", "body-path", "comment-id", "edit-mode", "issue-number", "reactions", "reactions-edit-mode", "repository", "token"

(action)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/pr-storybook-preview.yml around lines 58 - 67, The
workflow currently uses peter-evans/create-or-update-comment@v4 with a
now-unsupported comment-tag, causing new comments each run; update the job to
first run peter-evans/find-comment@v4 to locate the existing "storybook-preview"
comment (use the same body marker or search term) and pass the returned comment
id into create-or-update-comment@v4 via its comment-id input (keep issue-number:
${{ env.PR_NUMBER }} and the body content, and include _Updated: ${{ github.sha
}}_), so the existing PR comment is updated instead of creating duplicates.

Comment thread .storybook/main.ts
Comment on lines +49 to +53
config.resolve = config.resolve ?? {};
config.resolve.alias = {
...config.resolve.alias,
'@omniview/base-ui': path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Vite resolve.alias object array format documentation

💡 Result:

Vite's resolve.alias configuration supports two formats: an object (Record) or an array of objects. The array format is Array<{ find: string | RegExp, replacement: string }>. This is passed to @rollup/plugin-alias as its entries option. The order of entries matters, with earlier rules applied first. Use absolute paths for file system aliases. ### Object Format (Record) resolve: { alias: { utils: '../../../utils', 'batman-1.0.0': './joker-1.5.0' } } ### Array Format resolve: { alias: [ { find: 'utils', replacement: '../../../utils' }, { find: 'batman-1.0.0', replacement: './joker-1.5.0' } ] } When find is a RegExp, replacement supports patterns like $1: { find: /^(.*).js$/, replacement: '$1.alias' } For SSR with externalized dependencies, alias node_modules packages using npm: prefix (Yarn/pnpm).

Citations:


🏁 Script executed:

# Find and read the .storybook/main.ts file
find . -path "./.storybook/main.ts" -type f

Repository: omniviewdev/ui

Length of output: 78


🏁 Script executed:

# Get more context around lines 49-53 in .storybook/main.ts
if [ -f ".storybook/main.ts" ]; then
  echo "=== Full context of viteFinal function ==="
  cat -n ".storybook/main.ts" | head -80
fi

Repository: omniviewdev/ui

Length of output: 3122


🏁 Script executed:

# Search for other uses of resolve.alias in the codebase
rg "resolve\.alias" --type ts --type js -B 2 -A 2

Repository: omniviewdev/ui

Length of output: 40


🏁 Script executed:

# Check Storybook version and configuration
if [ -f "package.json" ]; then
  echo "=== Storybook/Vite versions ==="
  cat package.json | grep -E '"@storybook|vite"' | head -20
fi

Repository: omniviewdev/ui

Length of output: 248


🏁 Script executed:

# Check if there are any tests for the viteFinal hook
find . -path ./node_modules -prune -o -name "*.test.*" -o -name "*.spec.*" | grep -i storybook

Repository: omniviewdev/ui

Length of output: 40


🏁 Script executed:

# Search for how Storybook might configure resolve.alias in this project
rg "config\.resolve" --type ts --type js -B 3 -A 3 | head -50

Repository: omniviewdev/ui

Length of output: 40


Handle both Vite alias shapes before merging.

Vite allows resolve.alias to be either an object or an array of { find, replacement } entries, and array order is significant. Spreading config.resolve.alias into an object only works for the object form; if Storybook/Vite hands you an array here, the existing aliases are converted into numeric keys and resolution breaks. (Vite documentation)

Suggested fix
     // --- Alias: resolve `@omniview/base-ui` to source (from ai-ui config) ---
     config.resolve = config.resolve ?? {};
-    config.resolve.alias = {
-      ...config.resolve.alias,
-      '@omniview/base-ui': path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
-    };
+    if (Array.isArray(config.resolve.alias)) {
+      config.resolve.alias.unshift({
+        find: '@omniview/base-ui',
+        replacement: path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
+      });
+    } else {
+      config.resolve.alias = {
+        ...(config.resolve.alias ?? {}),
+        '@omniview/base-ui': path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
+      };
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
config.resolve = config.resolve ?? {};
config.resolve.alias = {
...config.resolve.alias,
'@omniview/base-ui': path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
};
config.resolve = config.resolve ?? {};
if (Array.isArray(config.resolve.alias)) {
config.resolve.alias.unshift({
find: '@omniview/base-ui',
replacement: path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
});
} else {
config.resolve.alias = {
...(config.resolve.alias ?? {}),
'@omniview/base-ui': path.resolve(__dirname, '../packages/base-ui/src/index.ts'),
};
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.storybook/main.ts around lines 49 - 53, The current merge of
config.resolve.alias always treats aliases as an object which breaks when Vite
provides an array; update the logic in .storybook/main.ts to detect
Array.isArray(config.resolve.alias) and handle both shapes: if it's an array,
push or unshift a { find: '@omniview/base-ui', replacement:
path.resolve(__dirname, '../packages/base-ui/src/index.ts') } entry (preserving
intended order), and if it's an object (or undefined) merge by spreading into
config.resolve.alias as before; ensure you still set config.resolve =
config.resolve ?? {} and only mutate config.resolve.alias in the matching branch
so existing array entries keep their numeric order and object entries keep their
properties.

The root .storybook/ config needs react, react-dom, and
@vitejs/plugin-react directly available. Without these, the
storybook build produces "React is not defined" errors.
@github-actions
Copy link
Copy Markdown

Storybook Preview
https://storybook-preview.omniview.dev/pr-35/

Updated: 955dbea

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Around line 51-53: package.json pins the dependency eslint-plugin-react-hooks
at ^5.2.0 which is incompatible with React 19.2 features; update the dependency
entry for "eslint-plugin-react-hooks" to a v6+ range (e.g. ^6.0.0 or latest
6.x), run the package manager to update the lockfile (npm/yarn/pnpm install) and
verify linting (run eslint) so the new rules for useEffectEvent and React
Compiler are enforced; ensure there are no breaking rule changes by addressing
any new lint errors reported by eslint.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e7a45cbc-c728-4f5c-939b-6c302332622e

📥 Commits

Reviewing files that changed from the base of the PR and between 21cb34c and b8c02d5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • package.json

Comment thread package.json
Comment on lines +51 to +53
"react": "^19.0.0",
"react-dom": "^19.0.0",
"storybook": "^10.2.15",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Manifest versions:"
jq -r '
  .devDependencies.react,
  .devDependencies["react-dom"],
  .devDependencies["eslint-plugin-react-hooks"]
' package.json

echo
echo "Resolved lockfile entries (if pnpm lock exists):"
LOCKFILE=$(fd '^pnpm-lock.yaml$' | head -n1 || true)
if [ -n "${LOCKFILE:-}" ]; then
  rg -n '^( {2})?(react|react-dom|eslint-plugin-react-hooks)@' "$LOCKFILE" -n
else
  echo "pnpm-lock.yaml not found"
fi

Repository: omniviewdev/ui

Length of output: 353


🌐 Web query:

eslint-plugin-react-hooks v6 React 19 compatibility release notes

💡 Result:

eslint-plugin-react-hooks v6 is fully compatible with React 19. It was released as part of the React 19.2 announcement on 2025-10-01, with the first official stable version being 6.1.0 (after an accidental 6.0.0 release was deprecated). The plugin now defaults to flat config support for ESLint 9+ in the 'recommended' preset (use 'recommended-legacy' for older configs). It includes core rules like rules-of-hooks and exhaustive-deps, plus opt-in React Compiler rules via 'recommended-latest'. Key changes in v6 (from CHANGELOG): - Breaking: Flat config is now default 'recommended'; legacy moved to 'recommended-legacy'. - Requires Node.js 18+. - Added support for additionalEffectHooks setting. - Handles React.useEffect in rules-of-hooks. - Added recommended-latest configs with React Compiler rules (e.g., purity, immutability, set-state-in-effect). React 19.2 blog explicitly recommends upgrading to eslint-plugin-react-hooks v6 (or latest) for compatibility with new features like useEffectEvent, which requires the updated linter to exclude effect events from dependency arrays. Current latest version is 7.0.1 (as of 2026-03-18), which builds on v6 with further refinements like slimmed presets and all compiler rules enabled by default in recommended-latest.

Citations:


Upgrade eslint-plugin-react-hooks to v6+ for React 19.2 compatibility

React 19.2 introduced new features (useEffectEvent, React Compiler rules) that require eslint-plugin-react-hooks v6 or later. Current manifest pins eslint-plugin-react-hooks@^5.2.0, which resolves to v5.2.0 in the lockfile while react and react-dom resolve to 19.2.4, causing the linter to miss validation rules for React 19.2+ features.

Proposed fix
-    "eslint-plugin-react-hooks": "^5.2.0",
+    "eslint-plugin-react-hooks": "^6.0.0",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 51 - 53, package.json pins the dependency
eslint-plugin-react-hooks at ^5.2.0 which is incompatible with React 19.2
features; update the dependency entry for "eslint-plugin-react-hooks" to a v6+
range (e.g. ^6.0.0 or latest 6.x), run the package manager to update the
lockfile (npm/yarn/pnpm install) and verify linting (run eslint) so the new
rules for useEffectEvent and React Compiler are enforced; ensure there are no
breaking rule changes by addressing any new lint errors reported by eslint.

- Add .npmrc with shamefully-hoist=true so root storybook can resolve
  all workspace package dependencies
- Explicitly inject @vitejs/plugin-react in viteFinal when missing
  (pnpm workspace hoisting can lose it from @storybook/react-vite)
- Add obsidian, carbon, void themes to toolbar in all storybook previews
  (root, base-ui, ai-ui, editors)
@github-actions
Copy link
Copy Markdown

Storybook Preview
https://storybook-preview.omniview.dev/pr-35/

Updated: e662359

@joshuapare joshuapare merged commit 8bcce58 into main Mar 18, 2026
1 of 2 checks passed
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 18, 2026

Merging this PR will degrade performance by 16.89%

❌ 2 regressed benchmarks
✅ 181 untouched benchmarks
⏩ 2 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime rows change 2.6 ms 3.2 ms -16.89%
WallTime mount with rows 6.3 ms 7.5 ms -16.15%

Comparing feat/unified-storybook (80f0edb) with main (e5e7f6a)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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