@crowdedkingdoms/construct 0.3.10-test.1 → 0.3.11-dev.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/construct",
3
- "version": "0.3.10-test.1",
3
+ "version": "0.3.11-dev.1",
4
4
  "description": "The Construct framework: the Crowded Kingdoms game platform layer (hosted sign-in, World Stores, voice/webcam/chat, Crowdy Studio dock, app and grid mods, grid programs) and engine loop, shared by the-construct starter and the games built on it. Ships TypeScript source for Vite.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -208,6 +208,20 @@ export class WebcamService {
208
208
  }
209
209
 
210
210
  /** Ask for the camera and start sending frames. Resolves once capturing. */
211
+ /** Video-pixel rectangle copied into the 128×96 JPEG. Null keeps the whole frame. */
212
+ private frameCrop: { x: number; y: number; width: number; height: number } | null = null;
213
+ /** Camera size for the next start(). The encoded frame stays 128×96. */
214
+ private captureRequest = { width: CAPTURE_WIDTH, height: CAPTURE_HEIGHT };
215
+
216
+ setFrameCrop(crop: { x: number; y: number; width: number; height: number } | null): void {
217
+ this.frameCrop = crop;
218
+ }
219
+
220
+ /** Ask the camera for a larger picture before the next start(), so a face crop still has pixels. */
221
+ setCaptureSize(width: number, height: number): void {
222
+ if (width > 0 && height > 0) this.captureRequest = { width, height };
223
+ }
224
+
211
225
  async start(): Promise<void> {
212
226
  if (this.captureTimer) return;
213
227
  if (typeof navigator === 'undefined' || !navigator.mediaDevices?.getUserMedia) {
@@ -216,7 +230,11 @@ export class WebcamService {
216
230
  }
217
231
  try {
218
232
  this.stream = await navigator.mediaDevices.getUserMedia({
219
- video: { width: CAPTURE_WIDTH, height: CAPTURE_HEIGHT, frameRate: CAPTURE_FPS },
233
+ video: {
234
+ width: this.captureRequest.width,
235
+ height: this.captureRequest.height,
236
+ frameRate: CAPTURE_FPS,
237
+ },
220
238
  audio: false,
221
239
  });
222
240
  } catch (error) {
@@ -246,6 +264,7 @@ export class WebcamService {
246
264
  stop(): void {
247
265
  if (this.captureTimer) clearInterval(this.captureTimer);
248
266
  this.captureTimer = null;
267
+ this.frameCrop = null;
249
268
  this.video?.pause();
250
269
  this.video = null;
251
270
  this.canvas = null;
@@ -302,7 +321,22 @@ export class WebcamService {
302
321
  const ctx = canvas.getContext('2d') as
303
322
  OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null;
304
323
  if (!ctx) return null;
305
- ctx.drawImage(video, 0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT);
324
+ const crop = this.frameCrop;
325
+ if (crop && crop.width > 0 && crop.height > 0) {
326
+ ctx.drawImage(
327
+ video,
328
+ crop.x,
329
+ crop.y,
330
+ crop.width,
331
+ crop.height,
332
+ 0,
333
+ 0,
334
+ CAPTURE_WIDTH,
335
+ CAPTURE_HEIGHT,
336
+ );
337
+ } else {
338
+ ctx.drawImage(video, 0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT);
339
+ }
306
340
  for (let attempt = 0; attempt < 3; attempt++) {
307
341
  const blob = await toBlob(canvas, this.quality);
308
342
  if (!blob) return null;