Skip to content

feat: add framework-react-lynx-web (experimental)#461

Open
fi3ework wants to merge 8 commits intomainfrom
feat/framework-react-lynx-web
Open

feat: add framework-react-lynx-web (experimental)#461
fi3ework wants to merge 8 commits intomainfrom
feat/framework-react-lynx-web

Conversation

@fi3ework
Copy link
Copy Markdown
Member

@fi3ework fi3ework commented Apr 4, 2026

Summary

Google Chrome 2026-04-04 5 29 04 PM
  • Add storybook-react-lynx-web-rsbuild framework package for developing ReactLynx Web components in Storybook
  • JS HMR with state preservation via requireModuleAsync polyfill injection into Lynx bundles
  • CSS hot reload via SSE relay (rspeedy stdout monitoring → browser <lynx-view> URL refresh)
  • Storybook controls update via <lynx-view>.updateGlobalProps() without component state loss
  • Custom renderToCanvas to preserve <lynx-view> DOM across args changes
  • Production build support with rspeedy build + static bundle/asset copying
  • Include react-lynx-web sandbox with Button component for testing

Architecture

Storybook (rsbuild) ←→ preset.ts middleware ←→ rspeedy dev server
     ↓                       ↓                        ↓
  preview.ts            proxy bundles            Lynx HMR runtime
  (SSE client,          (inject polyfill)        (WebSocket → HMR)
   renderToCanvas,
   updateGlobalProps)

Test plan

  • pnpm storybook in sandboxes/react-lynx-web renders Button story
  • Modify Button.tsx → JS HMR preserves counter state
  • Modify Button.css → CSS reload updates styles
  • Change Storybook controls (label/primary) → component updates without state loss
  • pnpm build:storybook in sandbox produces working static build
  • pnpm build passes
  • pnpm lint passes
  • pnpm check passes

Add a new Storybook framework package `storybook-react-lynx-web-rsbuild`
that enables developing ReactLynx Web components in Storybook with full
HMR support (JS state-preserving HMR + CSS hot reload).

Key features:
- Proxy rspeedy dev server for Lynx bundle serving with HMR polyfill injection
- SSE-based CSS rebuild detection for shadow DOM style reload
- `updateGlobalProps()` bridge for Storybook controls without state loss
- Custom `renderToCanvas` to preserve `<lynx-view>` across args changes
- Production build support with static bundle copying

Includes a `react-lynx-web` sandbox with a Button component for testing.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b88010f70

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fi3ework added 3 commits April 4, 2026 17:41
- Add shell: true on Windows for execFileSync/spawn('npx') calls
- Fix Button import extension from .jsx to .tsx
- Guard HMR EventSource behind import.meta.webpackHot check
Replace import.meta.webpackHot check (TS2339) with
process.env.NODE_ENV !== 'production', which Rsbuild replaces
at build time for proper dead-code elimination.
@fi3ework fi3ework changed the title feat: add framework-react-lynx-web feat: add framework-react-lynx-web (experimental) Apr 4, 2026
fi3ework added 2 commits April 8, 2026 14:35
The `.web.bundle` embeds asset references as relative paths like
`static/image/foo.png`. web-core hands those strings to the shadow DOM
unchanged, so the browser resolves them against `iframe.html` —
producing root-level `/static/image/foo.png` requests. Previously we
copied them under `/lynx-bundles/static/image/`, which 404ed once
`storybook-static/` was deployed behind a plain static server.

- Build: `output.copy` now writes `static/**` to the output root
  (alongside storybook's own `static/{js,css,wasm}`, which don't
  overlap with lynx's `static/{image,font,svg}`).
- Dev: proxy `/static/{image,font,svg}` to rspeedy so the same
  resolution works through the in-process dev server.

Also splits `preview.ts` into a sync render entry and an async
`preview-runtime.ts` side-effect entry. `@lynx-js/web-core` uses
top-level await (WASM init), which promotes the importing module to
async — and Storybook's `composeConfigs` reads `render` synchronously
via `__webpack_require__`, yielding a Promise whose `.render` is
undefined. The framework's render then loses to the web-components
default and the story renders as "component annotation is missing".
Keeping the render export in a sync module preserves the fields.
@fi3ework fi3ework force-pushed the feat/framework-react-lynx-web branch from 954b588 to 1f3b650 Compare April 8, 2026 08:24
fi3ework added 2 commits April 8, 2026 21:32
Replace the per-component bridge-entry model with a single dispatcher
entry. Users write one `.storybook/lynx-preview.tsx` that registers
components via `createLynxStorybook({ components })`, and stories
reference them by name via `parameters.lynx.component`. The `entry:`
and `url:` forms remain as escape hatches.

- Add `src/runtime.ts` exporting `createLynxStorybook` (runs inside
  the Lynx bundle, not in Storybook's preview iframe)
- Wire `./runtime` subpath export (build-config + package.json exports
  + bundler.entries)
- Preset auto-detects `.storybook/lynx-preview.*` and injects it as a
  synthetic `__storybook__` rspeedy entry alongside user entries
- Preview resolves `url → entry → component` with distinct error paths
- Migrate sandbox to dispatcher model, add Card component + story,
  make lynx.config.ts a proper app config with `source.entry`
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