@feugene/granularity-media

v0.7.12026-08-27

Fixed

  • Development warnings never reached the browser: the __GR_DEV__ guard included a typeof process check, and process is undefined in the browser, so the whole expression collapsed to false in development too. The guard now matches the core package. Production bundles are unaffected — the branch is still dropped.

v0.7.02026-08-27

Changed

  • Peer floors on @feugene/* raised to the current minor. Every peer this package declares on the ecosystem now starts at the version the monorepo actually ships:

    • @feugene/fint-i18n>=0.7.0 <1.0.0
    • @feugene/granularity>=0.36.0 <1.0.0
    • @feugene/unocss-preset-granular>=0.13.0 <1.0.0
    • @feugene/unplugin-granularity>=0.7.0 <1.0.0

    The floors had drifted far behind — some still admitted releases from a year of development ago — and a range that claims support it was never tested against is worse than a narrow one: the install succeeds and the breakage surfaces later, in the consumer’s app.

    This is breaking for anyone below a floor. Installing against an older @feugene/granularity now produces a peer conflict instead of silence. The fix is to move the core up; nothing in this package’s own API changed.

v0.6.12026-08-25

Fixed

  • The package tarball now ships LICENSE. The manifest has always declared "license": "SEE LICENSE IN LICENSE", and the file it points at was not there: npm adds LICENSE to a tarball on its own, but only when the file exists in the package directory. A consumer’s compliance scanner reads a licence reference that resolves to nothing and flags the dependency as unlicensed — a refusal on formal grounds, before anyone reads the terms.

    The copy is byte-identical to the one at the repository root and is kept that way by yarn check:licenses, a gate in CI: eleven copies of a 598-line file drift silently, and they drift exactly when the licence text is being edited.

v0.6.02026-08-24

Added

  • GrVideoPlayer — video with controls of its own. Native controls look different in every browser and know nothing about the design system’s themes or sizes; these are drawn from tokens.
  • Full screen is requested on the frame, not on <video>. Asking the video element hands the browser its own interface — our buttons, labels and keyboard would disappear exactly when they are needed most.
  • The buffered bar takes the range around the current position. Browsers keep several loaded ranges, and after seeking backwards the last one belongs to a different part of the clip: a bar drawn from it would jump ahead and promise data that is not there.
  • Duration is not always known. A stream recorded by MediaRecorder, or a live one, carries no duration in its header and the browser reports NaN. The progress bar is then not drawn at all and the label shows just the current time — “1:05 / 0:00” would promise an end the recording does not have.
  • Time is announced in words. aria-valuetext reads “1:05 / 2:00”; a bare aria-valuenow would say “65 of 120” — correct and useless.

This completes the package: cropping, camera capture, code scanning and playback.

v0.5.02026-08-24

Fixed

  • GrImageCrop cropped a frame other than the one on screen. The window’s height was computed as width / aspectRatio while the real height comes from CSS — aspect-ratio plus the border, and under box-sizing: border-box the gap is systematic. The window is now measured, both sides, and measured again after the image loads: the frame only takes its ratio together with the image, so the first measurement lands on a transitional height.
  • A resize now reports a changed frame. Responsive layouts resize the window without the user touching anything, and the first measurement arrives after load; a consumer building a file from the previous frame would get something other than what is on screen.

Changed

  • Zoom works without v-model:zoom. zoom was a controlled prop with no internal state, so the built-in slider was dead for everyone who had not wired the model — which is the common case. The component now keeps its own zoom and lets the prop override it, the same contract the core’s overlays use for v-model:open.

v0.4.02026-08-24

Added

  • GrCodeScanner — reading QR and barcodes with the camera. What leaves the component is a string, not a file; the frame is never stored.
  • No decoder ships with the package. BarcodeDetector exists in Chrome and Edge but in neither Safari nor Firefox — i.e. not on iPhone at all, which is where scanning mostly happens. Bundling a decoder would force the heaviest dependency in the package on everyone, including those who only took the cropper, so the native path is built in and everything else is covered by a detector the application passes; the component page carries a ready recipe. “Nothing can read codes here” is its own state: telling the user to “allow the camera” would send them to solve the wrong problem.
  • One code in frame is one event. The camera yields dozens of frames per second and the same code is recognised in each; unfiltered, an application would place twenty orders instead of one. continuous lifts the filter for goods-in, where identical packages are scanned in a row. The symbology is part of a code’s identity: the same digits as qr_code and as ean_13 are two different codes.

Changed

  • Camera plumbing — permission, refusal states, frame ratio, stopping tracks — moved into a shared composable used by both GrCameraCapture and GrCodeScanner. Written twice, it would have drifted apart on the first fix.

v0.3.02026-08-23

Changed

  • GrCameraCapture no longer crops. Cameras on different devices hand back different sizes and ratios, so fitting the frame to a fixed window is meaningless — one phone would lose the sides, another the top. The photo is now taken whole, in the camera’s own proportions, and the preview frame follows the stream. Cropping to a required shape is the next step, not this component: GrImageCrop does it on the captured file.
  • aspectRatio became a request to the camera rather than a crop. It goes into getUserMedia as ideal, so a device that can produce it will; one that cannot returns its own, and that is what gets shown and captured. exact is deliberately not used: it raises OverconstrainedError, i.e. reports “no camera” for a perfectly good camera with a different ratio.
  • output is a bounding box, not an exact size. With both sides given, the frame is fitted inside them and keeps its proportions; taking the numbers literally stretched the picture whenever the ratios disagreed. GrImageCrop follows the same rule.

Removed

  • cameraFrameRect and GrCameraFrameRect — the centre-crop helper has no callers left, and a dead utility in the public surface is worse than none: it invites use.

v0.2.22026-08-23

Fixed

  • A single side in output no longer stretches the result. Asking for one dimension is the common case — “an avatar 800 wide” — and the other was taken from the source area instead of being derived from its ratio: a 640×480 camera frame at width: 800 produced an 800×480 canvas, an image stretched a quarter wider than reality. Both GrCameraCapture and GrImageCrop were affected; the calculation now lives in one place and is covered on its own.

Earlier versions

The bodies of these releases are not shown: the full feed weighs more than a page is allowed to. The file in the repository is always complete.

  • v0.2.12026-08-23
  • v0.2.02026-08-23
  • v0.1.02026-08-23

The package CHANGELOG in full