@maravilla-labs/platform 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @maravilla-labs/platform
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4b6b127: Add a `zoom` option to `resize({ crop: 'focal' })` for face-box-relative crop
8
+ framing. Until now a focal crop only centered a fixed-size window on the stored
9
+ focal _point_ — the detected face's bounding-box size was discarded, so there
10
+ was no way to control how tightly the subject was framed. `zoom` (a scale
11
+ factor, `1.0..=100.0`) now sizes the crop window from the union of the source's
12
+ detected faces: `1.0` frames tightly (a headshot), larger pulls back for
13
+ headroom/context, and a value large enough to exceed the image saturates to the
14
+ plain scale-to-cover crop. It's declarable in the `maravilla.config.ts`
15
+ `transforms` block too.
16
+
17
+ Fully backward compatible: `zoom` is optional and never serializes when omitted,
18
+ so every existing focal/center crop hashes to the same derived key and is
19
+ byte-identical. `zoom` is only honoured for `crop: 'focal'` on a source that has
20
+ stored face data (run `detectFaces` first); it's ignored for `crop: 'center'`
21
+ and a no-op when no faces were detected.
22
+
23
+ Also adds two pure, isomorphic, framework-agnostic helpers to
24
+ `@maravilla-labs/platform` — `focalCssFrame()` and `unionFaceBox()` — the
25
+ browser-side counterpart to the server crop: they turn a source's focal
26
+ point + detected faces into `object-position` + `transform: scale` CSS values,
27
+ so one `<img>` can re-frame around the face(s) for any container aspect ratio
28
+ and zoom without a server round-trip or a pre-rendered crop per shape.
29
+
3
30
  ## 0.15.0
4
31
 
5
32
  ### Minor Changes
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { T as TransformsConfig } from './transforms-BPrlJoYd.js';
2
- export { a as TransformsPatternSpec } from './transforms-BPrlJoYd.js';
1
+ import { T as TransformsConfig } from './transforms-DkAlUPu6.js';
2
+ export { a as TransformsPatternSpec } from './transforms-DkAlUPu6.js';
3
3
 
4
4
  /**
5
5
  * @fileoverview Typed schema for `maravilla.config.{ts,yaml,json}` files.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { ActAsContext, AddCircleMemberOptions, AddRelationOptions, ApiKeyInfo, A
3
3
  export { b as RenClient, a as RenClientOptions, R as RenEvent, g as getOrCreateClientId, r as renFetch, c as storageDelete, s as storageUpload } from './ren-DetHRhCL.js';
4
4
  import { LocalParticipant } from 'livekit-client';
5
5
  export { RegisterPushOptions, RegisterPushResult, offsetBefore, registerPush, unregisterPush } from './push.js';
6
- export { D as DetachAudioOpts, e as DetectFacesOpts, l as DocConvertOpts, i as DocFormat, q as DocInsertQrCodeOpts, p as DocReplaceImagesOpts, s as DocTemplateMergeOpts, k as DocThumbnailOpts, n as DocToHtmlOpts, m as DocToMarkdownOpts, j as DocToPdfOpts, d as ExtractAudioOpts, E as ExtractFramesOpts, g as FaceBox, f as FocalPoint, h as FocalPointRecord, F as FramesetManifest, I as ImageFormat, o as ImageRef, u as JobHandle, J as JobStatus, v as JobStatusResponse, M as MediaInfo, O as OcrOpts, Q as QrCodeSpec, r as QrPayload, R as ResizeOpts, c as ThumbnailOpts, b as TranscodeOpts, t as TransformSpec, T as TransformsConfig, a as TransformsPatternSpec, w as TransformsService, V as VideoFormat, x as keyFor, y as transforms } from './transforms-BPrlJoYd.js';
6
+ export { D as DetachAudioOpts, e as DetectFacesOpts, l as DocConvertOpts, i as DocFormat, q as DocInsertQrCodeOpts, p as DocReplaceImagesOpts, s as DocTemplateMergeOpts, k as DocThumbnailOpts, n as DocToHtmlOpts, m as DocToMarkdownOpts, j as DocToPdfOpts, d as ExtractAudioOpts, E as ExtractFramesOpts, g as FaceBox, z as FocalCssFrame, y as FocalCssFrameInput, f as FocalPoint, h as FocalPointRecord, F as FramesetManifest, I as ImageFormat, o as ImageRef, u as JobHandle, J as JobStatus, v as JobStatusResponse, M as MediaInfo, O as OcrOpts, Q as QrCodeSpec, r as QrPayload, R as ResizeOpts, S as SubjectBox, c as ThumbnailOpts, b as TranscodeOpts, t as TransformSpec, T as TransformsConfig, a as TransformsPatternSpec, w as TransformsService, V as VideoFormat, B as focalCssFrame, x as keyFor, C as transforms, A as unionFaceBox } from './transforms-DkAlUPu6.js';
7
7
 
8
8
  /**
9
9
  * Media service for video/audio room management.
package/dist/index.js CHANGED
@@ -2096,8 +2096,44 @@ function keyFor(srcKey, spec) {
2096
2096
  const ext = outputExtension(spec);
2097
2097
  return `__derived/${srcHash}/${variantHash}.${ext}`;
2098
2098
  }
2099
+ var clamp01 = (n) => Math.min(1, Math.max(0, n));
2100
+ function unionFaceBox(faces) {
2101
+ if (!faces || faces.length === 0) return null;
2102
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
2103
+ for (const f of faces) {
2104
+ x0 = Math.min(x0, f.x);
2105
+ y0 = Math.min(y0, f.y);
2106
+ x1 = Math.max(x1, f.x + f.w);
2107
+ y1 = Math.max(y1, f.y + f.h);
2108
+ }
2109
+ x0 = clamp01(x0);
2110
+ y0 = clamp01(y0);
2111
+ x1 = clamp01(x1);
2112
+ y1 = clamp01(y1);
2113
+ const w = x1 - x0;
2114
+ const h = y1 - y0;
2115
+ if (w <= 0 || h <= 0) return null;
2116
+ return { x: x0, y: y0, w, h };
2117
+ }
2118
+ function focalCssFrame(input) {
2119
+ const { focal, faces, imgWidth, imgHeight, containerAspect } = input;
2120
+ const zoom = input.zoom ?? 1;
2121
+ const objectPosition = `${(clamp01(focal.x) * 100).toFixed(1)}% ${(clamp01(focal.y) * 100).toFixed(1)}%`;
2122
+ const base = { objectPosition, scale: 1, transformOrigin: objectPosition };
2123
+ const box = unionFaceBox(faces);
2124
+ if (!box || imgWidth <= 0 || imgHeight <= 0 || !(containerAspect > 0)) return base;
2125
+ const iar = imgWidth / imgHeight;
2126
+ const car = containerAspect;
2127
+ const z = Math.max(1, zoom);
2128
+ const targetW = z * Math.max(box.w, box.h * car / iar);
2129
+ const coverW = car >= iar ? 1 : car / iar;
2130
+ const scale = Math.max(1, coverW / targetW);
2131
+ return { objectPosition, scale: Math.round(scale * 1e3) / 1e3, transformOrigin: objectPosition };
2132
+ }
2099
2133
  var transforms = {
2100
- keyFor
2134
+ keyFor,
2135
+ focalCssFrame,
2136
+ unionFaceBox
2101
2137
  };
2102
2138
 
2103
2139
  // src/index.ts
@@ -2144,6 +2180,7 @@ export {
2144
2180
  attachTrack,
2145
2181
  clearPlatformCache,
2146
2182
  detachTrack,
2183
+ focalCssFrame,
2147
2184
  getCurrentRequestStore,
2148
2185
  getOrCreateClientId,
2149
2186
  getPlatform,
@@ -2155,6 +2192,7 @@ export {
2155
2192
  storageDelete,
2156
2193
  storageUpload,
2157
2194
  transforms,
2195
+ unionFaceBox,
2158
2196
  unregisterPush
2159
2197
  };
2160
2198
  //# sourceMappingURL=index.js.map