Skip to content

Add build-time demo mode for React Grab#472

Merged
aidenybai merged 16 commits into
mainfrom
demo-mode
Jul 2, 2026
Merged

Add build-time demo mode for React Grab#472
aidenybai merged 16 commits into
mainfrom
demo-mode

Conversation

@aidenybai

@aidenybai aidenybai commented Jun 14, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds an opt-in, build-time demo mode (process.env.IS_DEMO) that compiles React Grab into a display-only showcase, plus a react-grab/demo driver (createGrabDemo) for scripting it with synthetic events.
  • Demo mode never touches the host page: real (isTrusted) input is ignored, the clipboard and localStorage are never written, host animations / React updates / cursor / body styles are never frozen, and the overlay is click-through.
  • Every demo-only behavior is gated behind a build-time IS_DEMO constant that dead-code-eliminates from normal library builds — the default bundle's behavior and size are unchanged (verified: zero demo markers in index.global.js).
  • The demo is scoped to a container (hit-testing, toolbar viewport, scroll re-anchor) and mounts its own shadow host (data-react-grab-demo) so it can coexist with a normal React Grab instance on the same page.

How it works

  • process.env.IS_DEMO is a build-time define: library entries compile it to "" (so if (IS_DEMO) branches are DCE'd), demo entries to "true". pnpm build:demo emits the extra demo.* bundles (opt-in, so the published package stays lean).
  • createGrabDemo({ container }) mounts demo-mode React Grab scoped to the container, paints an animated cursor, and returns low-level synthetic-event drivers (moveCursor, pulseCursor, click, pressKey, typeText, getInput, cancel, dispose, …). The consumer scripts the showcase; the library doesn't bake in a sequence.
  • Single-instance by design: createGrabDemo throws on a second concurrent instance and restores the container's position + scope on dispose.

Also in here

  • Unified the four toolbar-state writers through updateToolbarState / a new syncEnabledState, with a canonical DEFAULT_TOOLBAR_STATE.
  • New additive reset() API (returns React Grab to a clean slate without disposing).
  • makeIifePack / makeModulePack build factories replace four near-identical pack configs.

Test plan

  • pnpm typecheck, pnpm lint, pnpm format
  • e2e: full functional suite (667), plus toolbar / keyboard / freeze-updates / selection green
  • Browser: demo grabs via synthetic events; real clicks/keys + Cmd/Ctrl+C ignored; host never frozen (html pointer-events: auto, body styles untouched); overlay click-through; library + demo coexist with separate shadow hosts; reset() and the single-instance/dispose lifecycle behave
  • CI

Note

Medium Risk
Large removal of a whole app can break CI, deploy, or docs if anything still references website-v2; the OpenStory define is a low-risk build fix.

Overview
Removes the entire apps/website-v2 Next.js app from the monorepo—the shadcn component gallery homepage, root layout/theme wiring, the /script.js route that served react-grab’s global bundle, and all local UI primitives.

Monorepo housekeeping drops @react-grab/website-v2 from the Changesets ignore list (the package no longer exists) and stops ignoring apps/website/public/script.js in the root .gitignore.

apps/openstory adds a Vite define for process.env.IS_DEMO so bundled react-grab source that reads it at module scope does not throw in the browser when process is undefined.

Reviewed by Cursor Bugbot for commit 957d8b8. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds a build‑time, display‑only demo mode for react-grab with a scriptable react-grab/demo driver, and ships the v2 website in apps/website with a homepage autoplay demo, Benchmarks, and a Paper theme. Demo builds are safe, container‑scoped, dead‑code‑eliminated from normal builds, and the old apps/website-v2 app is removed.

  • New Features

    • Demo build via process.env.IS_DEMO (pnpm build:demo emits demo.*); click‑through overlay; no clipboard/localStorage writes; container‑scoped hit‑testing/toolbar with scroll re‑anchoring; api.reset() and single‑instance lifecycle.
    • react-grab/demo driver: createGrabDemo({ container }) with moveCursor/click/drag/pressKey/typeText/cancel/dispose; cancellation‑aware steps; per‑frame cursor moves with cached rects; homepage autoplay loads react-grab/dist/demo.js on intersection.
    • Website v2 in apps/website: Paper theme, new homepage demo, Benchmarks leaderboard (responsive, keyboard‑accessible), updated OG cards; /script.js served via a route with CORS + revalidate headers; unused shadcn components/deps pruned; Changesets ignore list updated.
  • Bug Fixes

    • Centralized trusted‑input gating; demo hardening: ignore real input; let real Enter/copy pass through; guard clipboard writes; gate remaining real‑input listeners (discard‑edits prompt, panel keys, modifier state, icon rotation, prompt confirmation); trusted pointercancel can’t abort scripted drags; paused demos don’t fire pending keypresses; lazy demo bundle load errors logged.
    • Website/dev: changelog parser supports multi‑section versions; CLI report endpoint validates/ rejects null/bad payloads; open‑in‑editor URLs re‑encode file paths; OG image route caches fonts with a fallback and logs fallback use; privacy copy and analytics usage aligned; apps/openstory defines process.env.IS_DEMO; ThemeProvider wrapped to avoid next-themes build issues on Vercel.
    • Mobile/responsive: clamp demo overlays to card bounds; re‑anchor the scoped toolbar on container resize and re‑measure the glow when it appears; square the demo card on small screens; wrap footer links; guard dropdown ResizeObserver where unavailable.
    • /script.js: trace the react-grab bundle into the function and read it via a cwd‑relative path so Vercel output tracing includes dist/index.global.js (fixes deploy 500s).
    • Pin GrabDemo.displayName so the hover/selection label reads correctly in production builds; after reset(), resume the showcase instead of leaving it paused.

Written for commit 957d8b8. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-grab-storybook Ready Ready Preview, Comment Jul 2, 2026 6:12am
react-grab-website Ready Ready Preview, Comment Jul 2, 2026 6:12am

@pkg-pr-new

pkg-pr-new Bot commented Jun 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@react-grab/cli@472
npm i https://pkg.pr.new/grab@472
npm i https://pkg.pr.new/react-grab@472

commit: 957d8b8

Comment thread packages/react-grab/src/core/index.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 21 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/react-grab/src/demo.ts Outdated
Comment thread packages/react-grab/src/utils/get-element-at-position.ts Outdated
Comment thread packages/react-grab/src/components/toolbar/state.ts
Comment thread packages/react-grab/src/demo.ts Outdated
Comment thread packages/react-grab/src/demo.ts
Comment thread packages/react-grab/src/core/index.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 6 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/react-grab/src/core/index.tsx Outdated
Comment thread packages/react-grab/src/core/index.tsx
Comment thread packages/react-grab/src/components/toolbar/index.tsx
Comment thread packages/react-grab/src/utils/runtime-mode.ts
Comment thread apps/website-v2/package.json Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 9 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread apps/website-v2/package.json Outdated
Comment thread apps/website-v2/app/globals.css Outdated
Comment thread packages/react-grab/src/components/toolbar/index.tsx

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread packages/react-grab/src/components/frozen-glow.tsx
… can't disturb the showcase (discard-edits prompt, panel keys, modifier state, icon rotation, prompt confirmation)
Comment thread packages/react-grab/src/utils/create-anchored-dropdown.ts
…500ing on Vercel

readFile with a computed path is invisible to output tracing, so the deployed
function shipped without dist/index.global.js. Declare it via
outputFileTracingIncludes and read it from a plain cwd-relative path instead
of the require.resolve token dance.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8161ec0. Configure here.

Comment thread apps/website/utils/parse-changelog.ts

onMount(() => {
const resizeObserver = new ResizeObserver(handleViewportChange);
resizeObserver.observe(scopeContainer);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Scoped glow lacks ResizeObserver guard

Low Severity

Scoped FrozenGlow always constructs ResizeObserver on mount. Other scoped UI in this PR guards with typeof ResizeObserver !== "undefined", so container-scoped grab on engines without ResizeObserver can throw during mount instead of degrading.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8161ec0. Configure here.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/website/next.config.ts">

<violation number="1" location="apps/website/next.config.ts:8">
P1: The `outputFileTracingIncludes` for `/script.js` points to a file two directories above the Next.js app (`../../packages/react-grab/dist/index.global.js`), but the tracing root is still the `apps/website` project directory. Next.js excludes files outside the tracing root even when they are listed in `outputFileTracingIncludes`, so the bundle may still be missing from the deployed function and the route could 500. To fix this, widen `outputFileTracingRoot` to the monorepo root alongside the include.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

// The /script.js route streams the library bundle off disk with readFile,
// which output tracing can't follow — without this the file is missing from
// the deployed function and the route 500s on Vercel.
outputFileTracingIncludes: {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: The outputFileTracingIncludes for /script.js points to a file two directories above the Next.js app (../../packages/react-grab/dist/index.global.js), but the tracing root is still the apps/website project directory. Next.js excludes files outside the tracing root even when they are listed in outputFileTracingIncludes, so the bundle may still be missing from the deployed function and the route could 500. To fix this, widen outputFileTracingRoot to the monorepo root alongside the include.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/website/next.config.ts, line 8:

<comment>The `outputFileTracingIncludes` for `/script.js` points to a file two directories above the Next.js app (`../../packages/react-grab/dist/index.global.js`), but the tracing root is still the `apps/website` project directory. Next.js excludes files outside the tracing root even when they are listed in `outputFileTracingIncludes`, so the bundle may still be missing from the deployed function and the route could 500. To fix this, widen `outputFileTracingRoot` to the monorepo root alongside the include.</comment>

<file context>
@@ -2,6 +2,12 @@ import type { NextConfig } from "next";
+  // The /script.js route streams the library bundle off disk with readFile,
+  // which output tracing can't follow — without this the file is missing from
+  // the deployed function and the route 500s on Vercel.
+  outputFileTracingIncludes: {
+    "/script.js": ["../../packages/react-grab/dist/index.global.js"],
+  },
</file context>

…name in prod

The demo card's hover/selection label reads the nearest composite fiber's
name, which minification mangles to a single letter ("S.h1").
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