@yassine-bouassida/scenecap 1.0.0 → 1.0.2

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/README.md CHANGED
@@ -11,7 +11,7 @@ Describe what should happen in **plain English**, and Scenecap parses it, execut
11
11
  ## Installation
12
12
 
13
13
  ```bash
14
- npm install scenecap
14
+ npm install @yassine-bouassida/scenecap
15
15
  ```
16
16
 
17
17
  ---
@@ -22,7 +22,7 @@ npm install scenecap
22
22
 
23
23
  ```tsx
24
24
  'use client';
25
- import { useScenecap } from 'scenecap';
25
+ import { useScenecap } from '@yassine-bouassida/scenecap';
26
26
 
27
27
  export default function MyPage() {
28
28
  const { containerRef, record, download, isRecording, progress } = useScenecap();
@@ -53,7 +53,7 @@ export default function MyPage() {
53
53
  ### Vanilla JS / Imperative
54
54
 
55
55
  ```ts
56
- import { createScenecap } from 'scenecap';
56
+ import { createScenecap } from '@yassine-bouassida/scenecap';
57
57
 
58
58
  const sc = createScenecap({ recording: { fps: 30 } });
59
59
  const blob = await sc.record(`
@@ -70,7 +70,7 @@ a.click();
70
70
  ### Structured Scenario (skip NLP)
71
71
 
72
72
  ```ts
73
- import { ScenarioRunner } from 'scenecap';
73
+ import { ScenarioRunner } from '@yassine-bouassida/scenecap';
74
74
 
75
75
  const runner = new ScenarioRunner();
76
76
  const blob = await runner.recordScenario({
package/dist/index.js CHANGED
@@ -388,16 +388,20 @@ var VideoRecorder = class {
388
388
  */
389
389
  async startFromElement(element) {
390
390
  const canvas = document.createElement("canvas");
391
- const dpr = this.options.devicePixelRatio;
391
+ const dpr = this.options.devicePixelRatio ?? 1;
392
392
  canvas.width = this.options.width * dpr;
393
393
  canvas.height = this.options.height * dpr;
394
- await this.startFromCanvas(canvas);
395
394
  const ctx = canvas.getContext("2d");
395
+ const bg = this.options.backgroundColor || "#ffffff";
396
+ ctx.fillStyle = bg;
397
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
398
+ await this.startFromCanvas(canvas);
396
399
  this.captureInterval = window.setInterval(async () => {
400
+ ctx.fillStyle = bg;
401
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
397
402
  try {
398
403
  const data = await domToImage(element, this.options.width, this.options.height);
399
404
  if (data) {
400
- ctx.clearRect(0, 0, canvas.width, canvas.height);
401
405
  ctx.drawImage(data, 0, 0, canvas.width, canvas.height);
402
406
  }
403
407
  } catch {
@@ -449,18 +453,31 @@ var VideoRecorder = class {
449
453
  }
450
454
  };
451
455
  async function domToImage(element, width, height) {
456
+ let cssText = "";
457
+ for (const sheet of Array.from(document.styleSheets)) {
458
+ try {
459
+ cssText += Array.from(sheet.cssRules).map((r) => r.cssText).join("\n");
460
+ } catch {
461
+ }
462
+ }
452
463
  const clone = element.cloneNode(true);
453
- inlineStyles(element, clone);
454
- const html = new XMLSerializer().serializeToString(clone);
455
- const svg = `
456
- <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
457
- <foreignObject width="100%" height="100%">
458
- <div xmlns="http://www.w3.org/1999/xhtml" style="width:${width}px;height:${height}px;overflow:hidden;">
459
- ${html}
460
- </div>
461
- </foreignObject>
462
- </svg>
464
+ const innerHtml = clone.outerHTML;
465
+ const xhtml = `
466
+ <html xmlns="http://www.w3.org/1999/xhtml">
467
+ <head>
468
+ <style>
469
+ * { box-sizing: border-box; }
470
+ ${cssText}
471
+ </style>
472
+ </head>
473
+ <body style="margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;">
474
+ ${innerHtml}
475
+ </body>
476
+ </html>
463
477
  `;
478
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
479
+ <foreignObject width="100%" height="100%">${xhtml}</foreignObject>
480
+ </svg>`;
464
481
  const svgBlob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
465
482
  const url = URL.createObjectURL(svgBlob);
466
483
  return new Promise((resolve) => {
@@ -476,18 +493,6 @@ async function domToImage(element, width, height) {
476
493
  img.src = url;
477
494
  });
478
495
  }
479
- function inlineStyles(source, target) {
480
- const computed = window.getComputedStyle(source);
481
- const inline = Array.from(computed).map((prop) => `${prop}:${computed.getPropertyValue(prop)}`).join(";");
482
- target.setAttribute("style", inline);
483
- const sourceChildren = source.children;
484
- const targetChildren = target.children;
485
- for (let i = 0; i < sourceChildren.length; i++) {
486
- if (targetChildren[i]) {
487
- inlineStyles(sourceChildren[i], targetChildren[i]);
488
- }
489
- }
490
- }
491
496
 
492
497
  // src/core/zoom.ts
493
498
  var ZoomController = class {
package/dist/index.mjs CHANGED
@@ -350,16 +350,20 @@ var VideoRecorder = class {
350
350
  */
351
351
  async startFromElement(element) {
352
352
  const canvas = document.createElement("canvas");
353
- const dpr = this.options.devicePixelRatio;
353
+ const dpr = this.options.devicePixelRatio ?? 1;
354
354
  canvas.width = this.options.width * dpr;
355
355
  canvas.height = this.options.height * dpr;
356
- await this.startFromCanvas(canvas);
357
356
  const ctx = canvas.getContext("2d");
357
+ const bg = this.options.backgroundColor || "#ffffff";
358
+ ctx.fillStyle = bg;
359
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
360
+ await this.startFromCanvas(canvas);
358
361
  this.captureInterval = window.setInterval(async () => {
362
+ ctx.fillStyle = bg;
363
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
359
364
  try {
360
365
  const data = await domToImage(element, this.options.width, this.options.height);
361
366
  if (data) {
362
- ctx.clearRect(0, 0, canvas.width, canvas.height);
363
367
  ctx.drawImage(data, 0, 0, canvas.width, canvas.height);
364
368
  }
365
369
  } catch {
@@ -411,18 +415,31 @@ var VideoRecorder = class {
411
415
  }
412
416
  };
413
417
  async function domToImage(element, width, height) {
418
+ let cssText = "";
419
+ for (const sheet of Array.from(document.styleSheets)) {
420
+ try {
421
+ cssText += Array.from(sheet.cssRules).map((r) => r.cssText).join("\n");
422
+ } catch {
423
+ }
424
+ }
414
425
  const clone = element.cloneNode(true);
415
- inlineStyles(element, clone);
416
- const html = new XMLSerializer().serializeToString(clone);
417
- const svg = `
418
- <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
419
- <foreignObject width="100%" height="100%">
420
- <div xmlns="http://www.w3.org/1999/xhtml" style="width:${width}px;height:${height}px;overflow:hidden;">
421
- ${html}
422
- </div>
423
- </foreignObject>
424
- </svg>
426
+ const innerHtml = clone.outerHTML;
427
+ const xhtml = `
428
+ <html xmlns="http://www.w3.org/1999/xhtml">
429
+ <head>
430
+ <style>
431
+ * { box-sizing: border-box; }
432
+ ${cssText}
433
+ </style>
434
+ </head>
435
+ <body style="margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;">
436
+ ${innerHtml}
437
+ </body>
438
+ </html>
425
439
  `;
440
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
441
+ <foreignObject width="100%" height="100%">${xhtml}</foreignObject>
442
+ </svg>`;
426
443
  const svgBlob = new Blob([svg], { type: "image/svg+xml;charset=utf-8" });
427
444
  const url = URL.createObjectURL(svgBlob);
428
445
  return new Promise((resolve) => {
@@ -438,18 +455,6 @@ async function domToImage(element, width, height) {
438
455
  img.src = url;
439
456
  });
440
457
  }
441
- function inlineStyles(source, target) {
442
- const computed = window.getComputedStyle(source);
443
- const inline = Array.from(computed).map((prop) => `${prop}:${computed.getPropertyValue(prop)}`).join(";");
444
- target.setAttribute("style", inline);
445
- const sourceChildren = source.children;
446
- const targetChildren = target.children;
447
- for (let i = 0; i < sourceChildren.length; i++) {
448
- if (targetChildren[i]) {
449
- inlineStyles(sourceChildren[i], targetChildren[i]);
450
- }
451
- }
452
- }
453
458
 
454
459
  // src/core/zoom.ts
455
460
  var ZoomController = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yassine-bouassida/scenecap",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Scriptable screen recording with annotations, zoom effects, and highlights for Next.js — powered by natural language scenarios.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",