border-width: 0 was written
after the per-side longhands in the same declaration, and the shorthand reset
them. Order corrected, with a test that reads the four corners back.aspect-ratio: 1, square by
construction on every device, and hides the library's.enumerateDevices(). When it does not, the
running track's facingMode now decides front versus rear instead of a
heuristic.scanner.qrbox_ratio defaults to 0.7: the scan window is sized against the
shorter side of the live viewfinder instead of a fixed pixel count. A fixed
box is a small target on a monitor, an oversized one on a phone, and
rectangular whenever the rendered aspect differs from the requested one. Set
it to null for the old fixed qrbox.x-show shows an element by
REMOVING its inline display, so display:flex and display:inline-flex
written in the style attribute were silently dropped the moment the
controls became visible. x-show now only ever lands on wrapper elements;
the elements that need a display of their own no longer carry it.x-bind:style with a string REPLACES the
static style attribute rather than merging into it, so the button kept only
its colours and rendered as a narrow box with the icon and label stacked. It
binds an object now, and the label sits beside the icon on the same row as
the zoom.getUserMedia. It now falls back to the
platform's rear camera by itself, in the same attempt.sr-only or bg-gray-950/60 may simply not
exist there — which is how the hidden zoom label rendered as visible black
text over the feed, and the gradient behind the controls did not render at
all. Nothing about the component now depends on the host's build.aria-pressed
already carry the state. scanner.torch_on and scanner.torch_off are
replaced by a single scanner.torch string.x-data the way a browser does — value ends at the first
quote — so a stray " in a comment or a translation can never again close
the attribute early and dump the component's javascript onto the page.<select> now: it has a width of its own and truncates its own
text. Measured on an emulated iPhone with four cameras: dialog 361 px inside
a 393 px screen, no horizontal overflow, close button reachable.min-w-0 on the modal body and on the torch/zoom row, so no future control
can force the dialog wide again.getUserMedia, enumerated cameras with a second, and
started the camera with a third. iOS Safari treats every one of them as a
reason to ask the operator again. There is now exactly one request — the one
that actually starts the camera — measured, not assumed.facingMode: environment
constraint and lets the platform pick its default rear lens, then fills the
switcher from enumerateDevices() — which needs no stream of its own.aspect-ratio prop and scanner.aspect_ratio config. The square default is
unchanged; null gives the sensor's native frame, which is more scanning
area but is what caused the overflow above on iOS Safari.resources/dist/camera-picker.js — naming and choosing among lenses, apart
from Alpine and the DOM, with 22 tests at 100% line, branch and function
coverage against the labels real iPhones, Android phones and laptops report
in both English and Spanish.qrbox-ratio prop and scanner.qrbox_ratio config: size the scan window
against the shorter side of the live viewfinder instead of pinning it to a
pixel count that is small on a monitor and huge on a phone. A ratio wins over
qrbox-size; out-of-range values throw at render time.scanner.native_decoder config, making explicit what the library already did
silently: decode through the browser's own BarcodeDetector where it exists
(Chrome and Edge, Android included) and fall back to the bundled JavaScript
decoder elsewhere. Set it to false to always take the JavaScript path.aria-live on the last-read banner, aria-pressed on the torch toggle, and
motion-reduce on the success flash. The flash and the beep were the only
feedback a scan had.x-data blob into
resources/dist/scan-session.js, loaded on demand next to html5-qrcode.
Deciding whether a decoded code is a new read, the same code still in frame,
or a deliberate re-scan is where both duplicate-detection bugs came from; as
a plain module apart from Alpine and the DOM it can be tested directly. The
Blade component now only carries out what it decides. Behaviour is unchanged.npm test, no dependencies — node --test),
at 100% line, branch and function coverage. They pin the things that broke
before: two labels alternating in frame, a code held in front of the lens for
a minute, reopening after a rejection without firing a second one.components config that is not a list, blank
entries in a symbology list.composer test / composer test:coverage (gated at 100%) and
npm run test / npm run test:js:coverage.<source> in phpunit.xml, without which pest --coverage cannot collect.position: relative on its own container, which wiped the absolute positioning off it,
so the video was resolving a percentage height against no height at all. The
reserved box now belongs to a wrapper the library never touches. Measured:
the video fills 313×313 in a 313 px box.scanner.viewfinder_ratio and a viewfinder-ratio prop: the shape of the
preview box, independent of the frame the camera sends. It defaults to a
square, which is a third taller than the 4:3 frame a phone hands over, and
the picture fills it by cropping the sides rather than sitting in dark bars.
3 / 4 is taller still; 4 / 3 matches the frame exactly and is shortest.
Measured: the viewfinder goes from 235 px to 313 px on a 393 px-wide screen,
and the dialog still does not resize when the picture arrives.The crop is display only — the sensor keeps handing the decoder its whole frame — and it is centred, as are both the scan window and the aiming guide.
object-fit: contain — thin dark bars instead of a resize, and the whole
frame stays visible, so what the operator aims at is what gets decoded.aspect_ratio defaults to null
instead of 1.0. Forcing a square made the browser crop and scale the sensor
to reach a shape it was not built for: measured on an emulated iPhone, a
640×480 sensor became 480×480 with resizeMode: crop-and-scale — a third of
the field of view thrown away, and a viewfinder 78 px taller for less
picture. On a phone that height is the scarcest thing in the dialog.
(The reason this was reverted in v1.4.0 turned out to be wrong: the modal
overflow it was blamed for came from the camera button row, fixed in v1.6.0.)getUserMedia fires at ~15 ms instead of
~205 ms, and the picture is live in ~130–210 ms instead of ~370–480 ms.cameras[cameras.length - 1] — whichever lens the browser listed last, on
an iPhone usually the telephoto, which cannot focus at the distance an
operator holds a label — still fired three getUserMedia calls per open, and
still showed raw device labels, so a phone with three rear cameras offered
three identical buttons. It now shares the scanner's camera machinery: one
request per open, CameraPicker for naming and choosing, a <select> that
cannot widen the dialog, and a sticky footer.deviceId constraint
fails with OverconstrainedError as soon as the remembered id goes stale —
Safari reissues them every session — and a failed getUserMedia is a wasted
permission prompt. Both components open on facingMode and apply a remembered
camera afterwards, on a permission already granted.scanner.keep_alive (45 seconds): closing the scanner modal parks the
camera instead of releasing it, so reopening costs no getUserMedia at all —
which on iOS Safari is what being asked for the camera again and again
actually is. It is released after the window passes so the recording
indicator does not sit on for the rest of a shift. 0 releases immediately.photo.error_no_camera, photo.error_detecting and photo.error_generic,
which nothing renders since the error handling was consolidated. Caught by
the translation guard added in v1.10.0, on its first day.scanner.last_scan,
scanner.error_no_camera, scanner.error_detecting and
scanner.error_generic. Dead strings are a tax on whoever adds a language.
scanner.rejected_title, scanner.rejected_generic and scanner.acknowledge
stay, documented as strings for the host page's rejection overlay.formats prop and scanner.formats config to limit which symbologies the
decoder attempts. The default is still all seventeen, which is also the
slowest; narrowing it to what your labels carry is the cheapest frame rate
you can buy on a low-end tablet. Unknown names throw at render time instead
of reaching the decoder as undefined, where the camera silently stops
recognising anything.Panel::configureUsing. See below.First stable release. The pre-release dev-main snapshot from March was missing
most of what production had grown since; this is the whole thing.
<x-photo-camera-capture> — camera photo capture that hands the server a
downscaled base64 JPEG, plus the HasBase64PhotoCapture trait to decode and
store it. writeBase64Photo() is overridable for two-phase / queued uploads.scanner-reset and scan-resume window events, so a hosting page can clear
the scanned-code memory when the working context changes and reopen the
camera after the operator acknowledges a rejection.wire-action-args prop — a scalar or list spread into the Livewire call, for
actions that need to know what the scan is being checked against.filament-qr-scanner.php): panel auto-registration, component
aliases, decoder tuning, duplicate window, photo disk and encoding.html5-qrcode is bundled and served from the app's own domain through
FilamentAsset, loaded on demand. Panels no longer depend on a CDN reachable
from the shop floor. scanner.script_url restores the old behaviour.vendor/bin/pest.NOTICE documenting the bundled Apache-2.0 library.lastScannedCode. Two labels alternating in the camera frame no
longer read as a duplicate.register(). It worked from
boot() in a real application — Filament::registerPanel() defers panel
construction until PanelRegistry is first resolved, which is after every
provider has booted — but not under Orchestra Testbench, where the registry
resolves during setup. register() is correct in both.How can I help you explore Laravel packages today?