Conversation
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, so the default bundle is unchanged. 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. Also unifies the toolbar-state writers through updateToolbarState / syncEnabledState with a canonical DEFAULT_TOOLBAR_STATE, adds a reset() API, and extracts makeIifePack / makeModulePack build factories.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
commit: |
Contributor
There was a problem hiding this comment.
3 issues found across 21 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- getToolbarState() now reads the live currentToolbarState() (falling back to loadToolbarState()), so it no longer returns null in demo builds where loadToolbarState is gated off. (flagged by cursor + cubic) - Scoped hit-testing folds isWithinScope into the grabbable search so an out-of-scope topmost element falls back to the next in-scope grabbable instead of nulling the hit. (cubic P2)
createGrabDemo set the scope container before calling init(), so a no-op init (already-initialized) would leave the scope singleton pointing at the demo container while returning a noop API. init() now applies the scope after its single-init guard (and clears it on cleanup), so a no-op init can never set scope. Scope is still applied before the renderer mounts, so the toolbar still anchors to the container on first paint. (flagged by cursor, cubic, and Vercel Agent)
Drop refactors that were scope creep beyond the demo feature: - Revert the toolbar-state writer unification (syncEnabledState + routing setEnabled/setToolbarState/the toolbar handler through it). Those were a DRY refactor of pre-existing code unrelated to demo mode. reset() now just clears the active selection / grabbed boxes instead of also rewriting the toolbar (the demo never changes toolbar state). - Revert the demo-specific shadow-host attribute. The demo reuses the normal data-react-grab host; the separate host only mattered if the auto-init library and the demo loaded on the same page, which the demo page doesn't do. Keeps the actual demo surface: the IS_DEMO build flag and its gates, the scope container, the react-grab/demo driver, and toolbar-persistence gating.
Contributor
There was a problem hiding this comment.
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
After gating persistence off in demo, setToolbarState based partial updates on loadToolbarState() (null in demo), dropping previously set fields. Read currentToolbarState() first (matching getToolbarState), falling back to storage. (flagged by cursor + cubic)
- Reuse the existing Position type for GrabDemoPoint instead of redefining it. - Drop the speculative cursor?:boolean option and the nullable cursorElement it forced (4 null guards removed); the demo always paints its cursor. - Inline the one-off resetAll helper into the reset() API member. - Remove low-information comments that just restated `if (IS_DEMO) return`; keep the substantive "why" comments (runtime-mode header, ignoreRealInput DCE, copy-content feedback, freeze-updates toolbar path). - Fix an inaccurate saveToolbarState comment (clipboard -> storage), drop a stale react-grab/demo entrypoint reference, and trim an init-internal detail from the public Options.container doc.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 79d921d. Configure here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
process.env.IS_DEMO) that compiles React Grab into a display-only showcase, plus areact-grab/demodriver (createGrabDemo) for scripting it with synthetic events.isTrusted) input is ignored, the clipboard andlocalStorageare never written, host animations / React updates / cursor /bodystyles are never frozen, and the overlay is click-through.IS_DEMOconstant that dead-code-eliminates from normal library builds — the default bundle's behavior and size are unchanged (verified: zero demo markers inindex.global.js).data-react-grab-demo) so it can coexist with a normal React Grab instance on the same page.How it works
process.env.IS_DEMOis a build-time define: library entries compile it to""(soif (IS_DEMO)branches are DCE'd), demo entries to"true".pnpm build:demoemits the extrademo.*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.createGrabDemothrows on a second concurrent instance and restores the container'sposition+ scope ondispose.Also in here
updateToolbarState/ a newsyncEnabledState, with a canonicalDEFAULT_TOOLBAR_STATE.reset()API (returns React Grab to a clean slate without disposing).makeIifePack/makeModulePackbuild factories replace four near-identical pack configs.Test plan
pnpm typecheck,pnpm lint,pnpm formathtmlpointer-events: auto, body styles untouched); overlay click-through; library + demo coexist with separate shadow hosts;reset()and the single-instance/dispose lifecycle behaveNote
Medium Risk
Touches core interaction paths (event listeners, hit-testing, freeze/copy) and init lifecycle; demo gating limits production impact, but scoped container behavior could affect future non-demo uses of
container.Overview
Introduces an opt-in demo build (
pnpm build:demo/process.env.IS_DEMO) that compiles React Grab as a display-only showcase: trusted user input is dropped, clipboard andlocalStorageare no-ops, host freezing (React updates, animations, pseudo-states, body styles, global cursor) is skipped, and the shadow overlay is click-through. Demo-only paths use a build-timeIS_DEMOconstant so normal library bundles should DCE those branches.Adds
init({ container })scope via a runtime singleton: hit-testing, drag selection, and toolbar viewport math are confined to the container; the toolbar can re-anchor on scroll in demo builds when scoped.Ships
createGrabDemoindemo.ts—mounts scoped React Grab, paints a synthetic cursor, and exposes choreography primitives (moveCursor,click,typeText,pressKey, etc.) that dispatch untrusted pointer/keyboard events. Also addsapi.reset()for clearing selection between showcase loops withoutdispose, and refactors Vite pack config intomakeIifePack/makeModulePackfactories with separate demo entry outputs.Reviewed by Cursor Bugbot for commit 79d921d. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by cubic
Adds an opt-in build-time demo mode that compiles React Grab into a display-only, container‑scoped showcase with a
react-grab/demodriver for scripted synthetic events. Normal builds are unchanged; demo-only code is gated byprocess.env.IS_DEMOand renders a click‑through overlay.New Features
process.env.IS_DEMO;pnpm build:demoemitsdemo.*bundles.localStorage, and never freezes host React updates/animations/cursor/body; overlay is click-through.react-grab/demodriver:createGrabDemo({ container })with scripted-event primitives (moveCursor,click,pulseCursor,pressKey,typeText,setInputValue,getInput,cancel,dispose, …); always paints an animated cursor.api.reset()to clear selection and grabbed boxes between runs.Bug Fixes
getToolbarState()now reads the livecurrentToolbarState()(fallbacks toloadToolbarState()), so it returns state in demo builds.setToolbarState()bases partial updates on the live state first, preserving prior fields when persistence is gated off in demo.elementsFromPointand drag selection respect scope.init(): it applies the container after the single-init guard and clears it on cleanup, preventing dangling scope from a no-op init while keeping first-paint toolbar anchoring.Written for commit 79d921d. Summary will update on new commits.