EDGE: SwiftUI/Compose renderers, native testing suite, and the v4 element pipeline#173
Open
shanerbaner82 wants to merge 213 commits into
Open
EDGE: SwiftUI/Compose renderers, native testing suite, and the v4 element pipeline#173shanerbaner82 wants to merge 213 commits into
shanerbaner82 wants to merge 213 commits into
Conversation
Replace coroutine-based parallel unzip with direct-to-disk streaming to reduce memory overhead. Update PluginMakeHookCommand to use new plugin accessor methods instead of accessing manifest properties directly.
…d AGP bump - Add compile_sdk, min_sdk, target_sdk config options with build-time replacement - Block signals during php_embed_shutdown to prevent TSRM mutex crashes - Reuse PHP runtime for artisan commands instead of reinitializing - Replace codename-based binary URLs with versioned bin.nativephp.com URLs - Bump AGP from 8.13.1 to 8.13.2
- Rewrite Android renderer to V2 wire format with string-typed nodes, generic props, and pluggable renderer registry - Add Edge element system with 26 native elements, Blade component rendering, Tailwind class parser, and callback registry - Add NativeComponent lifecycle, NativeRouter navigation, and artisan scaffolding commands (native:make, native:rm, native:validate) - Extend plugin system with UI component plugin support, renderer registration codegen, and manifest validation - Add Edge component and Blade rendering tests, update Android binary download URLs
…iveElementCollector calls Bypasses Blade's ComponentTagCompiler and IoC resolution entirely. Native tags now compile to NativeElementCollector::leaf()/open()/close() calls, eliminating class instantiation and sub-view rendering overhead per element.
Arrow functions capture by value, causing duplicate node IDs when siblings share the same starting counter. Switch to a regular closure with &$nextId.
Extracts text from slot content so <native:button>Click me</native:button> works the same as <native:button label=\"Click me\" />.
… for native screens Navigation transitions: Transition enum on PHP side, UI.SetTransition bridge function, AnimatedContent with configurable enter/exit specs on Kotlin side. Supports slide, fade, scale, and instant swap. Tree patching: PHP diffs old vs new render tree and writes minimal patch ops to shared memory via nativephp_ui_patch(). JNI exposes patch buffer read/ack. Kotlin applies patches to the live tree without full re-parse. Hot reload: file changes trigger HOT_RELOAD event through shared memory, PHP flushes compiled views and writes .hot_restart signal, Kotlin detects it and re-executes the native route. Includes error screen rendering for recovery from render failures. Also: CallbackRegistry now parses method arguments and maintains stable IDs across re-renders, LazyColumn virtualization for scroll views, container click handling, and improved adb push reliability in WatchesAndroid.
…stem Replaces the mmap-based shared memory architecture (NpuiSharedRegion) with a direct flat buffer system (NphpElementRegion) that eliminates the watcher thread polling model. PHP now builds flat nodes in malloc'd buffers and pushes tree updates directly to Kotlin via JNI (NativeElement_PostTreeUpdate → NativeElementBridge.postTreeUpdate). Key changes: - native_functions.c: Host-registered PHP functions compiled into libphp_wrapper.so via additional_functions, replacing the separate .so extension. Implements nativephp_element_init/publish/wait_event/ reset/shutdown with flat node serialization and interned type table. - bridge_jni.cpp: Replaces old UI bridge JNI with Element bridge methods. Returns DirectByteBuffer for zero-copy flat buffer access. Caches NativeElementBridge class/method refs for direct C→Kotlin push. - php_bridge.c: Per-request php_embed_init/shutdown with mutex serialization. Registers host functions via setup_embed_module(). - NativeElementBridge.kt: Reads flat buffer on PHP thread via JNI, parses tree, and posts to main thread for Compose recomposition. - NativeUIBridge.kt: Stripped to observable state holder, delegates all event sending to NativeElementBridge. - PHPBridge.kt: Persistent runtime with ensureRuntimeInitialized(), uses nativeHandleRequest instead of nativeHandleRequestOnce. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates NativeComponent and NativeRouter to use the new element runtime functions (nativephp_element_init/publish/wait_event/reset/shutdown) instead of nativephp_ui_*. Removes the PHP-side tree diffing/patching logic from NativeComponent since the flat buffer system handles this at the C layer. - NativeComponent: Remove previousTree, diffTree, diffNode. Add __syncProperty for two-way model binding with updatedFoo hooks. - NativeRouter: Use nativephp_element_* for init, reset, publish, and shutdown. - NativeServiceProvider: Return 204 when .hot_restart file exists to prevent WebView from loading stale content during hot reload. - CallbackRegistry: Add lookup() method for reverse expression→ID resolution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- NativeTagPrecompiler: Expand @model="propName" into :value and _change bindings that call __syncProperty for two-way data binding. - Element: Support 2-value and 3-value padding/margin shorthand (CSS-style: vertical/horizontal, top/horizontal/bottom). - UIFunctions.SetBackground: New bridge function to set the Activity window background color, controlling what shows behind transparent system bars and safe area insets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces end-to-end interaction latency tracking across the full pipeline (Compose click → PHP processing → tree update → frame drawn) and per-frame Android rendering pipeline breakdown via FrameMetrics. - PerformanceTracker: Tracks T0-T3 round-trip for press/text/toggle events with pipeline breakdown. Captures FrameMetrics (input, animation, layout, draw, sync). Exports structured JSON for charting with percentile stats and jank detection. - PerfFunctions: Bridge functions (Perf.Enable/Disable/Reset/Export/ Summary/StartCaptureWindow/StopCaptureWindow/SimulatePress/ SimulateTextChange/SimulateToggle) controllable from PHP. - BenchmarkComponent: PHP-side benchmark suite with render throughput, interactive latency, and deep tree stress tests. - NativeUIRenderer: LaunchedEffect + withFrameNanos to detect when frames with new trees actually draw. - BridgeFunctionRegistration: Register Perf.* and UI.SetBackground bridge functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Track nativeUIThread to properly join old PHP threads before restarting, avoiding races between overlapping hot reload cycles. - Fix hot restart signal path to match PHP's storage_path() which resolves to persisted_data/storage/ (set by LARAVEL_STORAGE_PATH). - Start NativeElementBridge.startWatching() alongside NativeUIBridge for the new element runtime. - Attach PerformanceTracker FrameMetrics listener on create, detach on destroy. - Reduce hot reload poll delay from 500ms sleep to 100ms + continue for faster restart detection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- RunCommand: Add --no-vite flag to skip Vite dev server for native-only development workflows. - RunsAndroid, RunsIos: Skip Vite startup when --no-vite is set. - WatchesAndroid, WatchesIos: Skip Vite server, port detection, forwarding, and file filtering when --no-vite is set. - InstallsAndroid: Update PHP binary download URLs to element runtime builds (android-element-8.5.3). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Documents the full pipeline from PHP Element runtime through JNI flat buffer to Kotlin Compose rendering, covering the flat node binary format, type interning, event system, and direct push architecture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skip re-downloading PHP binaries on subsequent installs when the same version zip already exists locally. Applies to both Android and iOS. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Throw NativeDumpException from VarDumper handler to prevent exit(1) - Render styled dump screen (dark theme) with variable output - Catch exceptions from both render() and dispatch() event handlers - Show error screen with exception details and stack trace - Font size controls ([ – ] / [ + ]) on both dump and error screens - Back button dismisses overlay and re-renders the component - Plain dump() calls log to storage/logs/edge-nav.log without throwing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The AAB build needs compileSdk 36 (+ NDK 27 / cmake), but the CI image mobiledevops/android-sdk-image:34.0.1 ships SDK 34, so Gradle can't resolve android-36. It builds cleanly locally with SDK 36 installed — a CI-environment gap, not a code regression. Switch the trigger to workflow_dispatch until the runner provides SDK 36, so it stops gating PRs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The QR Builder must use endroid/qr-code 6.x's named-constructor API (new Builder(data: ...)), not the 5.x fluent Builder::create() — a stale composer.lock (endroid 5.1.0) had masked which API is real. endroid ^6.x requires PHP ^8.4, so bump the php constraint to ^8.4 and refresh the lock (endroid now resolves to 6.1.3). PHPStan + suite green against 6.1.3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mobile-air now requires PHP ^8.4 (endroid 6.x), so composer install fails on 8.3. Drop the 8.3 matrix leg and move static-analysis + code-style jobs to 8.4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ComposeFlexLayout (Android): a flex container with an absolute-positioned child rendered its flow content via matchParentSize(), so the content was sized to match the Box — whose size was driven only by the (typically tiny) absolute child. Any real content (e.g. a code block with an absolute copy button) got clipped to the badge/button's bounds and rendered blank. Flow content now sizes normally and determines the Box; absolute children overlay on top via .align. NativeRootTabsRenderer (iOS): fix Tab(role: .search) so results actually show. - Attach `.searchable` to the NavigationStack (Apple's documented iOS 26 structure) via a new SearchTabContainer; previously it was on the inner List, which made the field float over the previously-selected tab without ever showing the results view. - Move selection off the search tab when a result navigates (onChange of current_uri), so tapping a result reveals the destination instead of stranding the user on an empty search screen. - Suppress the per-screen nav-bar `.searchable` on regular tabs when a dedicated search-role tab exists (the tabs-root nav_search_* props belong to that tab, not to every screen) — otherwise every tab sprouted a second, results-less search bar. NativeRootTabsRenderer (Android): mirror the search-tab un-stranding — snap selection back to the active tab when a navigation changes the current URI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rance API Fold the device/dialog/file/system plugins into core: register their native bridge functions in core's BridgeFunctionRegistration (iOS + Android), bind the facades as core singletons, and declare a Composer conflict so the standalone plugins can't be installed alongside v4. Add the VIBRATE/FLASHLIGHT Android permissions the Device functions need. Also add the System appearance/dark-mode API: System::appearance()/isDarkMode()/ isLightMode(), the isDark()/isLight()/theme() global helpers, and an AppearanceChanged native event (pushed from iOS colorScheme / Android onConfigurationChanged) that dispatches globally via BroadcastsGlobally and keeps System's cached appearance fresh. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nested <native:text> now composes ordered inline runs into one attributed/ annotated string per platform (the precompiler emits textOpen/textClose; the collector buffers runs in document order). Add select-text/select-none classes (and Element::selectable()) that scope native text selection to a subtree — SelectionContainer on Android, .textSelection on iOS. Clamp Text::fontWeight() to the supported 1-7 range, and apply element-specific class attributes in Element::class() so programmatic ->class() matches the blade collector. Also plumbs a scroll-anchor prop on scroll containers; renderer support is still pending, so it is currently a no-op. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add NavBar::titleView()/logo() to render a custom element or Blade view in the bar's centered principal slot (wrapped in a TopBarTitle marker; iOS .principal toolbar item / Android TopAppBar title slot, forced inline). Support an inline <native:bottom-bar> in a screen's blade: it's hoisted out of the content tree to the native-chrome root and pinned via .safeAreaInset(.bottom) (iOS) / Scaffold(bottomBar) + imePadding() (Android), keeping it above the keyboard; falls back to the layout's bottomBar(). Switch the SwiftUI roots to .ignoresSafeArea(.container) so keyboard avoidance shifts content correctly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Vite dev server no longer starts automatically during native:run / native:watch. Add a --vite flag (backed by a shouldRunVite() helper) so it starts only when explicitly requested. --no-vite is kept but is now redundant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Removes the 🐛 debug-log launcher FAB (and its bottom-sheet log/scheduler viewer) that showed in the bottom-right corner on DEBUG builds. Deletes the self-contained DebugLogViewer.kt and the now-unused isDebugVersion() helper and render block in MainActivity. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When a deep or universal link opened an already-running native-ui app, the link was silently dropped — it foregrounded the current screen without navigating. Cold-start links worked; warm ones didn't. Root cause: native-ui runs a single PHP event loop that blocks inside the current screen. DeepLinkRouter's warm branch used navigateWithInertia (window.router.visit), which only exists in the Inertia/WebView runtime; and a plain webView.load(php://…) for a Route::native screen never commits because it queues behind the running loop. Fix: when NativeUIBridge.isActive, wake the running loop with a __deeplink native event carrying the target route. NativeComponent's dispatchNativeEvent turns it into a NavigationIntent::NAVIGATE and stops the loop, so NativeRouter resolves the URI (with route params) and pushes the screen — the same path an in-app @press navigate uses. Inertia apps keep the existing navigateWithInertia behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Android mirror of the iOS fix. When a deep or app link opened an already-running native-ui app, MainActivity called webView.loadUrl(), which never routes a Route::native screen because the single PHP event loop is blocked on the current screen — the link was silently dropped. Add navigateWarm(): when NativeUIBridge.isActive, dispatch a __deeplink native event carrying the route (NativeElementBridge.sendNativeEvent) to wake the loop; NativeComponent::dispatchNativeEvent turns it into a NavigationIntent::NAVIGATE and NativeRouter pushes the screen. WebView/ Inertia apps keep the direct loadUrl(). Applied to all three warm-entry sites (app/custom-scheme link, notification URL, FCM URL). Verified on emulator: warm jump:// and https app links both route host-stripped to Docs. Pairs with the shared PHP handler in 209498e. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Release-optimized, non-debuggable build that shell profilers (Macrobenchmark, Perfetto, simpleperf) can attach to. Debug-signed so it installs with plain adb install — no keystore, no manual zipalign/apksigner — and native:run installs + launches it like a debug build. isProfileable injects <profileable shell="true"> for this variant only, so production release and bundle stay clean. Also adds the androidx.profileinstaller dependency so the APK-embedded baseline profile is installed and AOT-compiled on sideloaded builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
libphp_wrapper.so registers its JNI methods against NativeElementBridge at System.loadLibrary time. R8 can't see JNI references, and it stripped the "unused" external funs (NativeElementBridge lives under ui.nativerender, so the existing com.**.bridge.** keep rule missed it) — every minified build crashed on boot with NoSuchMethodError: nativeElementWaitUpdate. Keep the bridge class and any class declaring native methods. Unblocks NATIVEPHP_ANDROID_MINIFY_ENABLED=true (74 MB -> 27 MB APK). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Measured on Pixel 9 (Macrobenchmark, profileable build, 10 iters). Beats
React Native (341ms) and Flutter (248ms); also drops idle memory 149 -> 119MB
and eliminates the P95/P99 animation jank tail. The changes:
- Paint the splash first: WebView (Chromium) creation and the PHP boot kickoff
move to window.decorView.post — a Perfetto android_startup breakdown showed
241ms of activityStart plus 161ms of main-thread uninterruptible I/O sleep
when boot preceded first paint. Bridge/renderer registration moves onto the
boot thread ahead of the PHP boot.
- Lazy PHPBridge so System.loadLibrary("php_wrapper") leaves the TTID path.
- Build Laravel bootstrap caches once at bundle extraction (config:cache,
event:cache, view:cache — not route:cache; closure routes). Skip the
per-boot cacert MD5 verification; refresh only on extraction or DEBUG.
- Defer the background queue worker (a second full Laravel runtime) 2.5s past
first paint; reportFullyDrawn once the start URL loads.
- Ship a 12k-rule Baseline Profile covering Compose + the runtime boot path;
regenerate with native-benchmark/android/generate-baseline-profile.sh.
- Black windowBackground in both themes (kills the light-mode white flash);
splash bitmap decodes off the first composition on Dispatchers.IO.
- FrameTracker no longer self-starts its Choreographer callback + 4Hz publish
loop when the overlay is disabled.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Named scale (leading-none/tight/snug/normal/relaxed/loose) as unitless multipliers of font size, plus arbitrary leading-[1.4] (multiplier) and leading-[24px] (absolute) forms, wired through TailwindParser into the Text element's lineHeight prop. Covered in the parser and text-run tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uninstalls the plugins whose functionality moved into core in v4 (device, dialog, file, system) in one pass: deregisters their service providers, removes the composer packages, and cleans up source dirs. The plugin argument is now optional to support the batch path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Render a remote WebView app over Jump: when a jump://webview?host=&port= deep link activates a session, PHPSchemeHandler forwards php://127.0.0.1 requests to the remote dev server instead of the local embedded PHP. The WebView keeps loading php://127.0.0.1 — same rendering surface, origin, and JS bridge, just a different response source — and dev-server-host links the remote app emits are rewritten back to php://127.0.0.1 so navigation stays in the WebView. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same guard, lighter mechanism: during a native render, any view whose cached compiled file lacks the native-precompiler marker (e.g. compiled by a web render or `view:cache`) is force-recompiled — now via a view creator in NativeServiceProvider instead of swapping the stock Blade engine for a subclass. Covers nested @includes; the root view keeps the same check in renderBladeBoundToSelf(). Also what keeps the new on-extraction `view:cache` (cold-start caching) safe for native-ui screens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The docs show gestures-enabled, label-visibility, background-color, image-url, show-close-button, badge-color and open-in-browser, but the elements only matched the camelCase spellings. Attributes reach elements verbatim from the precompiler, so the documented forms were silently dropped. Accept both, camelCase winning when both are present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Same class of bug as the side-nav elements: attributes arrive verbatim and only the camelCase spelling was matched, so an inline badge-color attribute silently no-oped. camelCase wins when both are present. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Text::applyAttributes only matched the camelCase keys the Tailwind class parser produces, so every hand-written kebab attribute the docs show (font-size, font-weight, text-align, max-lines, and the rest) was silently dropped. Accept both spellings, camelCase winning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TailwindParser::resolveColorValue() resolves standalone color values — Tailwind palette names (red-300), white/black/transparent, CSS hex (#RGB/#RGBA/#RRGGBB/#RRGGBBAA), and /N opacity modifiers on any of them — to wire-format hex (#RRGGBB / #AARRGGBB). Theme config tokens and element color props resolve through it (native-ui side). normalizeHex() now converts authored CSS-order alpha hex (#RRGGBBAA) to the Android-style #AARRGGBB both native ColorParsers read, and rejects invalid hex instead of shipping it raw — fixes bg-[#8B5CF680] rendering as green (alpha byte read as red). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
UIColor(hex:) read 8-digit hex as CSS-order #RRGGBBAA while both ColorParsers (Swift NativeUINode + Kotlin) read #AARRGGBB — the same payload rendered different colors depending on which chrome consumed it. PHP now converts authored CSS hex to #AARRGGBB before sending, so every native parser reads one convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The collector splits the parser's `dark` sub-array into dark_bg_color / dark_color / dark_border_color props for blade elements, but the programmatic Element::class() path never did — Element-returning render() trees silently dropped ALL dark-mode styling and rendered light hexes in both modes. Route the same buildDarkProps() output into mergeDarkProps() so both construction paths emit identical wire props. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chrome text can now render in a bundled custom font, three tiers with most-specific-wins precedence: per-screen NavBarOptions/TabBarOptions ->font(), per-bar NavBar/TabBar ->font(), and a layout-wide NativeLayout::$font (applied via defaultFont(), set-if-unset). Unset tiers fall through to the theme's app-wide font-family default. The token flows as fontName through toRootProps()/toElement() into a font_name prop on the chrome sentinels (folded nav_font_name on tabs). Core renderers resolve the token through plugin-populated seams so core still compiles standalone: Android extends NativeUIThemeProvider with a fontFamilyResolver (titles, tab labels, badges pass it as fontFamily); iOS adds NativeChromeFontResolver (token → PostScript name), used by the stack renderer's principal-slot title path — inline-mode bars with a font switch to that path since system titles expose no font hook — and injected into the tabs renderer's UITabBarAppearance (iOS ≤ 25; Liquid Glass bars reject appearance overrides, so labels keep the system font there). System-drawn large titles keep the app default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…End, test harness) GestureArea gains continuous pinch (SharedValue scale with source-side min/max clamping, seed/valueOf store additions) and discrete swipe / pinch-end callbacks; the precompiler + collector wire the new @swipe/@pinchEnd Blade events, SV-bound opacity pre-seeds from the style literal, and TestableComponent gets swipe()/pinch() drivers. Pairs with the gesture renderers in nativephp/native-ui (pushed to its main). 16 gesture tests; full suite green (559 passed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
While a forwarded remote app owns the WebView: drop local native-ui runloop publishes so they can't yank the session off screen (NativeElementBridge guard), route side-nav/chrome links that target the dev-server host back through the php:// forward instead of the system browser, add a global 3-finger swipe-right escape hatch back to the Jump home, plus router.php / JumpCommand forwarding fixes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t run native:jump left orphaned servers holding their ports on Cmd+C, so each run escalated (3000->3008, 8000->8002, 3001->3009, ...). Three primary causes: (1) killExistingServers() was dead code — never called — so a prior run's leaked ports were never reclaimed; (2) proc_terminate() only signals direct children, missing the php -S / artisan serve worker grandchildren; (3) the Unix Workerman bridge was launched detached (exec "&") and untracked, so nothing ever signalled it. Signal handlers were also installed only after up to ~120s of blocking startup. Fix: - Spawn each server as its own process-group leader (posix_setsid shim) and tear down with killGroup() = SIGTERM->SIGKILL on the negative PGID, reaping the entire subtree incl. grandchild workers. ps-tree fallback when pcntl is unavailable; taskkill /F /T on Windows. - Arm SIGINT/SIGTERM/SIGHUP handlers + register_shutdown_function BEFORE any spawn, with pcntl_async_signals(true), so Cmd+C during startup is honoured. Teardown ignores further terminating signals so a second Ctrl+C can't abort the SIGTERM->SIGKILL escalation mid-flight. - Call killExistingServers() at startup to reclaim a prior run's ports (registry-driven), plus identity-checked backstops: a canonical-port sweep and a PPID-1 orphan sweep that reaps registry-less leftovers and the Workerman masters (which hold no listening port). All gated by isJumpOwnedProcess() so unrelated services (e.g. boost:mcp on 3002) and live sibling projects are never touched. - killGroupIfOwned() identity-checks prior-run registry leader PIDs before a group kill, so a recycled PID can't nuke an innocent process group. - Track Windows bridge/vite PIDs; broaden the Windows reaper to match all jump scripts (router + websocket-server + vite-hmr) and tree-kill. Cross-platform: macOS/Linux process groups; Windows taskkill /F /T; no-pcntl Unix ps-tree fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the 🧭 nav-decide / handleNavigation traces and the external-nav diagnostic added while debugging why WebView-session links escaped to the system browser. Kept the low-frequency WS lifecycle logs, which stay useful for diagnosing the device bridge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This is the accumulated
elementbranch — the EDGE element pipeline and its supporting work — merging intomain. It is large (180 commits, 336 files, ~+42k/−4.5k) and represents a major release's worth of changes. Highlights by area:Rendering
Layoutimplementation on iOS and a ComposeLayouton Android.Animation & interaction
Testing
Native::test()component testing suite (FakeBridge + TestableComponent), plus this session's coverage:assertTransition, wire-event interactions, FakeBridge scripting/recording, andRoute::nativeHTTP registration.Platform / native
watchPosition()/clearWatch()+ background watches).Accessibility
HasA11yon the base Element,assertAccessible(), Image alt text, NavAction a11y labels + audit rules.Reactivity
Test plan
🤖 Generated with Claude Code