@maravilla-labs/platform 0.15.0 → 0.17.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,50 @@
1
1
  # @maravilla-labs/platform
2
2
 
3
+ ## 0.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 16d4f8d: Extend `resize({ crop: 'focal' })` framing with three more opt-in knobs (on top
8
+ of the initial `zoom` factor):
9
+ - **`zoom`** now also accepts values `< 1` (crop _into_ the subject) and
10
+ `'auto'` (a curated, consistent occupancy); range is `0.4..=100.0` | `'auto'`.
11
+ - **`padding`** — margin around the subject box (`0.0..=2.0`, fraction per side)
12
+ so a tight crop keeps breathing room around the head.
13
+ - **`subject`** — `'union'` (all faces, default) or `'primary'` (the
14
+ highest-confidence face, re-centred on it), so a group photo can crop to one
15
+ person.
16
+
17
+ Still fully backward compatible: the new fields never serialize when omitted, so
18
+ existing derived keys are byte-identical. The SDK CSS helpers gain the same
19
+ knobs plus a new `primaryFaceBox()` for parity.
20
+
21
+ ## 0.16.0
22
+
23
+ ### Minor Changes
24
+
25
+ - 4b6b127: Add a `zoom` option to `resize({ crop: 'focal' })` for face-box-relative crop
26
+ framing. Until now a focal crop only centered a fixed-size window on the stored
27
+ focal _point_ — the detected face's bounding-box size was discarded, so there
28
+ was no way to control how tightly the subject was framed. `zoom` (a scale
29
+ factor, `1.0..=100.0`) now sizes the crop window from the union of the source's
30
+ detected faces: `1.0` frames tightly (a headshot), larger pulls back for
31
+ headroom/context, and a value large enough to exceed the image saturates to the
32
+ plain scale-to-cover crop. It's declarable in the `maravilla.config.ts`
33
+ `transforms` block too.
34
+
35
+ Fully backward compatible: `zoom` is optional and never serializes when omitted,
36
+ so every existing focal/center crop hashes to the same derived key and is
37
+ byte-identical. `zoom` is only honoured for `crop: 'focal'` on a source that has
38
+ stored face data (run `detectFaces` first); it's ignored for `crop: 'center'`
39
+ and a no-op when no faces were detected.
40
+
41
+ Also adds two pure, isomorphic, framework-agnostic helpers to
42
+ `@maravilla-labs/platform` — `focalCssFrame()` and `unionFaceBox()` — the
43
+ browser-side counterpart to the server crop: they turn a source's focal
44
+ point + detected faces into `object-position` + `transform: scale` CSS values,
45
+ so one `<img>` can re-frame around the face(s) for any container aspect ratio
46
+ and zoom without a server round-trip or a pre-rendered crop per shape.
47
+
3
48
  ## 0.15.0
4
49
 
5
50
  ### 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-C2zafn5k.js';
2
+ export { a as TransformsPatternSpec } from './transforms-C2zafn5k.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, C as focalCssFrame, x as keyFor, B as primaryFaceBox, G as transforms, A as unionFaceBox } from './transforms-C2zafn5k.js';
7
7
 
8
8
  /**
9
9
  * Media service for video/audio room management.
package/dist/index.js CHANGED
@@ -2096,8 +2096,71 @@ function keyFor(srcKey, spec) {
2096
2096
  const ext = outputExtension(spec);
2097
2097
  return `__derived/${srcHash}/${variantHash}.${ext}`;
2098
2098
  }
2099
+ var AUTO_ZOOM = 1.22;
2100
+ var clamp01 = (n) => Math.min(1, Math.max(0, n));
2101
+ function unionFaceBox(faces) {
2102
+ if (!faces || faces.length === 0) return null;
2103
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
2104
+ for (const f of faces) {
2105
+ x0 = Math.min(x0, f.x);
2106
+ y0 = Math.min(y0, f.y);
2107
+ x1 = Math.max(x1, f.x + f.w);
2108
+ y1 = Math.max(y1, f.y + f.h);
2109
+ }
2110
+ x0 = clamp01(x0);
2111
+ y0 = clamp01(y0);
2112
+ x1 = clamp01(x1);
2113
+ y1 = clamp01(y1);
2114
+ const w = x1 - x0;
2115
+ const h = y1 - y0;
2116
+ if (w <= 0 || h <= 0) return null;
2117
+ return { x: x0, y: y0, w, h };
2118
+ }
2119
+ function primaryFaceBox(faces) {
2120
+ if (!faces || faces.length === 0) return null;
2121
+ const f = faces.reduce((a, b) => b.score > a.score ? b : a);
2122
+ const x0 = clamp01(f.x);
2123
+ const y0 = clamp01(f.y);
2124
+ const x1 = clamp01(f.x + f.w);
2125
+ const y1 = clamp01(f.y + f.h);
2126
+ const w = x1 - x0;
2127
+ const h = y1 - y0;
2128
+ if (w <= 0 || h <= 0) return null;
2129
+ return {
2130
+ box: { x: x0, y: y0, w, h },
2131
+ focal: { x: x0 + w / 2, y: clamp01(y0 + 0.4 * h) }
2132
+ };
2133
+ }
2134
+ function focalCssFrame(input) {
2135
+ const { faces, imgWidth, imgHeight, containerAspect } = input;
2136
+ let box;
2137
+ let focal = input.focal;
2138
+ if (input.subject === "primary") {
2139
+ const p = primaryFaceBox(faces);
2140
+ box = p?.box ?? null;
2141
+ if (p) focal = p.focal;
2142
+ } else {
2143
+ box = unionFaceBox(faces);
2144
+ }
2145
+ const objectPosition = `${(clamp01(focal.x) * 100).toFixed(1)}% ${(clamp01(focal.y) * 100).toFixed(1)}%`;
2146
+ const base = { objectPosition, scale: 1, transformOrigin: objectPosition };
2147
+ if (!box || imgWidth <= 0 || imgHeight <= 0 || !(containerAspect > 0)) return base;
2148
+ const iar = imgWidth / imgHeight;
2149
+ const car = containerAspect;
2150
+ const z = input.zoom === "auto" ? AUTO_ZOOM : Math.max(0.01, input.zoom ?? 1);
2151
+ const grow = 1 + 2 * Math.max(0, input.padding ?? 0);
2152
+ const bw = box.w * grow;
2153
+ const bh = box.h * grow;
2154
+ const targetW = z * Math.max(bw, bh * car / iar);
2155
+ const coverW = car >= iar ? 1 : car / iar;
2156
+ const scale = Math.max(1, coverW / targetW);
2157
+ return { objectPosition, scale: Math.round(scale * 1e3) / 1e3, transformOrigin: objectPosition };
2158
+ }
2099
2159
  var transforms = {
2100
- keyFor
2160
+ keyFor,
2161
+ focalCssFrame,
2162
+ unionFaceBox,
2163
+ primaryFaceBox
2101
2164
  };
2102
2165
 
2103
2166
  // src/index.ts
@@ -2144,17 +2207,20 @@ export {
2144
2207
  attachTrack,
2145
2208
  clearPlatformCache,
2146
2209
  detachTrack,
2210
+ focalCssFrame,
2147
2211
  getCurrentRequestStore,
2148
2212
  getOrCreateClientId,
2149
2213
  getPlatform,
2150
2214
  keyFor,
2151
2215
  offsetBefore,
2216
+ primaryFaceBox,
2152
2217
  registerPush,
2153
2218
  renFetch,
2154
2219
  runWithRequest,
2155
2220
  storageDelete,
2156
2221
  storageUpload,
2157
2222
  transforms,
2223
+ unionFaceBox,
2158
2224
  unregisterPush
2159
2225
  };
2160
2226
  //# sourceMappingURL=index.js.map