Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3b887f4
feat: add Lynx web platform support with sparkling-web-shell package
Huxpro Mar 5, 2026
eb193de
feat: add web platform CLI support (run:web, --platform flag, web aut…
Huxpro Mar 5, 2026
954cb38
feat: add web method bridge with handler registry and platform implem…
Huxpro Mar 5, 2026
63e7bfd
feat: add web method bridge, CLI web support, docs, and create-app we…
Huxpro Mar 6, 2026
7c0ee9a
chore: drop stale playground bundle artifacts re-introduced by rebase
claude Jul 12, 2026
f5c8e75
chore: regenerate pnpm-lock.yaml after rebasing web support onto main
claude Jul 12, 2026
33caad9
feat: add sparkling-history — reusable history shim driving native na…
claude Jul 12, 2026
9c76d55
feat(web): stacked multi-container web shell with lifecycle events an…
claude Jul 12, 2026
85f7d09
feat: add sparkling-playground-vue demo + fix cross-heap initial state
claude Jul 12, 2026
562e642
docs+test: Vue Router compatibility matrix and in-heap pass-through s…
claude Jul 12, 2026
7b3d250
fix: resolve conflict markers left in web-support cherry-picks
claude Jul 12, 2026
858a7d4
fix(create-app): use @clack/prompts for askWebPlatform
claude Jul 12, 2026
7353e6f
feat(examples): turn the vue-router demo into a go-web example
claude Jul 12, 2026
2756364
docs(examples): add Vue Router (MPA) example page with <Go> embeds
claude Jul 12, 2026
c8adbc3
refactor(web): adopt main's pluggable RouterWebHost in the web shell
claude Jul 12, 2026
de712ee
feat(example): surface cross-page nav result; document <Go> verification
claude Jul 12, 2026
b9028ac
feat(website): live cross-page MPA demo via the embedded web shell (<…
claude Jul 13, 2026
cc28806
feat(web): native-style push/pop transitions for MPA navigation
claude Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/en/apis/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

APIs for Lynx bundles running inside Sparkling containers.

| API | Description |
| --- | --- |
| [GlobalProps](/apis/global-props/Interface.GlobalProps) | Runtime globals injected by the native SDK (`lynx.__globalProps`) |
| [Scheme](/apis/scheme) | The unified `hybrid://...` URL format for opening pages/containers |
| [Navigation](/apis/sparkling-methods/sparkling-navigation) | Router helpers for opening/closing pages from Lynx/JS |
| [Storage](/apis/sparkling-methods/sparkling-storage) | Key-value storage helpers for Lynx/JS |
| [Media](/apis/sparkling-methods/sparkling-media) | Media helpers for choosing, uploading, downloading, and saving files |
| API | Description | Web |
| --- | --- | --- |
| [GlobalProps](/apis/global-props/Interface.GlobalProps) | Runtime globals injected by the native SDK (`lynx.__globalProps`) | - |
| [Scheme](/apis/scheme) | The unified `hybrid://...` URL format for opening pages/containers | - |
| [Navigation](/apis/sparkling-methods/sparkling-navigation) | Router helpers for opening/closing pages from Lynx/JS | Yes |
| [Storage](/apis/sparkling-methods/sparkling-storage) | Key-value storage helpers for Lynx/JS | Yes |
| [Media](/apis/sparkling-methods/sparkling-media) | Media helpers for choosing, uploading, downloading, and saving files | Yes |

## Sparkling SDK

Expand Down
30 changes: 28 additions & 2 deletions docs/en/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ npx sparkling build
| Option | Description |
| --- | --- |
| `--config <path>` | Path to `app.config.ts` (default: `app.config.ts`) |
| `--platform <platform>` | Target platform: `android`, `ios`, `web`, or `all` (default: `all`) |
| `--copy` | Copy built assets to Android and iOS native shells |
| `--skip-copy` | Skip copying assets (default) |

By default, asset copying is skipped for faster iteration during development. Use `--copy` when you need the bundles inside the native projects (e.g. for a release build).

When `--platform web` is specified, only web bundles (`*.web.bundle`) are produced. This is faster than building all environments.

### `sparkling dev`

Start the Rspeedy dev server for hot-reload development. Instead of rebuilding and copying bundles manually, the dev server serves bundles over HTTP so changes are reflected instantly.
Expand All @@ -41,6 +44,7 @@ npx sparkling dev
| Option | Description |
| --- | --- |
| `--config <path>` | Path to `app.config.ts` (default: `app.config.ts`) |
| `--platform <platform>` | Target platform: `web`, `native`, or `all` (default: `all`) |
| `--port <number>` | Dev server port (default: `app.config.ts -> dev.server.port`, fallback `5969`) |
| `--host <host>` | Dev server host (default: `app.config.ts -> dev.server.host`, then Rspeedy default) |

Expand Down Expand Up @@ -81,12 +85,13 @@ npx sparkling autolink

| Option | Description |
| --- | --- |
| `--platform <platform>` | Platform to autolink: `android`, `ios`, or `all` (default: `all`) |
| `--platform <platform>` | Platform to autolink: `android`, `ios`, `web`, or `all` (default: `all`) |

**What it does:**

- **Android** — Links Sparkling method Gradle projects and generates `SparklingAutolink.kt`. Debug-tool packages are linked as `debugImplementation`.
- **iOS** — Links Sparkling method pods and generates `SparklingAutolink.swift`. Debug-tool packages are linked in the debug target.
- **Web** — Reads `web` entries from each `module.config.json` and generates a `web-autolink.ts` file that imports all `*/web` method handlers.

### `sparkling run:android`

Expand Down Expand Up @@ -140,6 +145,24 @@ This command will:

You can also set the `SPARKLING_IOS_SIMULATOR` environment variable to specify a default simulator.

### `sparkling run:web`

Build web bundles and launch a browser preview.

```bash
npx sparkling run:web
```

This command will:

1. Build web bundles (`*.web.bundle`)
2. Start the `sparkling-web-shell` dev server
3. Open the app in your default browser at `http://localhost:3000`

Use `?page=<name>` query parameters to navigate to different entry points (e.g. `http://localhost:3000?page=second`).

For more details, see the [Web Platform Guide](/guide/web-platform).

### `sparkling doctor`

Verify that your development environment is properly set up.
Expand Down Expand Up @@ -196,6 +219,9 @@ npx sparkling run:android
# 5. Run on iOS
npx sparkling run:ios

# 6. Build bundles for release
# 6. Preview in browser
npx sparkling run:web

# 7. Build bundles for release
npx sparkling build --copy
```
7 changes: 7 additions & 0 deletions docs/en/guide/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ A minimal Sparkling Lynx page with a tap counter.

<Go example="hello-world" defaultFile="src/App.tsx" defaultEntryName="main" />

## Vue Router (MPA)

[Vue Router](/guide/examples/vue-router) driving Sparkling's multi-page native
navigation: one router config, multiple native Lynx pages. In-heap (SPA) route
transitions run live in the web preview; cross-page (MPA) navigation opens
native containers on device.

## Sparkling Go demos

The [Sparkling Go playground](https://github.com/tiktok/sparkling/tree/main/packages/playground)
Expand Down
95 changes: 95 additions & 0 deletions docs/en/guide/examples/vue-router.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Vue Router (MPA)

This example runs **[Vue Router](https://router.vuejs.org) on top of Sparkling
navigation**, so a single router configuration drives navigation across
**multiple native Lynx pages** — the multi-page (MPA) model.

It is built on [`sparkling-history`](https://github.com/tiktok/sparkling/tree/main/packages/sparkling-history),
a framework-agnostic shim that implements Vue Router's history contract on top
of Sparkling's `router.open` / `router.close`.

:::info Different from VueLynx's in-view router
[VueLynx](https://vue.lynxjs.org) supports Vue Router with `createMemoryHistory`
to build an SPA **inside a single `LynxView`** (one JS heap). This example is
the opposite: each page is a **separate Lynx container in its own JS heap**,
and Vue Router drives *native* navigation between them. The heaps share no
memory — only a build-time route manifest connects them.
:::

## In-heap navigation (live in the web preview)

The `main` bundle owns two routes, `/` and `/features`. Navigating between them
is an ordinary Vue Router transition **inside one heap** — no native bridge is
involved — so it runs live in the web preview below. Tap **Features** (or the
in-container button) and watch the route change without opening a new page.

<Go
example="vue-router-mpa"
defaultEntryName="main"
defaultFile="src/pages/main/views/Home.vue"
/>

:::tip This goes beyond the other demos
Sparkling's other web previews render UI only — their native calls are inert in
the browser. Here the in-heap route transitions actually work on the web,
because that regime is pure Vue Router.
:::

## Dynamic routes (the `users` bundle)

The `users` bundle owns `/users` and the dynamic `/users/:id`. Tapping a user
is an in-heap navigation with a route param, resolved entirely in this bundle's
router. This preview boots at the bundle's own default route (`/users`).

<Go
example="vue-router-mpa"
defaultEntryName="users"
defaultFile="src/pages/users/views/UserList.vue"
/>

## Cross-page navigation (MPA — live demo)

When `router.push` resolves to a route owned by **another** bundle (per the
route manifest), the shim diverts it to `router.open`, which stacks a **new
native container**. The previous page stays alive underneath, exactly like a
native navigation stack.

The `<Go>` previews above render one bundle at a time with no native bridge, so
cross-page navigation can't run in them. The demo below is different: it opens
the **Sparkling web shell** in a new tab — the shell stacks a `<lynx-view>` per
container, provides the `spkPipe` method bridge, and installs a
`RouterWebHost`, so `router.open` actually opens a new container **in your
browser**. Tap **push('/users')** and a second container **slides in** and
stacks on top (the header's `depth` becomes 1); the **browser back button**
slides it back out, because the shell owns the whole tab and drives real
browser history. (The slide is a native-style transition the shell applies on
push/pop; it honors `prefers-reduced-motion`.)

<MpaPreview example="vue-router-mpa" page="main" />

You can also **scan the QR code** on any `<Go>` preview to run it in Lynx
Explorer (with Sparkling integrated), where `router.open` runs natively.

:::note Why the `<Go>` previews can't cross pages, but the web shell can
In-heap navigation is pure Vue Router in the card's own JS heap — no native
call — so it runs in any preview. Cross-page navigation calls `router.open`,
which needs a native host. `sparkling-navigation` exposes a pluggable
`RouterWebHost`, but go-web's `<Go>` preview doesn't yet expose a hook to
register it (or the `spkPipe` bridge) on its `<lynx-view>`. The web shell owns
its whole tab, so it installs that host and bridge itself — which is why
cross-page navigation works there.
:::

## How it maps

| Vue Router | Sparkling |
|---|---|
| route owned by the current bundle | in-heap transition (SPA) |
| route owned by another bundle | `router.open(<scheme>)` — new container (MPA) |
| `router.back()` past the local history | `router.close()` — pop the container |
| `history.state` | carried across heaps inside the scheme URL |
| history mode (`createWebHistory`/…) | replaced by `createHybridHistory` |

For the complete traversal of Vue Router's feature set — what is supported,
reframed, or limited under the MPA model — see the
[compatibility matrix](https://github.com/tiktok/sparkling/blob/main/packages/sparkling-history/COMPATIBILITY.md).
11 changes: 11 additions & 0 deletions docs/en/guide/get-started/create-custom-method.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ npx sparkling autolink

</Steps>

## Add web support (optional)

You can add a web implementation so your method works in the browser. See the full guide at [Web Method Implementations](/guide/web-method-implementations).

In brief:

1. Create `src/web/index.ts` with `registerWebMethod()` calls
2. Add `"./web"` subpath export to `package.json`
3. Add `"web"` to `platforms` in `module.config.json`
4. Run `npx sparkling autolink`

## Best practices

- **Naming convention**: package name should follow `sparkling-<module>` format.
Expand Down
8 changes: 6 additions & 2 deletions docs/en/guide/get-started/create-new-app.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,17 @@ npm create sparkling-app@latest my-app
cd my-app
```

### Run native targets
### Run on any platform

```bash
# Android
npm run run:android

# iOS
npm run run:ios

# Web (browser preview)
npm run run:web
```

### Add Sparkling methods as needed
Expand All @@ -48,6 +51,7 @@ npx sparkling build

- A Lynx app project (based on the default template)
- Android and iOS native shells already wired for Sparkling
- Web platform support for browser previews (enabled by default)
- A working JS ↔ native pipe method example (router)
- Developer workflow commands (build / autolink / run)

Expand All @@ -56,7 +60,7 @@ Key folders/files created by the default template:
- `src/`: Lynx/React entry points and assets
- `android/`, `ios/`: native shells wired to Sparkling SDK
- `app.config.ts`: build + routing config consumed by `sparkling-app-cli`
- `package.json`: scripts (`dev`, `build`, `run:android`, `run:ios`)
- `package.json`: scripts (`dev`, `build`, `run:android`, `run:ios`, `run:web`)

### Prerequisites

Expand Down
62 changes: 62 additions & 0 deletions docs/en/guide/web-limitations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Web Limitations

The web platform provides a convenient development and preview environment, but has differences from the native Android/iOS platforms. This page documents known limitations.

## Rendering

- **DOM-based rendering**: `@lynx-js/web-core` renders Lynx components as custom HTML elements in the DOM, rather than using a native view hierarchy. CSS behavior follows browser standards, which may differ from Lynx's native layout engine in edge cases.
- **Performance**: For complex UIs with many elements or animations, native rendering will generally be faster. The web platform is best suited for development previews, not performance-critical production use.

## Native Bridge

- **`NativeModules` is unavailable**: On web, the Lynx `NativeModules` global does not exist. All method calls must go through the [web handler registry](/guide/web-method-implementations).
- **Unregistered methods**: Methods without a web handler return error code `-3` (module not registered). Check which methods have web handlers in the [built-in methods table](/guide/web-method-implementations#built-in-web-methods).

## Storage

- **Not encrypted**: `localStorage` is not encrypted, unlike native secure storage options. Do not store sensitive data (tokens, credentials) via `storage.*` methods on web in production.
- **Size limit**: `localStorage` has a ~5 MB per-origin limit in most browsers. Native storage does not have this restriction.
- **Synchronous**: `localStorage` operations are synchronous and block the main thread. For large datasets, this can cause jank.

## Media

- **Camera access**: `media.chooseMedia` with a camera source requires HTTPS. On `http://localhost` during development, camera access may be allowed by the browser, but in production, HTTPS is required.
- **File downloads**: `media.downloadFile` uses `fetch()` and is subject to CORS restrictions. The target URL must include appropriate CORS headers, or the download will fail.
- **File type filtering**: Browser `<input type="file">` accept filters are hints — users can override them and select any file type.

## Network

- **CORS**: All `fetch()` calls from the browser are subject to Cross-Origin Resource Sharing (CORS) policies. APIs that work from native (which has no CORS) may fail from web without server-side CORS headers.
- **Cookies**: Cookie handling differs between browser and native HTTP clients. Session management may behave differently on web.

## Device APIs

The following native capabilities are not available on web unless the browser provides equivalent APIs with user permission:

- Geolocation (available via browser Geolocation API, requires HTTPS)
- Push notifications (available via browser Push API, requires service worker)
- Biometric authentication (not available)
- App-level deep linking (not applicable in browser context)
- Background processing (limited to service workers)

## Feature Detection

Write platform-aware code using error handling:

```ts
import { callAsync } from 'sparkling-method';

try {
const result = await callAsync('nativeOnly.feature', params);
// Use native result
} catch (e) {
if (e.code === -2 || e.code === -3) {
// Not available on this platform
// Show alternative UI or skip the feature
}
}
```

Error codes:
- `-2`: `NativeModules` not available (running on web without a handler)
- `-3`: Module not registered (no web handler for this method)
Loading