startVideoRecording presents the camera for movies:
picker.sourceType = .camera
picker.mediaTypes = ["public.movie"] // CraftApp.swift:3802-3803
but the picker delegate — shared with pickImage / openCamera — reads only the still-image key:
if let image = info[.originalImage] as? UIImage, // CraftApp.swift:2705
let imageData = image.jpegData(compressionQuality: 0.8) {
…resolveCallback(… "mimeType": "image/jpeg" …)
} else {
rejectCallback(pendingCallbackId, error: "Failed to process image")
}
info[.originalImage] is nil for a movie pick. UIImagePickerControllerMediaURL is where the recording lands, and nothing reads it.
So the success branch has never executed. Every completed video recording takes the else and rejects with "Failed to process image" — a message describing an operation the user did not ask for. A cancelled recording and a successful one are indistinguishable from the page.
Not a migration regression: this is the shipping Swift behaviour and predates any Zig work. It is why startVideoRecording was passed over during the migration — there is no working contract to port, and inventing one would be a guess at what the reply should look like.
Fixing it means deciding the reply shape for a movie: a file URL, a data: URI (a recording is easily tens of MB — bridge_mobile_audiorec.zig caps its own base64 at 32 MiB for this reason), or a path plus metadata. That decision belongs with whoever owns the JS surface.
Related: 116 covers the gated arms that hang rather than reject.
startVideoRecordingpresents the camera for movies:but the picker delegate — shared with
pickImage/openCamera— reads only the still-image key:info[.originalImage]is nil for a movie pick.UIImagePickerControllerMediaURLis where the recording lands, and nothing reads it.So the success branch has never executed. Every completed video recording takes the
elseand rejects with"Failed to process image"— a message describing an operation the user did not ask for. A cancelled recording and a successful one are indistinguishable from the page.Not a migration regression: this is the shipping Swift behaviour and predates any Zig work. It is why
startVideoRecordingwas passed over during the migration — there is no working contract to port, and inventing one would be a guess at what the reply should look like.Fixing it means deciding the reply shape for a movie: a file URL, a
data:URI (a recording is easily tens of MB —bridge_mobile_audiorec.zigcaps its own base64 at 32 MiB for this reason), or a path plus metadata. That decision belongs with whoever owns the JS surface.Related: 116 covers the gated arms that hang rather than reject.