feat: add Lynx web platform support with sparkling-web-shell package - #3
Open
Huxpro wants to merge 18 commits into
Open
feat: add Lynx web platform support with sparkling-web-shell package#3Huxpro wants to merge 18 commits into
Huxpro wants to merge 18 commits into
Conversation
Huxpro
force-pushed
the
claude/vue-router-sparkling-nav-7a7fy7
branch
from
July 12, 2026 20:18
64204a2 to
192bc70
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Add multi-environment build config (web + lynx) to rspeedy so the playground and template can produce browser-compatible bundles alongside native ones. Web bundles output to dist/web/ and are served by a new sparkling-web-shell package that renders them via <lynx-view>. - Create packages/sparkling-web-shell with @lynx-js/web-core 0.19.8 and @lynx-js/web-elements 0.11.3 - Add environments config to playground lynx.config.ts with separate distPath for web output - Filter web/ dir from native asset copies - Add build:web and dev:web scripts to playground - Update template app.config.ts with matching environments config
…olink) Add web platform support to sparkling-app-cli: - New `run:web` command: autolink → build web → start web shell in browser - `--platform` flag on `build` and `dev` commands (web/native/all) - Web-aware `autolink`: generates `.sparkling/web-autolink.ts` with `import 'pkg/web'` for modules declaring web support - `copy-assets` excludes `web/` subdirectory from native copies - `doctor --platform web` checks for @lynx-js/web-core and web shell - Updated types: `web` in PlatformConfig, MethodModuleConfig, AppConfig.paths
…entations Add a web dispatch layer to sparkling-method so pipe calls route to browser-native handlers when running in a web environment. Each method package now exports a ./web subpath that registers handlers at import time. - sparkling-method: web-registry.ts (registerWebMethod, isWebEnvironment), WebMethodHandler type, web intercept in LynxPipe.call() before NativeModules check - sparkling-navigation/web: router.open (History API + CustomEvent), router.close - sparkling-storage/web: getItem/setItem/removeItem via localStorage with namespacing - sparkling-media/web: chooseMedia (file input), downloadFile (fetch+blob), uploadFile/uploadImage (FormData), saveDataURL (unsupported stub)
…b option - Add web-registry to sparkling-method for dispatching pipe calls to browser-based handlers when NativeModules is unavailable - Update sparkling-web-shell with onNativeModulesCall bridge and spkPipe Worker stub for web-core RPC - Fix navigation web handler scheme parsing - Add CLI run:web command and --platform web flag to dev/build/autolink - Add web platform docs (guide, method implementations, limitations) - Add "Enable web platform?" prompt to create-sparkling-app - Add web sidebar entries to website rspress config - Rebuild playground native bundles
The playground no longer tracks built .lynx.bundle files on main; the cherry-picked web-support commits carried old prebuilt bundles from the original PR base. Remove them again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
…vigation A framework-agnostic W3C-History-style shim that lets SPA routers (vue-router first) drive Sparkling's MPA-style native navigation: - NavigationHost contract: minimal platform API (initialUrl/open/close/ visibility) any container stack can implement - createHybridHistory: RouterHistory-compatible history per container — in-container navs behave like vue-router's memory history, boundary navs (pushExternal, go(-n) past the queue) walk the native stack - Route manifest + sparkling scheme codec: build-time file-based route table embedded in every bundle so isolated JS heaps agree on route->scheme mapping; state/depth/route ride across heaps in the URL - vue adapter: createSparklingRouter diverts cross-bundle push/replace (incl. named routes) to native opens, keeps same-bundle navs local - codegen: scans src/pages/* convention into a manifest module + rsbuild/rspeedy plugin - memory host: simulates the multi-container stack so vue-router's official memory-history suite is ported and cross-heap flows are tested with real vue-router instances (45 tests) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
…d globalProps Web-support improvements split out for a stacked PR onto the web platform work (tiktok#22). The previous shell destroyed and re-created a single <lynx-view> on every navigation, which cannot represent Sparkling's native container stack (no state preservation on back, no lifecycle, no globalProps). Now: - router.open STACKS a new <lynx-view> (own Worker heap); the previous view stays alive hidden underneath — router.close / browser back pops it and reveals the previous view with state intact - each view is injected with globalProps (containerID + queryItems parsed from its scheme) mirroring the native SDK's GlobalPropsUtils — required by anything that reads lynx.__globalProps on web, incl. sparkling-history - viewAppeared/viewDisappeared GlobalEvents are sent on stack transitions, mirroring the native ViewEventUtils contract - browser history entries carry the scheme stack in state; popstate reconciles the view stack (back pops live views, forward re-creates fresh heaps, reload rebuilds — matching native process-restore behavior) - sparkling-navigation web handlers slim down to validated event dispatch (sparkling:navigate / sparkling:close); the shell owns stack + history bookkeeping like the native SDK owns the activity stack, and the open handler now honors the replace option (previously ignored) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Vue-Lynx demo proving vue-router can drive Sparkling multi-page native
navigation (MPA) via sparkling-history — validated end-to-end on the web
shell harness (26 Playwright checks: local SPA nav, cross-container
stacking, dynamic params, guards, named routes, back across the container
boundary, onRestore events, replace, state hand-off, deep-linking).
- file-based pages (src/pages/*) with per-page routes.json → route manifest
generated at build time by pluginRouteManifest
- one createPageRouter() per bundle/heap; cross-bundle navs divert to
sparkling router.open, same-bundle navs stay in-heap
Also fixes sparkling-history: HybridHistory.replace now merges the current
entry's state with the new data, matching vue-router's HTML5 history
(replaceState merges history.state). This preserves the initial cross-heap
state through the router's first-navigation replace(fullPath, { scroll }).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
…uite - COMPATIBILITY.md: full traversal of Vue Router's guide features + e2e examples, each marked supported/reframed/limited for the Sparkling MPA model, with the reason and where it's validated - README.md: layering (host / codec+manifest / history / adapter) and the navigation flow across heaps - vue-features.spec.ts: proves createSparklingRouter leaves vue-router's in-heap features intact (dynamic match, nested, named views, redirect, alias, guard abort/redirect, afterEach, addRoute) while still diverting cross-bundle targets (12 tests) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
The first-session cherry-pick of the upstream web-support commits staged a few files with unresolved conflict markers (create-app completion notes, sparkling-media module.config.json, and the CLI / getting-started docs). Resolve them: merge the web-aware completion notes without duplicating the p.note output, align sparkling-media's module.config.json with the nav/ storage structure (adds web entry + platforms), and merge the docs so both the native and web options are documented. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
The web-support cherry-pick added askWebPlatform written against inquirer, but this fork's create-sparkling-app uses @clack/prompts. Rewrite it with p.confirm + checkCancel to match the rest of user-prompts.ts (the previous code referenced undefined inquirer/ui/handlePromptError and failed tsc). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Move packages/playground-vue -> examples/vue-router-mpa so the go-web prepare-examples pipeline discovers it and the <Go> component can embed it: - lynx.config.ts: dual lynx+web environments with default filenames, so it emits main/users/settings .web.bundle (browser preview) + .lynx.bundle (QR/device), with an HTTP assetPrefix; keeps the route-manifest codegen - package.json: @sparkling-example/vue-router-mpa, "build" script - shared/router.ts: boot each bundle at its OWN default route when the container URL carries no owned route (the standalone go-web preview case), so the users/settings previews render their own pages instead of a route another bundle owns - README: web-preview (live in-heap nav) vs device/harness (full MPA) scope Also fix the web shell to load the web-target build: sparkling schemes name the native <name>.lynx.bundle, but web-core needs <name>.web.bundle, so the shell rewrites the extension when resolving the URL to render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
New Examples page (en + zh) embedding the vue-router-mpa example via go-web: - embeds the `main` bundle, where in-heap Home<->Features navigation runs LIVE in the web preview (pure Vue Router, no native bridge) — going beyond the other demos, whose native calls are inert on the web - embeds the `users` bundle (dynamic /users/:id), booting at its own default route in the standalone preview - scopes what the web preview shows vs. cross-page MPA navigation, which needs the QR/device tab or the local web-shell harness - maps each Vue Router concept to its Sparkling equivalent and links the sparkling-history compatibility matrix Wired into the Examples overview and the en/zh website sidebars. Verified the page renders and the <Go> component mounts a live web-core preview of the vue-lynx bundle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
After rebasing onto PR#7 (which added the web-method bridge + a pluggable
RouterWebHost), drop the shell's old sparkling:navigate/close event contract
and install its stacked-container behavior via setRouterWebHost instead —
the sanctioned extension point. The host receives the full scheme (all a new
container's heap needs) and drives push/replace/close directly.
Also extend RouterWebHost.open with an optional { replace } (threaded from
router.open's params) so router.replace can swap the current container on web
instead of stacking; the default History-API host honors it via replaceState.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Verified the example in the go-web <Go> web preview:
- in-heap (SPA) navigation WORKS live (e.g. Home <-> Features) — pure Vue
Router, no native bridge
- cross-page (MPA) navigation does NOT run: go-web's preview provides no
Sparkling `spkPipe` bridge and no hook to register a RouterWebHost on its
<lynx-view>, so `router.open` fails ("Native module spkPipe is not
registered"). The same example cross-navigates correctly in the web-shell
harness, which owns the page and installs the host + bridge.
Home.vue now awaits cross-navigation and, when no native host is available,
shows an inline notice instead of failing silently — so the limitation is
visible in the <Go> preview. Docs (en/zh) updated to state precisely why
in-heap works but cross-page needs device/harness.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Huxpro
force-pushed
the
claude/vue-router-sparkling-nav-7a7fy7
branch
from
July 12, 2026 23:26
192bc70 to
de712ee
Compare
…MpaPreview>)
go-web's <Go> previews one bundle with no native bridge, so cross-page
(MPA) navigation can't run in it. This adds a working, no-go-web-change
alternative: <MpaPreview> opens the Sparkling web shell in a NEW TAB, where
the full router.open -> new native container flow runs live in the browser.
A new tab (not an inline iframe) is deliberate: the shell owns the whole
page, so the browser's own back/forward buttons drive its MPA history stack,
instead of being trapped inside a frame.
- sparkling-web-shell: load example bundles from a runtime `?base=<url>` (so
one static build serves any example), and build with a relative assetPrefix
(WEBSHELL_ASSET_PREFIX=auto) so it can be dropped under any deploy base;
publicDir is dev-only (copyOnBuild: false) so the static build carries no
example bundles.
- website: scripts/build-mpa-preview.mjs builds the shell into
public/mpa-preview/ (best-effort; wired into dev/build via prepare-previews).
New global <MpaPreview> component renders a launch card + new-tab link
(withBase). public/mpa-preview/ is gitignored (generated).
- docs (en/zh): the Vue Router example's cross-page section links to the live
demo and explains the browser-back behavior.
Verified in a browser: from the launch link, tapping push('/users') stacks a
second <lynx-view> (1 -> 2), the top container is the users bundle at depth 1,
and the browser BACK button pops it back to main — real MPA on the web.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
Huxpro
force-pushed
the
claude/vue-router-sparkling-nav-7a7fy7
branch
from
July 13, 2026 03:41
ba9b877 to
b9028ac
Compare
The web shell swapped <lynx-view> containers instantly. Add native-style
page transitions: on router.open a new container slides in from the right
(iOS-like ease-out); on router.close / browser back the top container slides
back out, revealing the one beneath. Replace does a cross-fade. Honors
prefers-reduced-motion (and can be disabled per-navigation).
- sparkling-navigation web host: thread `animated` through RouterOpenOptions
and a new RouterCloseOptions (read from the router.open/close payload,
mirroring the native `animated` option). Undefined lets the host default.
- sparkling-web-shell: transform-based slide transitions in push/pop and in
the browser back/forward reconciler; the shell animates by default
(reduced-motion aware). No example changes needed — cross-page navigation
animates out of the box.
Verified in a browser: tapping push('/users') slides the new container in
(sampled transform 100vw -> 0 with deceleration), it settles to none, and the
browser back button slides it out and pops the stack.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARyWgB3243gQx1exWK61Qb
5 tasks
5 tasks
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.
Add multi-environment build config (web + lynx) to rspeedy so the
playground and template can produce browser-compatible bundles alongside
native ones. Web bundles output to dist/web/ and are served by a new
sparkling-web-shell package that renders them via .
and @lynx-js/web-elements 0.11.3
distPath for web output