Skip to content

Resolve relative image src for the device under Jump - #361

Open
simonhamp wants to merge 1 commit into
mainfrom
image-src-jump
Open

Resolve relative image src for the device under Jump#361
simonhamp wants to merge 1 commit into
mainfrom
image-src-jump

Conversation

@simonhamp

Copy link
Copy Markdown
Member

What

A relative image srcimg/logo.png — means "an asset I shipped in public/". Who resolves that depends on where PHP is running, and until now we assumed it was always the device:

  • On device, PHP and the renderer share a filesystem, so the path travels over the wire untouched and the native renderer resolves it against the app's public/ directory.
  • Under native:jump, PHP is on the developer's machine and the renderer is on a phone. public/ only exists on the dev machine, so the path resolves to nothing on the device and the image silently fails to load.

native:jump is already serving public/ over HTTP, and the router forwards the phone-reachable Host header to Laravel — so asset() yields a URL the device can actually fetch, and the renderer treats it as any other remote image.

How

New Edge\ImageSource::forDevice() centralises the rule, wired into both entry points on the Image element (the <native:image src="..."> tag path and the fluent Image::make() path). Plugins that accept an image path should route through it too, so a src means the same thing wherever it's written.

Anything already pointing somewhere real is passed through untouched:

  • absolute paths (/var/mobile/.../photo.jpg) — camera captures arrive this way
  • anything carrying a URI scheme (https:, file:, data:, content:) — gallery picks arrive this way

Rewriting either would break them.

System::runningInJump()

The Jump-mode check needed a home. It landed on System, alongside the other "where am I running" methods (isIos(), isAndroid(), isMobile()) — it's the same question one scope out: after "which platform", "which machine". Being on the facade also makes it discoverable for plugin authors, who are part of the intended audience.

It's static and a bare getenv() deliberately: ImageSource::forDevice() runs per image on the device render path, where this must not cost a container resolve or a bridge round-trip.

NativeComponent::runLoop() had the same check inlined, along with a copy of the reasoning. Both now collapse into the one method, so the JUMP_BRIDGE_PORT gate — and the non-obvious note about why it can't be function_exists('nativephp_call'), which is defined on both sides in Jump mode — is documented in exactly one place.

Testing

10 new tests in tests/Unit/Edge/ImageSourceTest.php covering both modes, the ./ prefix, every pass-through case, and both Image entry points. Full suite green: 904 passed.

Noticed while here — not addressed

Two pre-existing issues in untouched files, flagged for a separate look:

  • composer analyse crashes at the default 128M memory limit. ./vendor/bin/phpstan analyse --memory-limit=2G gets a real result; may be worth baking into the composer script.
  • With the limit raised, 6 errors surface in Concerns/ChecksLatestBuildNumber.php and Concerns/RunsAndroid.php — references to command options/arguments that aren't declared (api-key-path, api-key-id, api-issuer-id, watch, udid). They read as real bugs rather than false positives.

🤖 Generated with Claude Code

A relative image `src` means "an asset in `public/`". On device PHP and
the renderer share a filesystem, so the path travels untouched. Under
`native:jump` PHP runs on the developer's machine and the renderer is on
a phone, where that path resolves to nothing — so rewrite it to a
phone-reachable `asset()` URL before it goes over the wire.

ImageSource::forDevice() centralises the rule so a `src` means the same
thing wherever it's written, including from plugins. Absolute paths and
anything carrying a URI scheme pass through untouched — camera captures
and gallery picks arrive that way and rewriting them would break them.

Adds System::runningInJump() as the home for the Jump-mode check, next
to the other "where am I running" methods, and folds NativeComponent's
inline getenv() into it so the JUMP_BRIDGE_PORT gate and its rationale
live in one place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@simonhamp
simonhamp marked this pull request as ready for review August 22, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant