@pallasoft/ps-file-components 1.4.0 → 1.5.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.
Files changed (113) hide show
  1. package/dist/components/ps-document-scanner-loader.d.ts +1 -1
  2. package/dist/components/ps-document-scanner-sheet.d.ts +1 -1
  3. package/dist/components/ps-document-scanner-sheet.js +69 -21
  4. package/dist/components/ps-document-scanner.d.ts +1 -1
  5. package/dist/components/ps-document-scanner.js +2 -2
  6. package/dist/components/ps-file-picker-actions.d.ts +10 -0
  7. package/dist/components/ps-file-picker-actions.js +21 -0
  8. package/dist/components/ps-file-picker.d.ts +7 -1
  9. package/dist/components/ps-file-picker.js +30 -6
  10. package/dist/components/ps-scan-button.d.ts +13 -0
  11. package/dist/components/ps-scan-button.js +9 -0
  12. package/dist/components/ps-scan-camera-view.d.ts +17 -7
  13. package/dist/components/ps-scan-camera-view.js +23 -16
  14. package/dist/components/ps-scan-crop-editor.d.ts +2 -2
  15. package/dist/components/ps-scan-crop-editor.js +19 -3
  16. package/dist/components/ps-scan-denied-steps.d.ts +6 -0
  17. package/dist/components/ps-scan-denied-steps.js +33 -0
  18. package/dist/components/ps-scan-icon-button.d.ts +4 -3
  19. package/dist/components/ps-scan-icon-button.js +3 -1
  20. package/dist/components/ps-scan-illustration.d.ts +1 -0
  21. package/dist/components/ps-scan-illustration.js +6 -0
  22. package/dist/components/ps-scan-loupe.d.ts +10 -0
  23. package/dist/components/ps-scan-loupe.js +31 -0
  24. package/dist/components/ps-scan-page-list.d.ts +3 -1
  25. package/dist/components/ps-scan-page-list.js +7 -8
  26. package/dist/components/ps-scan-page-stack.d.ts +7 -0
  27. package/dist/components/ps-scan-page-stack.js +6 -0
  28. package/dist/components/ps-scan-permission-view.d.ts +11 -0
  29. package/dist/components/ps-scan-permission-view.js +48 -0
  30. package/dist/components/ps-scan-photo-button.d.ts +8 -0
  31. package/dist/components/ps-scan-photo-button.js +20 -0
  32. package/dist/components/ps-scan-review.d.ts +2 -1
  33. package/dist/components/ps-scan-review.js +21 -4
  34. package/dist/components/ps-scan-shutter.d.ts +7 -0
  35. package/dist/components/ps-scan-shutter.js +6 -0
  36. package/dist/components/ps-scan-viewfinder-overlay.d.ts +8 -0
  37. package/dist/components/ps-scan-viewfinder-overlay.js +28 -0
  38. package/dist/components/ps-upload-row.js +1 -1
  39. package/dist/constants/ps-document-scanner-labels.d.ts +49 -19
  40. package/dist/constants/ps-document-scanner-labels.js +49 -19
  41. package/dist/constants/ps-file-picker-labels.d.ts +5 -3
  42. package/dist/constants/ps-file-picker-labels.js +5 -3
  43. package/dist/constants/ps-scan-camera-status.d.ts +4 -0
  44. package/dist/constants/ps-scan-camera-status.js +4 -0
  45. package/dist/constants/ps-scan-chrome-classes.d.ts +12 -0
  46. package/dist/constants/ps-scan-chrome-classes.js +15 -0
  47. package/dist/constants/ps-scan-config.d.ts +18 -1
  48. package/dist/constants/ps-scan-config.js +18 -1
  49. package/dist/constants/ps-scan-platform.d.ts +6 -0
  50. package/dist/constants/ps-scan-platform.js +8 -0
  51. package/dist/esm/components/ps-document-scanner-sheet.js +71 -23
  52. package/dist/esm/components/ps-document-scanner.js +2 -2
  53. package/dist/esm/components/ps-file-picker-actions.js +17 -0
  54. package/dist/esm/components/ps-file-picker.js +31 -7
  55. package/dist/esm/components/ps-scan-button.js +5 -0
  56. package/dist/esm/components/ps-scan-camera-view.js +26 -19
  57. package/dist/esm/components/ps-scan-crop-editor.js +21 -5
  58. package/dist/esm/components/ps-scan-denied-steps.js +29 -0
  59. package/dist/esm/components/ps-scan-icon-button.js +3 -1
  60. package/dist/esm/components/ps-scan-illustration.js +2 -0
  61. package/dist/esm/components/ps-scan-loupe.js +27 -0
  62. package/dist/esm/components/ps-scan-page-list.js +7 -8
  63. package/dist/esm/components/ps-scan-page-stack.js +2 -0
  64. package/dist/esm/components/ps-scan-permission-view.js +44 -0
  65. package/dist/esm/components/ps-scan-photo-button.js +16 -0
  66. package/dist/esm/components/ps-scan-review.js +21 -4
  67. package/dist/esm/components/ps-scan-shutter.js +2 -0
  68. package/dist/esm/components/ps-scan-viewfinder-overlay.js +24 -0
  69. package/dist/esm/components/ps-upload-row.js +1 -1
  70. package/dist/esm/constants/ps-document-scanner-labels.js +49 -19
  71. package/dist/esm/constants/ps-file-picker-labels.js +5 -3
  72. package/dist/esm/constants/ps-scan-camera-status.js +4 -0
  73. package/dist/esm/constants/ps-scan-chrome-classes.js +12 -0
  74. package/dist/esm/constants/ps-scan-config.js +18 -1
  75. package/dist/esm/constants/ps-scan-platform.js +5 -0
  76. package/dist/esm/hooks/use-ps-scan-camera.js +52 -15
  77. package/dist/esm/hooks/use-ps-scan-live-detection.js +45 -0
  78. package/dist/esm/index.js +1 -0
  79. package/dist/esm/models/ps-file-picker-scan.js +1 -0
  80. package/dist/esm/models/ps-scan-live-detection.js +1 -0
  81. package/dist/esm/models/ps-scan-stability.js +1 -0
  82. package/dist/esm/services/ps-scan-camera-service.js +50 -5
  83. package/dist/esm/services/ps-scan-canvas-service.js +6 -0
  84. package/dist/esm/services/ps-scan-edge-service.js +1 -1
  85. package/dist/esm/services/ps-scan-geometry-service.js +20 -0
  86. package/dist/esm/services/ps-scan-platform-service.js +15 -0
  87. package/dist/esm/services/ps-scan-stability-service.js +41 -0
  88. package/dist/hooks/use-ps-scan-camera.d.ts +3 -1
  89. package/dist/hooks/use-ps-scan-camera.js +52 -15
  90. package/dist/hooks/use-ps-scan-live-detection.d.ts +3 -0
  91. package/dist/hooks/use-ps-scan-live-detection.js +49 -0
  92. package/dist/index.d.ts +1 -0
  93. package/dist/index.js +1 -0
  94. package/dist/models/ps-document-scanner-props.d.ts +1 -0
  95. package/dist/models/ps-document-scanner-sheet-props.d.ts +1 -0
  96. package/dist/models/ps-file-picker-scan.d.ts +4 -0
  97. package/dist/models/ps-file-picker-scan.js +2 -0
  98. package/dist/models/ps-scan-live-detection.d.ts +5 -0
  99. package/dist/models/ps-scan-live-detection.js +2 -0
  100. package/dist/models/ps-scan-stability.d.ts +8 -0
  101. package/dist/models/ps-scan-stability.js +2 -0
  102. package/dist/services/ps-scan-camera-service.d.ts +4 -0
  103. package/dist/services/ps-scan-camera-service.js +50 -5
  104. package/dist/services/ps-scan-canvas-service.d.ts +1 -0
  105. package/dist/services/ps-scan-canvas-service.js +6 -0
  106. package/dist/services/ps-scan-edge-service.js +1 -1
  107. package/dist/services/ps-scan-geometry-service.d.ts +2 -0
  108. package/dist/services/ps-scan-geometry-service.js +20 -0
  109. package/dist/services/ps-scan-platform-service.d.ts +4 -0
  110. package/dist/services/ps-scan-platform-service.js +19 -0
  111. package/dist/services/ps-scan-stability-service.d.ts +9 -0
  112. package/dist/services/ps-scan-stability-service.js +45 -0
  113. package/package.json +3 -3
@@ -4,13 +4,45 @@ exports.PsScanCameraService = void 0;
4
4
  const ps_scan_camera_status_1 = require("../constants/ps-scan-camera-status");
5
5
  const ps_scan_config_1 = require("../constants/ps-scan-config");
6
6
  class PsScanCameraService {
7
+ static async attach(video, session) {
8
+ if (video.srcObject !== session.stream)
9
+ video.srcObject = session.stream;
10
+ await video.play().catch(() => undefined);
11
+ }
12
+ static async check() {
13
+ if (globalThis.isSecureContext === false)
14
+ return ps_scan_camera_status_1.PsScanCameraStatus.Insecure;
15
+ if (!PsScanCameraService.isSupported())
16
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
17
+ if (!(await PsScanCameraService.hasCamera()))
18
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
19
+ const permission = await PsScanCameraService.permission();
20
+ if (permission === null)
21
+ return ps_scan_camera_status_1.PsScanCameraStatus.Prompt;
22
+ if (permission.state === 'granted')
23
+ return ps_scan_camera_status_1.PsScanCameraStatus.Starting;
24
+ return permission.state === 'denied' ? ps_scan_camera_status_1.PsScanCameraStatus.Denied : ps_scan_camera_status_1.PsScanCameraStatus.Prompt;
25
+ }
7
26
  static classify(error) {
8
27
  const name = typeof error === 'object' && error !== null && 'name' in error ? String(error.name) : '';
9
- return name === 'NotAllowedError' ||
10
- name === 'SecurityError' ||
11
- name === 'PermissionDeniedError'
12
- ? ps_scan_camera_status_1.PsScanCameraStatus.Denied
13
- : ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
28
+ if (name === 'NotAllowedError' || name === 'PermissionDeniedError')
29
+ return ps_scan_camera_status_1.PsScanCameraStatus.Denied;
30
+ if (name === 'SecurityError')
31
+ return ps_scan_camera_status_1.PsScanCameraStatus.Insecure;
32
+ if (name === 'NotReadableError' || name === 'TrackStartError' || name === 'AbortError')
33
+ return ps_scan_camera_status_1.PsScanCameraStatus.Busy;
34
+ return ps_scan_camera_status_1.PsScanCameraStatus.Unavailable;
35
+ }
36
+ static async hasCamera() {
37
+ if (typeof navigator.mediaDevices.enumerateDevices !== 'function')
38
+ return true;
39
+ try {
40
+ const devices = await navigator.mediaDevices.enumerateDevices();
41
+ return devices.length === 0 || devices.some((device) => device.kind === 'videoinput');
42
+ }
43
+ catch {
44
+ return true;
45
+ }
14
46
  }
15
47
  static isSupported() {
16
48
  return (typeof navigator !== 'undefined' &&
@@ -29,6 +61,19 @@ class PsScanCameraService {
29
61
  const track = stream.getVideoTracks()[0];
30
62
  return { canTorch: PsScanCameraService.supportsTorch(track), stream, track };
31
63
  }
64
+ static async permission() {
65
+ if (typeof navigator.permissions === 'undefined' ||
66
+ typeof navigator.permissions.query !== 'function')
67
+ return null;
68
+ try {
69
+ return await navigator.permissions.query({
70
+ name: ps_scan_config_1.PsScanConfig.cameraPermission,
71
+ });
72
+ }
73
+ catch {
74
+ return null;
75
+ }
76
+ }
32
77
  static async setTorch(track, isOn) {
33
78
  try {
34
79
  await track.applyConstraints({
@@ -4,5 +4,6 @@ export declare class PsScanCanvasService {
4
4
  static decode(blob: Blob, maxDimension: number): Promise<PsScanDecodedImage>;
5
5
  static encode(raster: PsScanRaster, quality: number): Promise<Blob>;
6
6
  static fromVideo(video: HTMLVideoElement, quality: number): Promise<Blob>;
7
+ static sample(video: HTMLVideoElement, maxDimension: number): PsScanRaster;
7
8
  private static context;
8
9
  }
@@ -30,6 +30,12 @@ class PsScanCanvasService {
30
30
  context.drawImage(video, 0, 0);
31
31
  return await PsScanCanvasService.encode(context.getImageData(0, 0, video.videoWidth, video.videoHeight), quality);
32
32
  }
33
+ static sample(video, maxDimension) {
34
+ const size = ps_scan_geometry_service_1.PsScanGeometryService.fitWithin({ height: video.videoHeight, width: video.videoWidth }, maxDimension);
35
+ const context = PsScanCanvasService.context(size.width, size.height);
36
+ context.drawImage(video, 0, 0, size.width, size.height);
37
+ return context.getImageData(0, 0, size.width, size.height);
38
+ }
33
39
  static context(width, height) {
34
40
  if (typeof OffscreenCanvas !== 'undefined') {
35
41
  const context = new OffscreenCanvas(width, height).getContext('2d', {
@@ -36,7 +36,7 @@ class PsScanEdgeService {
36
36
  return null;
37
37
  if (!ps_scan_geometry_service_1.PsScanGeometryService.isConvex(quad))
38
38
  return null;
39
- return quad.map((point) => ({
39
+ return ps_scan_geometry_service_1.PsScanGeometryService.inset(quad, ps_scan_config_1.PsScanConfig.detectInset).map((point) => ({
40
40
  x: Math.min(1, Math.max(0, point.x / (width - 1))),
41
41
  y: Math.min(1, Math.max(0, point.y / (height - 1))),
42
42
  }));
@@ -4,10 +4,12 @@ import type { PsScanQuad } from '../models/ps-scan-quad';
4
4
  export declare class PsScanGeometryService {
5
5
  static area(points: PsPoint[]): number;
6
6
  static clamp(point: PsPoint): PsPoint;
7
+ static cover(container: PsPageSize, content: PsPageSize): PsPageSize;
7
8
  static defaultQuad(): PsScanQuad;
8
9
  static distance(from: PsPoint, to: PsPoint): number;
9
10
  static fit(container: PsPageSize, content: PsPageSize): PsPageSize;
10
11
  static fitWithin(size: PsPageSize, maxDimension: number): PsPageSize;
12
+ static inset(quad: PsScanQuad, distance: number): PsScanQuad;
11
13
  static isConvex(points: PsPoint[]): boolean;
12
14
  static isUsable(quad: PsScanQuad): boolean;
13
15
  static nudge(quad: PsScanQuad, index: number, dx: number, dy: number): PsScanQuad;
@@ -14,6 +14,12 @@ class PsScanGeometryService {
14
14
  static clamp(point) {
15
15
  return { x: Math.min(1, Math.max(0, point.x)), y: Math.min(1, Math.max(0, point.y)) };
16
16
  }
17
+ static cover(container, content) {
18
+ if (container.width <= 0 || container.height <= 0 || content.width <= 0 || content.height <= 0)
19
+ return { height: 0, width: 0 };
20
+ const scale = Math.max(container.width / content.width, container.height / content.height);
21
+ return { height: content.height * scale, width: content.width * scale };
22
+ }
17
23
  static defaultQuad() {
18
24
  const inset = 0.08;
19
25
  return [
@@ -42,6 +48,20 @@ class PsScanGeometryService {
42
48
  width: Math.max(1, Math.round(size.width * scale)),
43
49
  };
44
50
  }
51
+ static inset(quad, distance) {
52
+ const centre = {
53
+ x: quad.reduce((sum, point) => sum + point.x, 0) / quad.length,
54
+ y: quad.reduce((sum, point) => sum + point.y, 0) / quad.length,
55
+ };
56
+ return quad.map((point) => {
57
+ const length = PsScanGeometryService.distance(point, centre);
58
+ const ratio = length <= distance ? 0 : (length - distance) / length;
59
+ return {
60
+ x: centre.x + (point.x - centre.x) * ratio,
61
+ y: centre.y + (point.y - centre.y) * ratio,
62
+ };
63
+ });
64
+ }
45
65
  static isConvex(points) {
46
66
  let sign = 0;
47
67
  for (let index = 0; index < points.length; index += 1) {
@@ -0,0 +1,4 @@
1
+ import { PsScanPlatform } from '../constants/ps-scan-platform';
2
+ export declare class PsScanPlatformService {
3
+ static detect(): PsScanPlatform;
4
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanPlatformService = void 0;
4
+ const ps_scan_platform_1 = require("../constants/ps-scan-platform");
5
+ class PsScanPlatformService {
6
+ static detect() {
7
+ if (typeof navigator === 'undefined')
8
+ return ps_scan_platform_1.PsScanPlatform.Desktop;
9
+ const agent = navigator.userAgent;
10
+ if (/iPhone|iPad|iPod/i.test(agent))
11
+ return ps_scan_platform_1.PsScanPlatform.Ios;
12
+ if (/Macintosh/i.test(agent) && navigator.maxTouchPoints > 1)
13
+ return ps_scan_platform_1.PsScanPlatform.Ios;
14
+ if (/Android/i.test(agent))
15
+ return ps_scan_platform_1.PsScanPlatform.Android;
16
+ return ps_scan_platform_1.PsScanPlatform.Desktop;
17
+ }
18
+ }
19
+ exports.PsScanPlatformService = PsScanPlatformService;
@@ -0,0 +1,9 @@
1
+ import type { PsScanQuad } from '../models/ps-scan-quad';
2
+ import type { PsScanStability } from '../models/ps-scan-stability';
3
+ export declare class PsScanStabilityService {
4
+ static disarm(state: PsScanStability): PsScanStability;
5
+ static initial(): PsScanStability;
6
+ static movement(from: PsScanQuad, to: PsScanQuad): number;
7
+ static next(state: PsScanStability, detected: PsScanQuad | null, elapsedMs: number): PsScanStability;
8
+ static progress(state: PsScanStability): number;
9
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PsScanStabilityService = void 0;
4
+ const ps_scan_config_1 = require("../constants/ps-scan-config");
5
+ const ps_scan_geometry_service_1 = require("./ps-scan-geometry-service");
6
+ class PsScanStabilityService {
7
+ static disarm(state) {
8
+ return { ...state, isArmed: false, stableMs: 0 };
9
+ }
10
+ static initial() {
11
+ return { isArmed: true, misses: 0, quad: null, raw: null, stableMs: 0 };
12
+ }
13
+ static movement(from, to) {
14
+ return Math.max(...from.map((point, index) => ps_scan_geometry_service_1.PsScanGeometryService.distance(point, to[index])));
15
+ }
16
+ static next(state, detected, elapsedMs) {
17
+ if (detected === null) {
18
+ const misses = state.misses + 1;
19
+ return misses > ps_scan_config_1.PsScanConfig.liveMissLimit
20
+ ? { isArmed: true, misses, quad: null, raw: null, stableMs: 0 }
21
+ : { ...state, misses, stableMs: 0 };
22
+ }
23
+ if (state.quad === null || state.raw === null)
24
+ return { ...state, misses: 0, quad: detected, raw: detected, stableMs: 0 };
25
+ const movement = PsScanStabilityService.movement(state.raw, detected);
26
+ const weight = ps_scan_config_1.PsScanConfig.liveSmoothing;
27
+ const quad = state.quad.map((point, index) => ({
28
+ x: point.x + (detected[index].x - point.x) * weight,
29
+ y: point.y + (detected[index].y - point.y) * weight,
30
+ }));
31
+ return {
32
+ isArmed: state.isArmed || movement > ps_scan_config_1.PsScanConfig.liveRearmMovement,
33
+ misses: 0,
34
+ quad,
35
+ raw: detected,
36
+ stableMs: movement <= ps_scan_config_1.PsScanConfig.liveStillMovement ? state.stableMs + elapsedMs : 0,
37
+ };
38
+ }
39
+ static progress(state) {
40
+ if (!state.isArmed || state.quad === null)
41
+ return 0;
42
+ return Math.min(1, state.stableMs / ps_scan_config_1.PsScanConfig.autoCaptureMs);
43
+ }
44
+ }
45
+ exports.PsScanStabilityService = PsScanStabilityService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pallasoft/ps-file-components",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -32,8 +32,8 @@
32
32
  "test": "vitest run"
33
33
  },
34
34
  "dependencies": {
35
- "@pallasoft/ps-core-components": "^2.27.0",
36
- "@pallasoft/ps-icons": "^1.8.1",
35
+ "@pallasoft/ps-core-components": "^2.29.0",
36
+ "@pallasoft/ps-icons": "^1.9.0",
37
37
  "@pallasoft/ps-popovers": "^1.10.2",
38
38
  "@pallasoft/ps-utilities": "^2.19.1",
39
39
  "pdfjs-dist": "^6.3.289"