Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Filament Qr Scanner Laravel Package

emuniq/filament-qr-scanner

View on GitHub
Deep Wiki
Context7
v1.9.2

Changed

  • The plain rear camera leads the switcher. It is what an operator means by "the back camera", and on a phone whose browser only reports generic labels it is the only rear entry there is. The named lenses follow; the front camera still goes last.
v1.9.1

Fixed

  • The new aiming guide rendered as nothing at all: 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.
v1.9.0

Fixed

  • The aiming box was a rectangle. html5-qrcode sizes its overlay from the frame it asked for rather than the frame it got, so the moment a browser ignores the requested aspect ratio — Safari does — the square comes out stretched. The component draws its own guide now, aspect-ratio: 1, square by construction on every device, and hides the library's.
  • The switcher could still name the wrong camera. Safari does not always report a device id that appears in enumerateDevices(). When it does not, the running track's facingMode now decides front versus rear instead of a heuristic.

Changed

  • 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.
v1.8.2

Fixed

  • The torch label sat under its icon instead of beside it, and the zoom slider fell onto a second line. Cause: Alpine's 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.
v1.8.1

Fixed

  • The torch button lost its layout: 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.
v1.8.0

Fixed

  • The close button could scroll out of reach on a phone. The viewfinder plus the controls pushed the footer past the bottom of the screen. The modal footer is sticky now and the viewfinder is capped at 52vh, so closing the scanner is always one tap away.
  • A stale remembered camera cost a second permission prompt. Safari hands out fresh device ids every session, so the id saved last time is routinely invalid on the next visit: the open failed, the operator tapped again, and that second attempt was a second getUserMedia. It now falls back to the platform's rear camera by itself, in the same attempt.

Changed

  • The torch button carries its label again, next to the icon and on the same row as the zoom. Only the state text is gone — the colour says that.
v1.7.1

Fixed

  • The controls overlaid on the camera feed are styled inline instead of with Tailwind utilities. The package ships no CSS and a host app's Tailwind never scans its views, so classes like 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.
v1.7.0

Fixed

  • The switcher named the front camera while the rear one was streaming. A select bound to a device id that matches no option silently displays its first one. The active camera is now resolved against the list that is actually rendered, so what the operator reads is what is running.
  • The camera menu was in the browser's order, which puts the front camera first. It is ordered by how useful the lens is for scanning now: wide → rear → ultra wide → macro → telephoto → front.

Changed

  • The torch button no longer changes its label from "Light" to "Light on". It resized under the operator's thumb, and the colour and aria-pressed already carry the state. scanner.torch_on and scanner.torch_off are replaced by a single scanner.torch string.
  • Modal redesign: camera select and sound toggle share one toolbar, torch and zoom sit on the feed the way a camera app puts them, the reading counter is a badge on the viewfinder, and the footer carries one full-width action. The dialog is shorter, which is what a phone was short of.

Added

  • A test that parses 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.
v1.6.0

Fixed

  • The modal was wider than a phone screen, and the camera row was why. A flex row of buttons hands its full intrinsic width to the dialog no matter how it is clipped, so four lenses with names like "Ultra gran angular" pushed the modal — and its close button — off the side of the screen. The switcher is a <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.
v1.5.0

Fixed

  • Opening the scanner asked for the camera three times. It probed for permission with one 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.
  • A remembered camera that no longer exists used to fail the open every time. It is forgotten on failure so the next attempt asks the platform for whatever rear camera it has.

Changed

  • With no remembered camera the scanner starts from a 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.
  • Camera errors are mapped to the translated explanations from a single place regardless of which call failed.
v1.4.0

Fixed

  • The scanner opened on the wrong lens. It took whatever camera the browser enumerated last, which on an iPhone is one of the extra rear lenses: the telephoto cannot focus at the distance an operator holds a label, and the ultra wide spends its resolution on everything except the code. It now picks the main wide lens — plain wide → generic rear → ultra wide → macro → telephoto → front — and a remembered choice still wins over all of it.
  • Every rear lens was called the same thing. A phone with three rear cameras showed three buttons reading "Back", so the operator had to try all three. Each lens now gets its own name, and names that would still collide are numbered.
  • The close button was styled as destructive. Closing the scanner destroys nothing, and on a shop floor red reads as stop / abort / something broke.

Changed

  • Camera switcher is one row that scrolls sideways instead of wrapping. Four lenses used to take two rows of height from the viewfinder for a control the operator touches once.
  • The camera preview can no longer grow past the modal. A stream wider than the dialog used to drag it off the side of a phone screen, close button included.
  • 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.

Added

  • 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.
v1.3.0

Added

  • Torch and zoom controls inside the scanner modal, shown only when the running video track reports them — a laptop webcam has neither, a phone back camera has both. Both choices are remembered: a station in a dark corner should not need the torch switched on at every scan.
  • 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.
v1.2.0

Changed

  • The scanner's decision layer moved out of the inline 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.

Added

  • 20 tests for the scan session (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.
  • Coverage of the PHP side taken to 100%, with the paths that had none: aliases disabled through config, a 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.
v1.15.1

Fixed

  • The taller viewfinder was only taller: the picture sat in it with a black band underneath instead of filling it. html5-qrcode forces 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.
v1.15.0

Added

  • 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.

v1.14.0

Fixed

  • The modal grew when the picture arrived. The viewfinder had no height of its own — it took one from the video, which does not exist until the stream paints, so the whole dialog resized under the operator's thumb. The box now reserves its height from an aspect ratio before any video exists, and the reader fills it absolutely. Measured with the dialog settled: identical height with the camera off and with it running, a jump of 0 px.
  • A frame whose shape differs from the reserved box is fitted with 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.
v1.13.1

Fixed

  • The viewfinder was a transparent hole while the camera started. Its background came from a Tailwind utility, which a host app's build has no reason to have compiled — the package ships no CSS and nothing scans its views. Same failure as the hidden zoom label in v1.7.1; the background is inline now.
  • The "starting the camera" notice only appeared once the scanner was live, but the camera cannot start until the modal is laid out. During that gap the operator saw an empty box with no explanation. It shows from the moment the modal opens.

Added

  • Continuous autofocus is requested where the device offers it. A label held at arm's length in front of a camera parked on a fixed focus distance never resolves, and the operator ends up waving the phone about.
v1.13.0

Changed

  • The camera frame is taken as it comes. 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.)
  • The aiming guide is sized from the short side of the feed rather than the width, the same basis the scan window uses. On a landscape feed the width version put its corners outside the picture.
v1.12.0

Fixed

  • Opening the camera was slower than it needed to be. A flat 200 ms wait sat between the tap and the camera request, to let the modal lay out before the library measures the viewfinder — but a modal is normally laid out within one frame. It polls for the layout now, capped so it can never hang. Measured on an emulated iPhone: getUserMedia fires at ~15 ms instead of ~205 ms, and the picture is live in ~130–210 ms instead of ~370–480 ms.
  • A remembered camera used to force a second camera negotiation on every open, even when the platform had already handed over that very lens. The restart only happens when the kind actually differs.

Added

  • The viewfinder says it is starting instead of being a mute black box. The modal now appears instantly rather than after the camera is negotiated, so the wait moved to where the operator can see it — and an unexplained black square reads as slower than the same wait with a reason attached.
v1.11.2

Added

  • The README now states the rule behind the iOS camera prompt, quoted from a WebKit engineer: while capture is ongoing the grant lasts 24 hours; once capture stops it expires after a minute of inactivity. It explains why the grant has nothing to do with which lens is selected, and why parking the camera rather than releasing it widens the prompt-free window.
v1.11.1

Added

  • A Field notes section in the README: every problem this package ran into on real phones, whether it is handled for you or is a browser policy the code cannot reach, and in that case exactly what to do about it. Camera permission on iOS Safari, the wrong lens, three buttons all reading "Back", the aiming square rendering as a rectangle, a modal wider than the screen, an unreachable close button, false duplicate scans, a Blade alias shadowing an app component, and a CDN serving a stale asset during development.
v1.11.0

Fixed

  • Photo capture had every bug the scanner had already lost. It still opened on 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.
  • Opening no longer asks for a specific device. A 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.

Added

  • 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.

Changed

  • The package presents itself as what it is: camera components for Filament, QR scanning and quick photo capture, rather than a QR scanner that happens to ship a second component.

Removed

  • 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.
v1.10.0

Added

  • The translations are now covered by tests: every language must carry exactly the same keys, no string may be blank, every string a component renders must exist in every language, and the two shipped languages must not be a copy-paste of each other. A key added in one language and forgotten in the other used to render as the raw dotted path on a button — the sort of thing that ships unnoticed because the developer's own locale looks fine.

Removed

  • Four strings nothing rendered any more: 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.
v1.1.0

Added

  • 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.

Fixed

  • Corrected the v1.0.0 note about Panel::configureUsing. See below.
v1.0.0

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.

Added

  • <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.
  • Config file (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.
  • Test suite (Pest + Testbench), run locally with vendor/bin/pest.
  • NOTICE documenting the bundled Apache-2.0 library.

Changed

  • Duplicate detection tracks a last-seen timestamp per code instead of a single lastScannedCode. Two labels alternating in the camera frame no longer read as a duplicate.
  • Component aliases are skipped when the host app already owns that name. A Blade alias silently outranks an app's anonymous component, which made an app-level file look editable while the package's copy was what rendered.
  • Panel auto-registration moved to the provider's 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.
  • Scanned text is trimmed and empty reads are dropped.
  • Requires Filament v4, PHP 8.2+. The v3 claim was never covered by tests and has been dropped rather than left as an untested promise.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator