diff --git a/README.md b/README.md index 8bf2db83..a930cf43 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,40 @@ ![rozenite-banner](https://www.rozenite.dev/rozenite-banner.jpg) -### React Native DevTools Plugin Framework +### Plug-and-play DevTools panels for React Native and Lynx [![mit licence][license-badge]][license] [![npm downloads][npm-downloads-badge]][npm-downloads] [![Chat][chat-badge]][chat] [![PRs Welcome][prs-welcome-badge]][prs-welcome] -Rozenite brings plug-and-play panels to React Native DevTools. Install plugins, enable them, and start debugging in minutes — no extra windows, servers, or tabs. Works with Expo and bare React Native, across Metro and Re.Pack. +Rozenite brings plug-and-play panels to React Native DevTools. Install plugins, enable them, and start debugging in minutes — no extra windows, servers, or tabs. Works with Expo and bare React Native across Metro and Re.Pack, with React Native Web and [Lynx](https://lynxjs.org) support in the box. ![Plugin slideshow](https://rozenite.dev/plugin-slideshow.webp) ## Features - **Plug-and-Play Panels**: Install plugins and they auto-appear in DevTools — start debugging in minutes. -- **Unified DevTools Experience**: All panels live inside React Native DevTools — no extra windows, servers, or browser tabs. +- **Unified DevTools Experience**: Panels live inside DevTools by default — no extra windows, servers, or browser tabs. - **Real-Time Debugging**: Live updates for network requests, storage and performance as you use your app. - **Works With Your Setup**: Compatible with Expo and bare React Native projects. Supports Metro and Re.Pack based workflows. +- **Beyond React Native**: The same panels, plugins and CLI reach Lynx apps through rspeedy, and React Native Web apps through the browser extension. Both are experimental. - **Production-Safe Controls**: Plugins are automatically disabled in production builds — no plugin code ships to your users. - **Easy to Build Your Own**: When you need something custom, create your own panel with type-safe, isomorphic APIs and great DX. ## Compatibility -| Rozenite | Expo SDK | React Native | Re.Pack | -| -------- | -------- | ------------- | ------- | -| 1.x | 52+ | 0.76+ | 5.2+ | +**React Native** -Bare React Native projects are supported too — match your React Native version to the one shipped by the minimum supported Expo SDK above. See the [Compatibility](https://rozenite.dev/docs/compatibility) page for full details. +| Rozenite | Expo SDK | React Native | Re.Pack | +| ---------------- | -------- | ------------ | ------- | +| 1.13.0, >= 2.0.0 | 52+ | 0.76+ | 5.2+ | + +Bare React Native projects are supported too — match your React Native version to the one shipped by the minimum supported Expo SDK above. + +**Lynx** (experimental) + +| Rozenite | rspeedy | @lynx-js/react | +| -------- | ------- | -------------- | +| >= 2.4.0 | 0.16+ | 0.125+ | + +See the [Compatibility](https://rozenite.dev/docs/compatibility) page for full details. ## Documentation @@ -31,6 +42,7 @@ The documentation is available at [rozenite.dev](https://rozenite.dev). You can - [Quick Start](https://rozenite.dev/docs/getting-started) - [Plugin Directory](https://rozenite.dev/plugin-directory) +- [Rozenite for Lynx](https://rozenite.dev/docs/targets/rozenite-for-lynx) - [Plugin Development](https://rozenite.dev/docs/plugin-development/overview) ## Made with ❤️ at Callstack diff --git a/packages/cli/README.md b/packages/cli/README.md index 70e2edfc..23383f52 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,14 +1,14 @@ ![rozenite-banner](https://www.rozenite.dev/rozenite-banner.jpg) -### A command-line interface for creating and managing React Native DevTools plugins. +### A command-line interface for creating and managing Rozenite DevTools plugins. [![mit licence][license-badge]][license] [![npm downloads][npm-downloads-badge]][npm-downloads] [![Chat][chat-badge]][chat] [![PRs Welcome][prs-welcome-badge]][prs-welcome] -The Rozenite CLI is the primary tool for scaffolding, building, and developing React Native DevTools plugins. It provides an intuitive command-line experience with interactive prompts, automated project setup, and development workflows. +The Rozenite CLI is the primary tool for scaffolding, building, and developing Rozenite DevTools plugins. It provides an intuitive command-line experience with interactive prompts, automated project setup, and development workflows. ## Features -- **Plugin Generation**: Create new React Native DevTools plugins with a complete project structure +- **Plugin Generation**: Create new Rozenite DevTools plugins with a complete project structure - **Interactive Setup**: Guided prompts for plugin configuration and metadata - **Build System**: Compile plugins for different targets - **Development Server**: Hot-reload development environment with file watchers @@ -28,7 +28,7 @@ npm install rozenite ### Generate a New Plugin -Create a new React Native DevTools plugin: +Create a new Rozenite DevTools plugin: ```bash npx rozenite generate diff --git a/packages/cli/package.json b/packages/cli/package.json index 23ed5d7b..d719f3dd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "rozenite", "version": "2.4.0", - "description": "Command-line interface for creating and managing React Native DevTools plugins", + "description": "Command-line interface for creating and managing Rozenite DevTools plugins", "keywords": [ "cli", "devtools", diff --git a/packages/rhf-plugin/README.md b/packages/rhf-plugin/README.md index e4f0999b..709773de 100644 --- a/packages/rhf-plugin/README.md +++ b/packages/rhf-plugin/README.md @@ -58,7 +58,7 @@ If you omit `id`, the hook uses React’s `useId()` for a dev-only identifier. ## Web (React Native for Web) -When you use [Rozenite for Web](https://rozenite.dev/docs/rozenite-for-web) in development, this plugin loads in the browser like on native. It follows the same `control` wiring as in your React Native app, as long as `react-hook-form` is available in the bundle. +When you use [Rozenite for Web](https://rozenite.dev/docs/targets/rozenite-for-web) in development, this plugin loads in the browser like on native. It follows the same `control` wiring as in your React Native app, as long as `react-hook-form` is available in the bundle. ## Notes diff --git a/packages/storage-plugin/README.md b/packages/storage-plugin/README.md index eabbef55..b78bda55 100644 --- a/packages/storage-plugin/README.md +++ b/packages/storage-plugin/README.md @@ -93,7 +93,7 @@ createAsyncStorageAdapter({ ## Web (React Native for Web) -When you use [Rozenite for Web](https://rozenite.dev/docs/rozenite-for-web) in development, this plugin loads in the browser like on native. **AsyncStorage** and **Expo SecureStore** adapters work on web when the underlying libraries do. +When you use [Rozenite for Web](https://rozenite.dev/docs/targets/rozenite-for-web) in development, this plugin loads in the browser like on native. **AsyncStorage** and **Expo SecureStore** adapters work on web when the underlying libraries do. **MMKV** is not available in typical web bundles. In development on web, `createMMKVStorageAdapter` returns an adapter with **empty `storages`** (same behavior as production builds), so you can keep shared setup code without importing `react-native-mmkv` in the web bundle. diff --git a/packages/web/README.md b/packages/web/README.md index 07c96bbb..28b573b9 100644 --- a/packages/web/README.md +++ b/packages/web/README.md @@ -29,7 +29,7 @@ Install the browser extension from the [GitHub releases](https://github.com/call The documentation is available at [rozenite.dev](https://rozenite.dev). You can also use the following links to jump to specific topics: -- [Rozenite for Web](https://rozenite.dev/docs/rozenite-for-web) +- [Rozenite for Web](https://rozenite.dev/docs/targets/rozenite-for-web) - [Quick Start](https://rozenite.dev/docs/getting-started) - [Plugin Directory](https://rozenite.dev/plugin-directory) - [Plugin Development](https://rozenite.dev/docs/plugin-development/overview) diff --git a/website/landing/README.md b/website/landing/README.md index 1516a273..4e9a106c 100644 --- a/website/landing/README.md +++ b/website/landing/README.md @@ -43,30 +43,45 @@ Three rules are worth knowing before adding anything: - **Radius.** There are exactly two: `--rz-radius-control` for interactive elements and chips, `--rz-radius-surface` for panels, cards and code blocks. - **Colour blocks.** `Section` takes `tint="block"`, which remaps the token set - locally so its children need no dark-specific styling. The page spends that - once, on Rozenite for Agents. A second inverted section would read as a theme + locally so its children need no dark-specific styling. Nothing spends it + today. Spend it at most once: a second inverted section would read as a theme flip mid-scroll. +- **Tints alternate.** Every section is `subtle` or `default`, strictly + alternating down the page, and the `subtle` ones carry `bordered`. Inserting a + section means retinting its neighbours, not breaking the run. - **The hero owns the first screen.** It is `min-height: calc(100dvh - var(--rz-nav-height))`. If the host navigation changes height, retune that - token rather than the section. + token rather than the section. Everything down to the install command has to + fit above the fold, on a laptop and on a phone. Check both after changing + anything in it. ## Conventions the page holds to These came out of a design review and are easy to break by accident: -- **Eyebrows are rationed.** Only the three product surfaces carry one: official - plugins, Rozenite for Agents, Rozenite for Web. Not every section needs a - label above its headline. +- **Eyebrows are rationed.** Only the product surfaces carry one: official + plugins, Rozenite for Agents, Rozenite for Lynx, Rozenite for Web. Not every + section needs a label above its headline -- the standalone app is a way of + working rather than a surface, so it has none. `eyebrow` takes a `ReactNode` + so a surface can pair its label with a brand mark or a status chip on that + one line. That is its whole remit; it is not a second headline. - **Section headers stack.** Eyebrow, headline, body, in one column. Rozenite for Agents is the one exception: it pairs its header with the terminal sample on the same line, and takes `className` to drop its stacking margin. -- **No layout family repeats.** Hero and Rozenite for Web are the only - text-and-visual splits, and they are not adjacent. Everything else uses a - different composition. +- **No layout family repeats.** Rozenite for Web is the only text-and-visual + split. Everything else uses a different composition: the hero is a centred + stack, Lynx is the page's only ordered rail, and the standalone app is the + only centred header over a full-width figure. The hero and the standalone app + both centre, but they are the two ends of the page and hold nothing else in + common -- one is type over a mark, the other is a caption over a screenshot. - **Icons come from a library.** `@phosphor-icons/react` for UI glyphs, `simple-icons` for brand marks via `components/brand-mark`. Nothing is drawn by hand. Brand marks render in `currentColor`, never in the brand hex, so a logo cannot smuggle a second accent onto the page. + + The Lynx mark is the one exception: `simple-icons` has no Lynx entry, so its + paths are vendored into `components/brand-mark`, which carries the provenance + and the licensing reasoning. Vendor another mark only on the same terms. - **Grids have no empty cells.** The plugin grid shows `FEATURED_PLUGINS`, six of the fourteen, which divides evenly at three, two and one column. The rest are counted in a line of copy underneath rather than drawn. Keep the featured @@ -74,7 +89,7 @@ These came out of a design review and are easy to break by accident: ## Motion -Two effects, and adding a third needs an argument. +Three effects, and adding a fourth needs an argument. `components/reveal` is a fade and lift as a block enters the viewport, so a section's header lands before its content. It is built on CSS scroll-driven @@ -84,10 +99,13 @@ state, and the animation only attaches inside `prefers-reduced-motion: no-preference`. `components/connection-diagram` runs two dots along the link between its -endpoints. This is the one looping animation on the page, and it is there -because the subject is traffic and a still line cannot show a direction. Same -progressive-enhancement shape: without `prefers-reduced-motion: no-preference` -the dots simply sit on the line. +endpoints. It is there because the subject is traffic and a still line cannot +show a direction. Same progressive-enhancement shape: without +`prefers-reduced-motion: no-preference` the dots simply sit on the line. + +The hero's animated mark is the third. It is a canvas rather than CSS, so its +rules live in `sections/hero/hero.tsx`; like the other two it degrades to a +single static frame under `prefers-reduced-motion`. Everything else is hover, focus and `:active` feedback. @@ -103,3 +121,24 @@ ratio. `components/connection-diagram` is for the places where no screenshot is coming. It draws two endpoints and the link between them, which explains an architecture better than a picture of a panel would. Rozenite for Web uses it. + +Nothing uses `components/screenshot` right now -- the standalone app's capture +landed, which is what the slot is for. Keep it around for the next section that +is drafted before its screenshot exists. + +## Framing a real capture + +The page has one product capture, and how it is framed follows from what the +image already contains rather than from a house style. + +`standalone-rozenite.png` is a raw window grab: it has macOS traffic lights but +no border or shadow, and a dark app screen sitting flush on the page reads as a +hole rather than a window. So the section frames it, and that frame is the +page's only elevated surface. See `sections/standalone/standalone.module.css`. + +Never draw chrome the capture already has. If a future capture arrives without +traffic lights, that is a reason to recapture it, not to fake a title bar. + +`website/landing-rozenite.png` is the hero's old DevTools screenshot. Nothing +imports it since the hero became the animated mark, so it is emitted by no +build; it is kept only in case the hero ever wants a product shot back. diff --git a/website/landing/components/brand-mark/brand-mark.tsx b/website/landing/components/brand-mark/brand-mark.tsx index 64a659e4..f3548e2e 100644 --- a/website/landing/components/brand-mark/brand-mark.tsx +++ b/website/landing/components/brand-mark/brand-mark.tsx @@ -1,12 +1,39 @@ import { siGooglechrome, siReact } from 'simple-icons'; -export type BrandSlug = 'googlechrome' | 'react'; +export type BrandSlug = 'googlechrome' | 'react' | 'lynx'; -/** Real brand marks, straight from simple-icons. Never redrawn by hand. */ -const ICONS = { - googlechrome: siGooglechrome, - react: siReact, -} as const; +type BrandIcon = { + paths: readonly string[]; + /** simple-icons is uniformly 24x24; a vendored mark keeps its own box. */ + viewBox?: string; + /** Some marks are authored with even-odd holes and look solid without it. */ + fillRule?: 'evenodd'; +}; + +/** + * Real brand marks, straight from simple-icons. Never redrawn by hand. + * + * Lynx is the exception: it has no simple-icons entry, so the paths below are + * the mark from lynxjs.org's own header, vendored rather than hotlinked so the + * page pulls nothing from a third-party CDN. It is ByteDance/TikTok's mark, + * used nominatively to identify the Lynx project Rozenite integrates with. The + * Lynx repositories are Apache-2.0, whose section 6 grants no trademark rights, + * so this rests on nominative use rather than on the licence -- which is also + * why it renders in `currentColor` below. The mark identifies; it does not + * imply endorsement. + */ +const ICONS: Record = { + googlechrome: { paths: [siGooglechrome.path] }, + react: { paths: [siReact.path] }, + lynx: { + paths: [ + 'M7.56542 6.19594L3.90642 8.7164C3.50877 8.99031 3.23346 9.40191 3.13675 9.86708L2.77902 11.5878C2.76005 11.679 2.71799 11.7642 2.65665 11.8355L0.996306 14.0121C0.772761 14.2722 0.778152 14.9632 1.39044 15.4057C1.62523 15.6103 1.93915 16.0691 2.29622 16.591C3.05224 17.6959 4.00169 19.0836 4.80312 18.9394C5.9372 18.541 7.32544 18.4135 8.39128 18.9394C10.4632 20.7282 9.95449 22.3775 9.22514 24.7421C8.93165 25.6936 8.60243 26.7609 8.39128 27.9997C9.38643 24.3777 11.6242 20.1711 15.6592 18.7437C14.9322 18.1498 13.4486 17.5981 12.1357 17.4653C12.1357 17.4653 16.1691 14.0121 21.1439 12.4254C17.671 4.16205 11.9386 0.213095 11.9386 0.213095C11.6465 -0.148197 11.0566 -0.0296711 10.9349 0.414774C10.8371 1.72112 10.675 2.60942 10.4074 3.44676L8.39128 1.12029C8.18068 0.866399 7.75965 1.013 7.76176 1.33949C8.10312 3.23719 8.05521 4.30239 7.56542 6.19594ZM8.9846 6.02248L8.99663 6.02171C9.02298 6.02002 9.0489 6.01659 9.07424 6.01153L8.9846 6.02248ZM11.7123 1.7617C13.094 4.1491 13.7199 5.5054 13.9322 8.03659C12.4625 7.2017 11.8221 6.98923 10.7413 6.99451C11.3718 5.0773 11.5644 3.92284 11.7123 1.7617Z', + 'M20.5926 19.4929C14.9649 20.7806 11.7681 22.7198 9.32324 28.0001C13.712 20.6367 26.9976 21.9536 26.9976 21.9536C26.7494 20.7508 24.1079 18.4571 22.3706 17.0503C22.3706 17.0503 23.7722 15.3272 26.8706 14.455C26.8706 14.455 20.9135 14.8002 17.4455 16.6727C18.569 17.2656 20.0081 18.2663 20.5926 19.4929Z', + ], + viewBox: '0 0 27 28', + fillRule: 'evenodd', + }, +}; type BrandMarkProps = { slug: BrandSlug; @@ -19,16 +46,24 @@ type BrandMarkProps = { * Rendered in `currentColor` rather than the brand hex, so a mark sits in the * page's palette instead of dragging a second accent into it. */ -export const BrandMark = ({ slug, label, size = 26, className }: BrandMarkProps) => ( - - - -); +export const BrandMark = ({ slug, label, size = 26, className }: BrandMarkProps) => { + const icon = ICONS[slug]; + + return ( + + {icon.paths.map((path) => ( + + ))} + + ); +}; diff --git a/website/landing/components/rozenite-loader/rozenite-loader.tsx b/website/landing/components/rozenite-loader/rozenite-loader.tsx new file mode 100644 index 00000000..e4b05eb4 --- /dev/null +++ b/website/landing/components/rozenite-loader/rozenite-loader.tsx @@ -0,0 +1,21 @@ +/* + * The one file in `landing/` that reaches outside the website. + * + * `@rozenite/ui` exports only its barrel, and declares no `sideEffects: false`, + * so importing the loader through the package entry point would pull Base UI, + * TanStack Table, Virtuoso and lucide-react into a static docs bundle for the + * sake of one canvas. The loader itself depends on nothing but React. + * + * The website is never published -- it is only ever bundled -- so it can import + * the workspace source directly and skip the package's export map entirely. + * That keeps `@rozenite/ui` unchanged and keeps a single source of truth for + * the animation. + * + * The deep path lives here and nowhere else: sections import from this module, + * so if the package ever grows a `./rozenite-loader` subpath export, this file + * is the only one that changes. + */ +export { + RozeniteLoader, + type RozeniteLoaderProps, +} from '../../../../packages/ui/src/rozenite-loader/rozenite-loader'; diff --git a/website/landing/components/section/section.tsx b/website/landing/components/section/section.tsx index ba8a0d3e..5a90aa4f 100644 --- a/website/landing/components/section/section.tsx +++ b/website/landing/components/section/section.tsx @@ -40,10 +40,12 @@ export const Section = ({ type SectionHeaderProps = { /** - * Small label above the headline. Rationed on purpose: only the three product - * surfaces (plugins, agents, web) carry one. + * Small label above the headline. Rationed on purpose: only the product + * surfaces (plugins, agents, Lynx, web) carry one. `ReactNode` rather than + * `string` so a surface can pair its label with a brand mark or a status + * chip on the same line -- not so it can grow into a second headline. */ - eyebrow?: string; + eyebrow?: ReactNode; title: ReactNode; body?: ReactNode; /** For sections that place the header inside a layout of their own. */ diff --git a/website/landing/components/use-token-color/use-token-color.tsx b/website/landing/components/use-token-color/use-token-color.tsx new file mode 100644 index 00000000..ecbc5681 --- /dev/null +++ b/website/landing/components/use-token-color/use-token-color.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState, type RefObject } from 'react'; + +/** + * Resolves a `--rz-*` token to the concrete colour it currently holds. + * + * Canvas cannot read CSS: `ctx.fillStyle = 'var(--rz-accent)'` is invalid and + * is silently ignored, leaving whatever was set before it. So anything drawing + * to a canvas has to be handed a real colour, and be handed a new one when the + * theme flips. + * + * Returns `fallback` until the first measurement, which matters during SSG -- + * Rspress renders these components on the server, where there is no computed + * style to read. + */ +export const useTokenColor = ( + ref: RefObject, + token: string, + fallback: string, +): string => { + const [color, setColor] = useState(fallback); + + useEffect(() => { + const element = ref.current; + + if (!element) { + return; + } + + const read = () => { + const value = getComputedStyle(element).getPropertyValue(token).trim(); + + if (value) { + setColor(value); + } + }; + + read(); + + // Both are in play: Rspress toggles `html.dark`, and the token file also + // honours `[data-theme]`. + const observer = new MutationObserver(read); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class', 'data-theme'], + }); + + return () => observer.disconnect(); + }, [ref, token]); + + return color; +}; diff --git a/website/landing/landing-page.tsx b/website/landing/landing-page.tsx index 9dca73f9..9e3164f6 100644 --- a/website/landing/landing-page.tsx +++ b/website/landing/landing-page.tsx @@ -9,7 +9,9 @@ import { Agents } from './sections/agents/agents'; import { BuildYourOwn } from './sections/build-your-own/build-your-own'; import { Closing } from './sections/closing/closing'; import { Hero } from './sections/hero/hero'; +import { Lynx } from './sections/lynx/lynx'; import { Plugins } from './sections/plugins/plugins'; +import { Standalone } from './sections/standalone/standalone'; import { Web } from './sections/web/web'; type LandingPageProps = { @@ -26,8 +28,10 @@ export const LandingPage = ({ footer }: LandingPageProps) => ( + + {footer} diff --git a/website/landing/sections/build-your-own/build-your-own.tsx b/website/landing/sections/build-your-own/build-your-own.tsx index c13f4478..e1c9b7a2 100644 --- a/website/landing/sections/build-your-own/build-your-own.tsx +++ b/website/landing/sections/build-your-own/build-your-own.tsx @@ -31,7 +31,7 @@ client.onMessage('cart-updated', (cart) => { client.send('clear-cart', { reason: 'devtools' });`; export const BuildYourOwn = () => ( -
+
( -
-
+ ); +}; diff --git a/website/landing/sections/lynx/lynx.module.css b/website/landing/sections/lynx/lynx.module.css new file mode 100644 index 00000000..1bb27770 --- /dev/null +++ b/website/landing/sections/lynx/lynx.module.css @@ -0,0 +1,126 @@ +/* + * A numbered rail. Every other section on the page composes as a grid of peers + * or a text-and-visual split; this one is the only ordered sequence, which is + * what the Lynx setup actually is -- and it puts the easily-missed first step + * where it cannot be skimmed past. + */ + +.label { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.mark { + /* Inherits the eyebrow's accent through `currentColor`. */ + flex-shrink: 0; +} + +.status { + border: 1px solid var(--rz-border-strong); + border-radius: var(--rz-radius-control); + padding: 1px 6px; + color: var(--rz-text-tertiary); + --rz-ls: 0.06em; +} + +.steps { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + margin: 0; + padding: 0; + list-style: none; + border-top: 1px solid var(--rz-border); +} + +.step { + display: flex; + flex-direction: column; + gap: 10px; + padding: 28px 32px 32px 0; +} + +/* The rail reads left to right, so the divider belongs between the steps. */ +.step + .step { + padding-left: 32px; + border-left: 1px solid var(--rz-border); +} + +.stepIndex { + font-family: var(--rz-font-mono); + font-size: 0.75rem; + --rz-ls: 0.07em; + color: var(--rz-accent); +} + +.stepTitle { + margin: 0; + font-family: var(--rz-font-display); + font-size: 1.0625rem; + font-weight: 500; + --rz-ls: -0.015em; + --rz-lh: 1.3; +} + +.stepBody { + margin: 0; + font-size: 0.9375rem; + --rz-lh: 1.55; + color: var(--rz-text-secondary); +} + +.bottom { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 20px 40px; + padding-top: 32px; + border-top: 1px solid var(--rz-border); +} + +.setup { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 12px; +} + +.setup > :first-child { + width: min(340px, 100%); +} + +.caveat { + flex: 1 1 30ch; + margin: 0; + font-size: 0.875rem; + --rz-lh: 1.55; + color: var(--rz-text-tertiary); +} + +@media (max-width: 900px) { + .steps { + grid-template-columns: minmax(0, 1fr); + } + + .step { + padding: 24px 0; + } + + .step + .step { + padding-left: 0; + border-left: none; + border-top: 1px solid var(--rz-border); + } +} + +@media (max-width: 560px) { + .setup { + width: 100%; + } + + .setup > * { + width: 100%; + justify-content: space-between; + } +} diff --git a/website/landing/sections/lynx/lynx.tsx b/website/landing/sections/lynx/lynx.tsx new file mode 100644 index 00000000..1c57f801 --- /dev/null +++ b/website/landing/sections/lynx/lynx.tsx @@ -0,0 +1,72 @@ +import { ActionButton } from '../../components/action-button/action-button'; +import { BrandMark } from '../../components/brand-mark/brand-mark'; +import { CommandLine } from '../../components/command-line/command-line'; +import { Reveal } from '../../components/reveal/reveal'; +import { Section, SectionHeader } from '../../components/section/section'; +import styles from './lynx.module.css'; + +type Step = { + title: string; + body: string; +}; + +/** + * Ordered, because the order is the point: the DevTool switch is off by + * default and nothing else in the list works until it is on. + */ +const STEPS: Step[] = [ + { + title: 'Turn on Lynx DevTool', + body: 'Lynx ships its DevTool component switched off. Flip it on in the app, then relaunch — nothing is discoverable until you do.', + }, + { + title: 'Add the plugin', + body: 'One entry in lynx.config.ts. The plugin injects the device runtime for you, in development only, so there is nothing to import in your app.', + }, + { + title: 'Open the printed URL', + body: 'The dev server prints a DevTools URL per card. Plugins resolve from your package.json, exactly as they do on Metro.', + }, +]; + +export const Lynx = () => ( +
+ + + Rozenite for Lynx + Experimental + + } + title="Same panels, same CLI, now on Lynx" + body="Rozenite discovers Lynx apps over DebugRouter and bridges them to the Chrome DevTools Protocol, so the DevTools frontend React Native uses connects to a Lynx card unmodified." + /> + + +
    + {STEPS.map((step, index) => ( +
  1. + {String(index + 1).padStart(2, '0')} +

    {step.title}

    +

    {step.body}

    +
  2. + ))} +
+ +
+
+ + + Lynx setup guide + +
+ +

+ Plugins that move state work today — controls, feature flags, React Hook Form and TanStack + Query. Ones built on React Native APIs do not yet. +

+
+
+
+); diff --git a/website/landing/sections/standalone/standalone.module.css b/website/landing/sections/standalone/standalone.module.css new file mode 100644 index 00000000..43171d5e --- /dev/null +++ b/website/landing/sections/standalone/standalone.module.css @@ -0,0 +1,101 @@ +/* + * Centred header over one full-width figure. Every other section either splits + * text and visual side by side or lays peers out in a grid, so this is the one + * place the page centres -- fitting for a section about a single window. + */ + +.header { + max-width: 62ch; + margin-inline: auto; + text-align: center; +} + +.title { + margin: 0; + font-family: var(--rz-font-display); + font-weight: 500; + font-size: clamp(2rem, 1.4rem + 2.4vw, 3rem); + --rz-lh: 1.08; + --rz-ls: -0.028em; + text-wrap: balance; +} + +.body { + margin: 20px auto 0; + max-width: 58ch; + font-size: clamp(1rem, 0.97rem + 0.16vw, 1.0625rem); + --rz-lh: 1.6; + color: var(--rz-text-secondary); +} + +.code { + font-family: var(--rz-font-mono); + font-size: 0.9em; + color: var(--rz-text); +} + +.actions { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 12px; + margin-top: 32px; +} + +.command { + width: min(320px, 100%); +} + +/* + * The capture is of the real window, so it already carries macOS traffic + * lights. It has no border or shadow of its own, though, and a dark app screen + * sitting flush on the page edge reads as a hole rather than a window -- hence + * the hairline and the lift below. Nothing here draws chrome the capture + * already has. + * + * `--rz-radius-surface` because this is a frame, and `overflow: hidden` so the + * capture's own square corners take the frame's radius. + */ +.frame { + /* 80% of the measure. The capture is a window, not a hero shot -- at full + bleed it competed with the hero's, and holding it back also keeps it + nearer its native 1280px so it stays crisp on a 2x display. */ + width: 80%; + margin: 56px auto 0; + border: 1px solid var(--rz-border); + border-radius: var(--rz-radius-surface); + overflow: hidden; + /* The page has no shadow token; this is its only elevated surface. Kept + soft and low so it reads as a window resting on the page, not a card + floating above it. */ + box-shadow: + 0 1px 2px rgba(23, 22, 26, 0.04), + 0 12px 32px -12px rgba(23, 22, 26, 0.18); + background-color: var(--rz-surface); +} + +:global(html.dark) .frame, +:global([data-theme='dark']) .frame { + box-shadow: + 0 1px 2px rgba(0, 0, 0, 0.3), + 0 16px 40px -16px rgba(0, 0, 0, 0.55); +} + +.image { + display: block; + width: 100%; + height: auto; +} + +@media (max-width: 560px) { + .actions > * { + width: 100%; + justify-content: space-between; + } + + .frame { + width: 100%; + margin-top: 36px; + } +} diff --git a/website/landing/sections/standalone/standalone.tsx b/website/landing/sections/standalone/standalone.tsx new file mode 100644 index 00000000..7569818e --- /dev/null +++ b/website/landing/sections/standalone/standalone.tsx @@ -0,0 +1,44 @@ +import { ActionButton } from '../../components/action-button/action-button'; +import { CommandLine } from '../../components/command-line/command-line'; +import { Reveal } from '../../components/reveal/reveal'; +import { Section } from '../../components/section/section'; +import styles from './standalone.module.css'; + +/** + * No eyebrow. The rationed labels belong to the product surfaces; the + * standalone app is a way of working, not another target. + */ +export const Standalone = () => ( +
+ +
+

Rozenite on its own

+

+ If Rozenite panels are where you spend your debugging time, run just those.{' '} + rozenite open connects straight to the device, so + your panels stay put across app reloads — and they work the same way whichever target you + are debugging. +

+ +
+ + + Standalone app guide + +
+
+
+ + +
+ The Rozenite standalone app in its own window, showing the File System panel browsing an iOS app's Library directory with a PNG previewed. +
+
+
+); diff --git a/website/landing/sections/web/web.tsx b/website/landing/sections/web/web.tsx index e3ff0a95..964523fd 100644 --- a/website/landing/sections/web/web.tsx +++ b/website/landing/sections/web/web.tsx @@ -32,7 +32,7 @@ const POINTS: Point[] = [ ]; export const Web = () => ( -
+
(
- + Web setup guide
diff --git a/website/rspress.config.ts b/website/rspress.config.ts index fe4401cc..c0ab029e 100644 --- a/website/rspress.config.ts +++ b/website/rspress.config.ts @@ -8,7 +8,7 @@ export default withCallstackPreset( context: __dirname, docs: { description: - 'Build powerful debugging tools and custom panels for React Native DevTools with type-safe, isomorphic communication', + 'Plug-and-play DevTools panels for React Native and Lynx, with type-safe plugin APIs and runtime tools for agents.', icon: '/logo.svg', logoDark: '/logo-dark.svg', logoLight: '/logo-light.svg', diff --git a/website/src/docs/_meta.json b/website/src/docs/_meta.json index 200bd02b..85dedbf3 100644 --- a/website/src/docs/_meta.json +++ b/website/src/docs/_meta.json @@ -3,9 +3,8 @@ { "type": "file", "name": "prior-art", "label": "Prior Art" }, { "type": "file", "name": "getting-started", "label": "Getting started" }, { "type": "file", "name": "compatibility", "label": "Compatibility" }, + { "type": "dir", "name": "targets", "label": "Targets" }, { "type": "dir", "name": "agent", "label": "Rozenite for Agents" }, - { "type": "file", "name": "rozenite-for-web", "label": "Rozenite for Web" }, - { "type": "file", "name": "rozenite-for-lynx", "label": "Rozenite for Lynx" }, { "type": "file", "name": "standalone-app", "label": "Standalone App" }, { "type": "dir", diff --git a/website/src/docs/agent/adding-tools-to-your-application.mdx b/website/src/docs/agent/adding-tools-to-your-application.mdx index c98d0039..88c0811c 100644 --- a/website/src/docs/agent/adding-tools-to-your-application.mdx +++ b/website/src/docs/agent/adding-tools-to-your-application.mdx @@ -2,7 +2,7 @@ import { PackageManagerTabs } from '@rspress/core/theme'; # Adding tools to your application -You can expose custom agent tools directly from your React Native app. These tools appear under the **app** domain so coding agents (e.g. CLI, Cursor, Codex) can discover and call them via Rozenite for Agents. +You can expose custom agent tools directly from your app. These tools appear under the **app** domain so coding agents (e.g. CLI, Cursor, Codex) can discover and call them via Rozenite for Agents. Use this when you want agents to perform app-specific actions—for example, triggering a debug flow, returning build metadata, or driving in-app behavior during automated testing. diff --git a/website/src/docs/agent/making-your-plugin-agent-enabled.mdx b/website/src/docs/agent/making-your-plugin-agent-enabled.mdx index bd41458c..82692d45 100644 --- a/website/src/docs/agent/making-your-plugin-agent-enabled.mdx +++ b/website/src/docs/agent/making-your-plugin-agent-enabled.mdx @@ -8,8 +8,8 @@ This page describes what you need to expose tools from your plugin package. ## Prerequisites -- You have a Rozenite plugin with a **React Native side** (code that runs inside the app). See [Plugin Development](/docs/plugin-development/overview) if you are new to plugins. -- Your plugin is already integrated so its React Native entry runs when the app is in development. +- You have a Rozenite plugin with an **app side** (code that runs inside the app, from its `react-native.ts` entry point). See [Plugin Development](/docs/plugin-development/overview) if you are new to plugins. +- Your plugin is already integrated so its app entry runs when the app is in development. ## Add the agent bridge dependency @@ -21,7 +21,7 @@ The bridge provides the same message protocol and tool lifecycle as in-app tools ## Use the plugin agent tool hook -From your plugin’s React Native code, call `useRozenitePluginAgentTool` with: +From your plugin’s app-side code, call `useRozenitePluginAgentTool` with: - **pluginId** – Your plugin’s public ID (e.g. `@rozenite/react-navigation-plugin`). This becomes the **domain** agents use to list and call your tools. - **tool** – An `AgentTool`: `name`, `description`, and `inputSchema` (JSON Schema–style). @@ -59,7 +59,7 @@ export function MyPluginAgentTools() { } ``` -Mount `MyPluginAgentTools` from your plugin’s React Native entry (e.g. next to your DevTools panel registration) so it runs when the app is connected. Agents will see a domain equal to `PLUGIN_ID` and a tool named `echo` (qualified as `@my-org/my-rozenite-plugin.echo`). +Mount `MyPluginAgentTools` from your plugin’s app entry (e.g. next to your DevTools panel registration) so it runs when the app is connected. Agents will see a domain equal to `PLUGIN_ID` and a tool named `echo` (qualified as `@my-org/my-rozenite-plugin.echo`). ### Example: plugin-specific tool @@ -111,7 +111,7 @@ The pattern used by Rozenite's official plugins is: 1. Put the **public tool contract** in a non-React module such as `src/shared/agent-tools.ts`. 2. Define each tool with `defineAgentToolContract(...)` from `@rozenite/agent-shared`. -3. Reuse those shared definitions both in your React Native registration hook and in `sdk.ts`. +3. Reuse those shared definitions both in your app-side registration hook and in `sdk.ts`. 4. In `sdk.ts`, call `defineAgentToolDescriptors(pluginId, toolDefinitions)` and export the resulting descriptor object. That split keeps one source of truth for: @@ -272,7 +272,7 @@ Rozenite derives a short, stable domain name from your `pluginId` alone (for exa ## Summary 1. Add **@rozenite/agent-bridge** to your plugin. -2. In your plugin’s React Native code, call **useRozenitePluginAgentTool** with a stable **pluginId**, a **tool** (name, description, inputSchema), and a **handler**. +2. In your plugin’s app-side code, call **useRozenitePluginAgentTool** with a stable **pluginId**, a **tool** (name, description, inputSchema), and a **handler**. 3. Mount the component that registers these tools so it runs when the plugin is active. 4. Agents will see your plugin as a domain and can list and call your tools via `rozenite agent tools|call ...`. @@ -280,4 +280,4 @@ Rozenite derives a short, stable domain name from your `pluginId` alone (for exa - [Adding tools to your application](/docs/agent/adding-tools-to-your-application) – expose app-owned tools under the `app` domain. - [Rozenite for Agents](/docs/agent/overview) – how agents use domains and tools. -- [Plugin Development](/docs/plugin-development/overview) for general plugin structure and React Native integration. +- [Plugin Development](/docs/plugin-development/overview) for general plugin structure and app-side integration. diff --git a/website/src/docs/agent/overview.mdx b/website/src/docs/agent/overview.mdx index a2a2c19f..1b0ac251 100644 --- a/website/src/docs/agent/overview.mdx +++ b/website/src/docs/agent/overview.mdx @@ -1,6 +1,6 @@ # Rozenite for Agents -Rozenite for Agents is the agent-facing way to interact with React Native DevTools and Rozenite plugins. It gives coding agents a reliable interface for inspecting a running React Native app through Rozenite, including logs, network activity, React profiling data, and plugin-specific tooling. +Rozenite for Agents is the agent-facing way to interact with your running app and its Rozenite plugins. It gives coding agents a reliable interface for inspecting a running app through Rozenite, including logs, network activity, React profiling data, and plugin-specific tooling. You can access this workflow through the `rozenite agent` CLI or, if you need direct programmatic access, through [`@rozenite/agent-sdk`](/docs/agent/sdk). For most users, the CLI is still the recommended starting point. @@ -24,13 +24,13 @@ Make sure: - Rozenite is installed and configured for the app - your app is running in development mode - Metro is running -- at least one React Native target is connected +- at least one target is connected If you have more than one simulator, emulator, or device connected, you will need to choose which one to inspect. Rozenite for Agents requires a project with Rozenite already installed and configured. The agent skills teach your coding agent how to use that setup effectively. -Rozenite for Agents is designed for AI and coding agents first. The underlying CLI can be called directly with `npx rozenite`, but the intended use is for an agent to drive it while debugging or implementing changes in a React Native app. +Rozenite for Agents is designed for AI and coding agents first. The underlying CLI can be called directly with `npx rozenite`, but the intended use is for an agent to drive it while debugging or implementing changes in your app. :::warning React Native DevTools will disconnect Rozenite for Agents acts as a debugger connection to the running app. Because of a current React Native limitation, starting an agent session will disconnect React Native DevTools if it is already attached. diff --git a/website/src/docs/agent/sdk.mdx b/website/src/docs/agent/sdk.mdx index 0e29bc27..a9226cf4 100644 --- a/website/src/docs/agent/sdk.mdx +++ b/website/src/docs/agent/sdk.mdx @@ -19,7 +19,7 @@ Make sure: - Node.js 20 or newer - your app is running in development mode - Metro is running and reachable -- at least one React Native target is connected +- at least one target is connected ## Install @@ -252,4 +252,4 @@ async function reconnectToSession(sessionId: string) { - [Rozenite for Agents](/docs/agent/overview) – how agents use domains and tools - [Adding tools to your application](/docs/agent/adding-tools-to-your-application) – expose app-owned tools under the `app` domain -- [Plugin Development](/docs/plugin-development/overview) – general plugin structure and React Native integration +- [Plugin Development](/docs/plugin-development/overview) – general plugin structure and app-side integration diff --git a/website/src/docs/compatibility.mdx b/website/src/docs/compatibility.mdx index 2a5bbb08..2c7560c7 100644 --- a/website/src/docs/compatibility.mdx +++ b/website/src/docs/compatibility.mdx @@ -2,6 +2,8 @@ ## Supported versions +### React Native + | Rozenite | Expo SDK | React Native | Re.Pack | | ---------------- | -------- | ------------ | ------- | | 1.13.0, >= 2.0.0 | 52+ | 0.76+ | 5.2+ | @@ -11,17 +13,28 @@ Expo SDK pins a specific React Native version. If you're on bare React Native (no Expo), match your React Native version to the one shipped by the minimum supported Expo SDK above. +### Lynx + +| Rozenite | rspeedy | @lynx-js/react | +| -------- | ------- | -------------- | +| >= 2.4.0 | 0.16+ | 0.125+ | + +Lynx support is experimental. These are the versions it is developed and tested +against; it has not been exercised across the full range of LynxSDK releases. + ## Bundlers - **Metro** — supported via `@rozenite/metro`, using whichever Metro version ships with your React Native version. No separate Metro version requirement beyond the React Native minimum above. - **Re.Pack** — supported via `@rozenite/repack` for Re.Pack 5.2 and above. +- **rspeedy / Rsbuild** — supported via `@rozenite/lynx` for rspeedy 0.16 and above. ## Platforms - iOS and Android, via React Native DevTools -- Web, via [Rozenite for Web](/docs/rozenite-for-web) (experimental) +- Lynx, via [Rozenite for Lynx](/docs/targets/rozenite-for-lynx) (experimental) +- Web, via [Rozenite for Web](/docs/targets/rozenite-for-web) (experimental) - Plugins declare which of these integrations they support in `rozenite.config.ts`; see [Plugin Development](/docs/plugin-development/plugin-development). ## Notes diff --git a/website/src/docs/getting-started.mdx b/website/src/docs/getting-started.mdx index d07da15d..b2e212b7 100644 --- a/website/src/docs/getting-started.mdx +++ b/website/src/docs/getting-started.mdx @@ -6,6 +6,8 @@ import { PackageManagerTabs } from '@rspress/core/theme'; Rozenite assumes you're comfortable with a React Native project. If you're new to React Native, start with the [React Native documentation](https://reactnative.dev/) first. +On Lynx, follow [Rozenite for Lynx](/docs/targets/rozenite-for-lynx) instead — `rozenite init` doesn't detect rspeedy projects yet ([#493](https://github.com/callstackincubator/rozenite/issues/493)). + ## Install Run the `rozenite init` command in your project. It detects your bundler, installs the right package, and updates your config for you. diff --git a/website/src/docs/introduction.md b/website/src/docs/introduction.md index f2c20736..568eae37 100644 --- a/website/src/docs/introduction.md +++ b/website/src/docs/introduction.md @@ -1,6 +1,6 @@ # Introduction -Rozenite adds plug-and-play debugging panels to React Native DevTools. Install official plugins, open DevTools, and start debugging in minutes — no extra windows or servers. +Rozenite adds plug-and-play debugging panels to React Native DevTools — for React Native, web and Lynx apps. Install official plugins, open DevTools, and start debugging in minutes — no extra windows or servers. ![](/rozenite-loaded.png) @@ -31,6 +31,7 @@ Rozenite is a rare mineral first described in 1960 on Ornak Mountain in the West - **Developers who want built‑in tooling**: add network, performance, storage and state panels without building anything. - **Teams using coding agents**: let agents inspect logs, network activity, and React profiling data through Rozenite for Agents. - **Teams that need custom insights**: create tailored panels for your product, internal observability, or business logic. +- **Teams on Lynx**: the same panels, plugins and CLI, over [Rozenite for Lynx](/docs/targets/rozenite-for-lynx) (experimental). ## Build your own @@ -38,4 +39,4 @@ Rozenite includes a type‑safe, batteries‑included development experience for ## Next steps -Ready to try it? Start with the [Getting Started guide](/docs/getting-started), explore the [Rozenite for Agents overview](/docs/agent/overview), or browse the [Plugin Directory](/plugin-directory). When you need something custom, see the [Plugin Development overview](/docs/plugin-development/overview). +Ready to try it? Start with the [Getting Started guide](/docs/getting-started), explore the [Rozenite for Agents overview](/docs/agent/overview), see which [targets](/docs/targets/rozenite-for-lynx) Rozenite reaches, or browse the [Plugin Directory](/plugin-directory). When you need something custom, see the [Plugin Development overview](/docs/plugin-development/overview). diff --git a/website/src/docs/official-plugins/controls.mdx b/website/src/docs/official-plugins/controls.mdx index 4939b744..de364b82 100644 --- a/website/src/docs/official-plugins/controls.mdx +++ b/website/src/docs/official-plugins/controls.mdx @@ -90,7 +90,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is available when you debug your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is available when you debug your React Native web app. ## Usage diff --git a/website/src/docs/official-plugins/overlay.mdx b/website/src/docs/official-plugins/overlay.mdx index 36c85bca..5770a9c7 100644 --- a/website/src/docs/official-plugins/overlay.mdx +++ b/website/src/docs/official-plugins/overlay.mdx @@ -28,7 +28,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app. ## Usage diff --git a/website/src/docs/official-plugins/performance-monitor.mdx b/website/src/docs/official-plugins/performance-monitor.mdx index 68f7eb43..6bf1a449 100644 --- a/website/src/docs/official-plugins/performance-monitor.mdx +++ b/website/src/docs/official-plugins/performance-monitor.mdx @@ -24,7 +24,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app. ## Usage diff --git a/website/src/docs/official-plugins/react-navigation.mdx b/website/src/docs/official-plugins/react-navigation.mdx index 9114121d..0c3950ca 100644 --- a/website/src/docs/official-plugins/react-navigation.mdx +++ b/website/src/docs/official-plugins/react-navigation.mdx @@ -49,7 +49,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app. ## Usage diff --git a/website/src/docs/official-plugins/redux-devtools.mdx b/website/src/docs/official-plugins/redux-devtools.mdx index 7346cf0b..090dcc47 100644 --- a/website/src/docs/official-plugins/redux-devtools.mdx +++ b/website/src/docs/official-plugins/redux-devtools.mdx @@ -67,7 +67,7 @@ export default store; ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app. ## Usage diff --git a/website/src/docs/official-plugins/storage.mdx b/website/src/docs/official-plugins/storage.mdx index 51a5d98e..7db0e7a9 100644 --- a/website/src/docs/official-plugins/storage.mdx +++ b/website/src/docs/official-plugins/storage.mdx @@ -45,7 +45,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app — showing entries from whichever Async Storage / Expo Secure Store adapters you configure for the browser. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app — showing entries from whichever Async Storage / Expo Secure Store adapters you configure for the browser. ## Adapters diff --git a/website/src/docs/official-plugins/tanstack-query.mdx b/website/src/docs/official-plugins/tanstack-query.mdx index 18ad537c..da94524a 100644 --- a/website/src/docs/official-plugins/tanstack-query.mdx +++ b/website/src/docs/official-plugins/tanstack-query.mdx @@ -40,7 +40,7 @@ function App() { ## Web (React Native for Web) -With [Rozenite for Web](/docs/rozenite-for-web), this plugin is also available when debugging your React Native web app. +With [Rozenite for Web](/docs/targets/rozenite-for-web), this plugin is also available when debugging your React Native web app. ## Usage diff --git a/website/src/docs/plugin-development/overview.md b/website/src/docs/plugin-development/overview.md index a7db8767..5ee391ad 100644 --- a/website/src/docs/plugin-development/overview.md +++ b/website/src/docs/plugin-development/overview.md @@ -6,7 +6,7 @@ Plugins add new panels to React Native DevTools through Rozenite — custom debu A plugin has two parts that talk to each other over a type-safe, event-based bridge: -1. **React Native side** — code that runs in your app. +1. **App side** — code that runs in your app. Its entry point is `react-native.ts`, on every target Rozenite supports. 2. **DevTools side** — the panel UI shown in DevTools. Changes on either side are reflected on the other in real time, and both sides can send data or commands. @@ -17,7 +17,7 @@ Changes on either side are reflected on the other in real time, and both sides c my-plugin/ ├── src/ │ └── hello-world.tsx # Your DevTools panels -├── react-native.ts # React Native entry point +├── react-native.ts # App-side entry point ├── rozenite.config.ts # Plugin configuration ├── vite.config.ts # Build configuration ├── package.json # Dependencies and scripts diff --git a/website/src/docs/plugin-development/plugin-development.md b/website/src/docs/plugin-development/plugin-development.md index 6024bb4b..a20a6eaa 100644 --- a/website/src/docs/plugin-development/plugin-development.md +++ b/website/src/docs/plugin-development/plugin-development.md @@ -40,7 +40,7 @@ Your generated plugin has this structure: my-plugin/ ├── src/ │ └── hello-world.tsx # Your DevTools panels -├── react-native.ts # React Native entry point +├── react-native.ts # App-side entry point ├── rozenite.config.ts # Plugin configuration ├── vite.config.ts # Build configuration ├── package.json # Dependencies and scripts @@ -49,7 +49,7 @@ my-plugin/ ## Step 3: Creating Panels -Panels are React components that appear in the DevTools interface, defined in your `rozenite.config.ts` file. Your React Native side can use any React Native API or library, so a panel can integrate as deeply with the runtime as you need. +Panels are React components that appear in the DevTools interface, defined in your `rozenite.config.ts` file. Your app side can use any API or library available in the app, so a panel can integrate as deeply with the runtime as you need. ### Type-safe communication @@ -250,9 +250,9 @@ export default function MyPanel() { } ``` -## Step 4: React Native Integration +## Step 4: App-Side Integration -Add React Native functionality by creating a `react-native.ts` file. You can use React Native APIs and libraries to enhance your plugin: +Add app-side functionality by creating a `react-native.ts` file. The filename is the same on every target Rozenite supports. On React Native you can reach for React Native APIs and libraries: ```typescript title="react-native.ts" import { DevToolsPluginClient } from '@rozenite/plugin-bridge'; @@ -381,7 +381,7 @@ rozenite build This creates optimized bundles: - DevTools panels (minified and optimized) -- React Native entry point (if `react-native.ts` exists) +- App-side entry point (if `react-native.ts` exists) - Ready for distribution ### Build Output @@ -389,7 +389,7 @@ This creates optimized bundles: The build creates a `dist/` directory with: - `*.js` - Individual DevTools panel files (one file per panel, names reflect your config) -- `react-native.js` - React Native integration (if applicable) +- `react-native.js` - app-side integration (if applicable) - `rozenite.json` - Plugin manifest with metadata and configuration - Source maps for debugging diff --git a/website/src/docs/standalone-app.mdx b/website/src/docs/standalone-app.mdx index afae53b1..c7b892f2 100644 --- a/website/src/docs/standalone-app.mdx +++ b/website/src/docs/standalone-app.mdx @@ -6,7 +6,7 @@ import { PackageManagerTabs } from '@rspress/core/theme'; The standalone app is an experiment in alternative DevTools workflows. It may change in future releases. ::: -The standalone Rozenite app runs your plugin panels in their own window instead of inside React Native DevTools. This is useful if you primarily work with Rozenite plugins and want to keep your plugin state intact when your app reloads — instead of losing everything when React Native DevTools tears down its panel on a Fast Refresh or crash. +The standalone Rozenite app runs your plugin panels in their own window instead of inside React Native DevTools. It connects straight to the device, so it gives you the plugins you installed and nothing else. ## Before you start @@ -18,9 +18,13 @@ If you skip this step, `rozenite open` tells you to install it when you run the ## Why use it -React Native DevTools reloads its Rozenite panel whenever your app's JS VM reloads (on a Fast Refresh that touches native code, a crash, or a manual reload). That wipes out any panel state — you're back to the default view every time. +The standalone app is for two situations. -The standalone app keeps your plugins connected through those reloads. Since it connects directly to the device instead of going through React Native DevTools, your plugin panels stay open and preserve their state. This is helpful if you spend most of your debugging time in Rozenite and don't need the full React Native DevTools feature set. +**You mostly use Rozenite.** If your debugging happens in Rozenite panels rather than in React Native DevTools' built-in tools, the standalone app gives you those panels and nothing else — one window, the plugins you installed, no tab to hunt for. + +**You're on another target.** The app connects straight to the device rather than going through React Native DevTools, so it works the same way whichever target you're debugging. + +Connecting directly has a useful side effect: your panels aren't torn down when the app's JS VM reloads, so panel state survives a Fast Refresh, a crash or a manual reload. ## Launching it diff --git a/website/src/docs/targets/_meta.json b/website/src/docs/targets/_meta.json new file mode 100644 index 00000000..e21199a6 --- /dev/null +++ b/website/src/docs/targets/_meta.json @@ -0,0 +1,4 @@ +[ + { "type": "file", "name": "rozenite-for-lynx", "label": "Rozenite for Lynx" }, + { "type": "file", "name": "rozenite-for-web", "label": "Rozenite for Web" } +] diff --git a/website/src/docs/rozenite-for-lynx.mdx b/website/src/docs/targets/rozenite-for-lynx.mdx similarity index 88% rename from website/src/docs/rozenite-for-lynx.mdx rename to website/src/docs/targets/rozenite-for-lynx.mdx index 2d932421..ec380274 100644 --- a/website/src/docs/rozenite-for-lynx.mdx +++ b/website/src/docs/targets/rozenite-for-lynx.mdx @@ -3,7 +3,7 @@ import { PackageManagerTabs } from '@rspress/core/theme'; # Rozenite for Lynx :::warning Experimental -Rozenite for Lynx is experimental. It has been verified against LynxExplorer on the iOS Simulator, the Android emulator, and a physical Android phone over USB. A physical iPhone should work the same way, over DebugRouter's usbmux transport, but that path hasn't been tested yet. It has not been verified across the full range of LynxSDK versions, and the API may change in future releases. +Rozenite for Lynx is experimental. It is developed and tested against rspeedy 0.16 and `@lynx-js/react` 0.125, and has not been verified across the full range of LynxSDK versions. The API may change in future releases. ::: Rozenite for Lynx lets you debug a [Lynx](https://lynxjs.org) app with the same DevTools panels, the same plugin catalogue and the same `rozenite` CLI you use for React Native. @@ -44,7 +44,7 @@ in development: it is inert in production builds (pass `enabled` explicitly if you want to control that yourself), and it never runs at all during `rspeedy build`. -Rozenite plugins are discovered from your project's `package.json` exactly as they are for React Native — install a plugin as a dependency and it shows up. +Rozenite plugins are discovered from your project's `package.json` exactly as they are for React Native — install a plugin as a dependency and it shows up. Not every plugin supports Lynx yet; see [What doesn't work yet](#what-doesnt-work-yet). ## Open DevTools @@ -81,5 +81,5 @@ Note that `enableDesktop` is an iOS Simulator concern only. An Android emulator ## What doesn't work yet -- Plugins whose device half is written against React Native APIs don't work on Lynx: network activity, storage, file system, performance monitor, require profiler and Expo Atlas. Plugins that only move state around do work — Redux DevTools, TanStack Query, React Hook Form, feature flags and controls. -- Only apps DebugRouter can reach are discovered: Android over adb, physical iPhones over USB, and the iOS Simulator over localhost. +- Plugins whose device half is written against React Native APIs don't work on Lynx: network activity, storage, file system, performance monitor, require profiler, Redux DevTools and Expo Atlas. Plugins that only move state around do work — TanStack Query, React Hook Form, feature flags and controls. +- Every plugin declares the targets it supports in its `rozenite.config.ts`, so the list above is the one the plugins themselves agree on. diff --git a/website/src/docs/rozenite-for-web.mdx b/website/src/docs/targets/rozenite-for-web.mdx similarity index 100% rename from website/src/docs/rozenite-for-web.mdx rename to website/src/docs/targets/rozenite-for-web.mdx diff --git a/website/src/public/standalone-rozenite.png b/website/src/public/standalone-rozenite.png new file mode 100644 index 00000000..9ce39f29 Binary files /dev/null and b/website/src/public/standalone-rozenite.png differ diff --git a/website/vercel.json b/website/vercel.json index f97a6117..84c0dab0 100644 --- a/website/vercel.json +++ b/website/vercel.json @@ -4,7 +4,24 @@ "cleanUrls": true, "framework": null, "installCommand": "pnpm install", - "rewrites": [{ "source": "/(.*)", "destination": "/404.html" }], + "redirects": [ + { + "source": "/docs/rozenite-for-lynx", + "destination": "/docs/targets/rozenite-for-lynx", + "permanent": true + }, + { + "source": "/docs/rozenite-for-web", + "destination": "/docs/targets/rozenite-for-web", + "permanent": true + } + ], + "rewrites": [ + { + "source": "/(.*)", + "destination": "/404.html" + } + ], "outputDirectory": "build", "trailingSlash": false, "git": {