capacitor-camera-multicapture 0.12.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.
Files changed (76) hide show
  1. package/CameraMultiCapture.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +551 -0
  5. package/android/build.gradle +69 -0
  6. package/android/src/main/AndroidManifest.xml +6 -0
  7. package/android/src/main/java/dev/hemang/cameramulticapture/CameraConfig.java +45 -0
  8. package/android/src/main/java/dev/hemang/cameramulticapture/CameraConfigMapper.java +98 -0
  9. package/android/src/main/java/dev/hemang/cameramulticapture/CameraMultiCapture.java +10 -0
  10. package/android/src/main/java/dev/hemang/cameramulticapture/CameraMultiCapturePlugin.java +1340 -0
  11. package/android/src/main/java/dev/hemang/cameramulticapture/ExifWrapper.java +108 -0
  12. package/android/src/main/java/dev/hemang/cameramulticapture/GenericUploadWorker.java +173 -0
  13. package/android/src/main/java/dev/hemang/cameramulticapture/ImageUtils.java +215 -0
  14. package/android/src/main/java/dev/hemang/cameramulticapture/PluginVersion.java +6 -0
  15. package/android/src/main/java/dev/hemang/cameramulticapture/ThumbnailGenerator.java +48 -0
  16. package/android/src/main/res/.gitkeep +0 -0
  17. package/dist/esm/controllers/camera-controller.d.ts +127 -0
  18. package/dist/esm/controllers/camera-controller.js +388 -0
  19. package/dist/esm/controllers/camera-controller.js.map +1 -0
  20. package/dist/esm/controllers/gallery-controller.d.ts +66 -0
  21. package/dist/esm/controllers/gallery-controller.js +333 -0
  22. package/dist/esm/controllers/gallery-controller.js.map +1 -0
  23. package/dist/esm/definitions.d.ts +372 -0
  24. package/dist/esm/definitions.js +6 -0
  25. package/dist/esm/definitions.js.map +1 -0
  26. package/dist/esm/index.d.ts +5 -0
  27. package/dist/esm/index.js +6 -0
  28. package/dist/esm/index.js.map +1 -0
  29. package/dist/esm/overlay-manager.d.ts +124 -0
  30. package/dist/esm/overlay-manager.js +732 -0
  31. package/dist/esm/overlay-manager.js.map +1 -0
  32. package/dist/esm/overlay.d.ts +2 -0
  33. package/dist/esm/overlay.js +39 -0
  34. package/dist/esm/overlay.js.map +1 -0
  35. package/dist/esm/registration.d.ts +5 -0
  36. package/dist/esm/registration.js +8 -0
  37. package/dist/esm/registration.js.map +1 -0
  38. package/dist/esm/types/ui-types.d.ts +78 -0
  39. package/dist/esm/types/ui-types.js +2 -0
  40. package/dist/esm/types/ui-types.js.map +1 -0
  41. package/dist/esm/ui/capture-gesture-handler.d.ts +15 -0
  42. package/dist/esm/ui/capture-gesture-handler.js +87 -0
  43. package/dist/esm/ui/capture-gesture-handler.js.map +1 -0
  44. package/dist/esm/ui/default-styles.d.ts +27 -0
  45. package/dist/esm/ui/default-styles.js +134 -0
  46. package/dist/esm/ui/default-styles.js.map +1 -0
  47. package/dist/esm/ui/image-editor.d.ts +10 -0
  48. package/dist/esm/ui/image-editor.js +103 -0
  49. package/dist/esm/ui/image-editor.js.map +1 -0
  50. package/dist/esm/ui/layout-manager.d.ts +42 -0
  51. package/dist/esm/ui/layout-manager.js +199 -0
  52. package/dist/esm/ui/layout-manager.js.map +1 -0
  53. package/dist/esm/ui/media-viewer.d.ts +2 -0
  54. package/dist/esm/ui/media-viewer.js +267 -0
  55. package/dist/esm/ui/media-viewer.js.map +1 -0
  56. package/dist/esm/ui/pinch-zoom-handler.d.ts +37 -0
  57. package/dist/esm/ui/pinch-zoom-handler.js +118 -0
  58. package/dist/esm/ui/pinch-zoom-handler.js.map +1 -0
  59. package/dist/esm/ui/ui-factory.d.ts +39 -0
  60. package/dist/esm/ui/ui-factory.js +272 -0
  61. package/dist/esm/ui/ui-factory.js.map +1 -0
  62. package/dist/esm/version.d.ts +1 -0
  63. package/dist/esm/version.js +3 -0
  64. package/dist/esm/version.js.map +1 -0
  65. package/dist/esm/web.d.ts +69 -0
  66. package/dist/esm/web.js +122 -0
  67. package/dist/esm/web.js.map +1 -0
  68. package/dist/plugin.cjs.js +6274 -0
  69. package/dist/plugin.cjs.js.map +1 -0
  70. package/dist/plugin.js +6276 -0
  71. package/dist/plugin.js.map +1 -0
  72. package/ios/Sources/CameraMultiCapturePlugin/CameraMultiCapture.swift +8 -0
  73. package/ios/Sources/CameraMultiCapturePlugin/CameraMultiCapturePlugin.swift +1551 -0
  74. package/ios/Sources/CameraMultiCapturePlugin/PluginVersion.swift +2 -0
  75. package/ios/Tests/CameraMultiCapturePluginTests/CameraMultiCapturePluginTests.swift +15 -0
  76. package/package.json +93 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../../../src/ui/media-viewer.ts"],"names":[],"mappings":"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","sourcesContent":["import BiggerPicture from 'bigger-picture';\n\nimport bpCss from 'bigger-picture/css-text';\n\nconst BP_STYLES_ID = 'cmmc-bigger-picture-styles';\n\ntype BiggerPictureInstance = ReturnType<typeof BiggerPicture>;\ntype BiggerPictureItem = {\n img?: string;\n thumb?: string;\n alt?: string;\n sources?: { src: string; type: string }[];\n width?: number;\n height?: number;\n};\n\nlet bpInstance: BiggerPictureInstance | null = null;\nlet activeBackHandler: (() => void) | null = null;\nconst DEFAULT_MEDIA_WIDTH = 1920;\nconst DEFAULT_MEDIA_HEIGHT = 1080;\n\nfunction ensureStyles(): void {\n const existing = document.getElementById(BP_STYLES_ID);\n if (existing && existing.tagName.toLowerCase() !== 'style') {\n existing.remove();\n }\n\n const style = document.getElementById(BP_STYLES_ID) as HTMLStyleElement | null;\n if (style) {\n return;\n }\n\n const styleEl = document.createElement('style');\n styleEl.id = BP_STYLES_ID;\n styleEl.textContent = `${bpCss}\n .bp-wrap,\n .bp-wrap * {\n animation-duration: 0ms !important;\n transition-duration: 0ms !important;\n transition-delay: 0ms !important;\n }\n .bp-wrap {\n --cmmc-safe-top-extra: 0px;\n --cmmc-safe-bottom-extra: 0px;\n }\n .bp-wrap > div:first-child {\n background: #000 !important;\n }\n .bp-x {\n top: env(safe-area-inset-top) !important;\n }\n .bp-img-wrap {\n top: calc(env(safe-area-inset-top) + var(--cmmc-safe-top-extra)) !important;\n bottom: calc(env(safe-area-inset-bottom) + var(--cmmc-safe-bottom-extra)) !important;\n height: auto !important;\n }\n .bp-vid,\n .bp-if {\n max-height: calc(100% - env(safe-area-inset-top) - env(safe-area-inset-bottom) - var(--cmmc-safe-top-extra) - var(--cmmc-safe-bottom-extra)) !important;\n margin-top: calc(env(safe-area-inset-top) + var(--cmmc-safe-top-extra)) !important;\n margin-bottom: calc(env(safe-area-inset-bottom) + var(--cmmc-safe-bottom-extra)) !important;\n }\n .bp-controls .cmmc-bp-edit {\n position: absolute;\n top: env(safe-area-inset-top);\n left: 0;\n z-index: 10;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n cursor: pointer;\n color: #fff;\n padding: 0;\n -webkit-tap-highlight-color: transparent;\n }\n .bp-controls .cmmc-bp-edit:hover {\n background: none;\n }\n .bp-controls .cmmc-bp-edit svg {\n filter: drop-shadow(0 1px 3px rgba(0,0,0,0.6));\n }\n `;\n document.head.appendChild(styleEl);\n}\n\nfunction getInstance(): BiggerPictureInstance {\n if (bpInstance) {\n return bpInstance;\n }\n\n bpInstance = BiggerPicture({\n target: document.body,\n });\n return bpInstance;\n}\n\nfunction getMimeType(src: string): string {\n const normalized = src.toLowerCase();\n if (normalized.endsWith('.webm')) return 'video/webm';\n if (normalized.endsWith('.mov')) return 'video/quicktime';\n return 'video/mp4';\n}\n\nfunction readSafeAreaInset(property: 'top' | 'bottom'): number {\n const probe = document.createElement('div');\n probe.style.position = 'fixed';\n probe.style.visibility = 'hidden';\n probe.style.pointerEvents = 'none';\n probe.style.zIndex = '-1';\n if (property === 'top') {\n probe.style.top = '0';\n probe.style.paddingTop = 'env(safe-area-inset-top)';\n } else {\n probe.style.bottom = '0';\n probe.style.paddingBottom = 'env(safe-area-inset-bottom)';\n }\n document.body.appendChild(probe);\n const computed = window.getComputedStyle(probe);\n const raw = property === 'top' ? computed.paddingTop : computed.paddingBottom;\n probe.remove();\n const parsed = Number.parseFloat(raw);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction applyDynamicSafeArea(container: HTMLElement): void {\n const closeButton = container.querySelector('.bp-x') as HTMLElement | null;\n const media = container.querySelector('.bp-vid, .bp-if, .bp-img') as HTMLElement | null;\n if (!closeButton || !media) {\n container.style.setProperty('--cmmc-safe-top-extra', '0px');\n container.style.setProperty('--cmmc-safe-bottom-extra', '0px');\n return;\n }\n\n const closeRect = closeButton.getBoundingClientRect();\n const mediaRect = media.getBoundingClientRect();\n const safeBottom = readSafeAreaInset('bottom');\n\n const topOverlap = Math.max(0, closeRect.bottom - mediaRect.top);\n const bottomLimit = window.innerHeight - safeBottom;\n const bottomOverlap = Math.max(0, mediaRect.bottom - bottomLimit);\n\n container.style.setProperty('--cmmc-safe-top-extra', `${topOverlap}px`);\n container.style.setProperty('--cmmc-safe-bottom-extra', `${bottomOverlap}px`);\n}\n\nfunction getImageDimensions(src: string): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const img = new Image();\n img.onload = () => {\n if (img.naturalWidth > 0 && img.naturalHeight > 0) {\n resolve({ width: img.naturalWidth, height: img.naturalHeight });\n } else {\n resolve(null);\n }\n };\n img.onerror = () => resolve(null);\n img.src = src;\n });\n}\n\nfunction getVideoDimensions(src: string): Promise<{ width: number; height: number } | null> {\n return new Promise((resolve) => {\n const video = document.createElement('video');\n video.preload = 'metadata';\n video.playsInline = true;\n\n const cleanup = () => {\n video.removeAttribute('src');\n video.load();\n };\n\n video.onloadedmetadata = () => {\n const width = video.videoWidth;\n const height = video.videoHeight;\n cleanup();\n if (width > 0 && height > 0) {\n resolve({ width, height });\n } else {\n resolve(null);\n }\n };\n video.onerror = () => {\n cleanup();\n resolve(null);\n };\n\n video.src = src;\n });\n}\n\nfunction injectEditButton(container: HTMLElement, onEdit: () => void): void {\n const controls = container.querySelector('.bp-controls');\n if (!controls || controls.querySelector('.cmmc-bp-edit')) return;\n\n const btn = document.createElement('button');\n btn.className = 'cmmc-bp-edit';\n btn.innerHTML = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"22\" height=\"22\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z\"/></svg>`;\n\n btn.onclick = (e) => {\n e.stopPropagation();\n getInstance().close();\n onEdit();\n };\n\n controls.appendChild(btn);\n}\n\nfunction registerBackHandler(): void {\n removeBackHandler();\n const handler = () => {\n getInstance().close();\n };\n activeBackHandler = handler;\n document.addEventListener('backbutton', handler);\n}\n\nfunction removeBackHandler(): void {\n if (activeBackHandler) {\n document.removeEventListener('backbutton', activeBackHandler);\n activeBackHandler = null;\n }\n}\n\nexport function openImagePreview(src: string, thumb?: string, onEdit?: () => void): void {\n void (async () => {\n ensureStyles();\n const dimensions = await getImageDimensions(src);\n\n const items: BiggerPictureItem[] = [\n {\n img: src,\n thumb: thumb || src,\n width: dimensions?.width ?? DEFAULT_MEDIA_WIDTH,\n height: dimensions?.height ?? DEFAULT_MEDIA_HEIGHT,\n },\n ];\n\n getInstance().open({\n items,\n el: document.body,\n scale: 1,\n intro: 'fadeup',\n onOpen: (container) => {\n applyDynamicSafeArea(container);\n registerBackHandler();\n if (onEdit) {\n injectEditButton(container, onEdit);\n }\n },\n onClose: () => {\n removeBackHandler();\n },\n onResize: (container) => {\n if (!container) return;\n applyDynamicSafeArea(container);\n },\n });\n })();\n}\n\nexport function openVideoPreview(src: string, thumb?: string): void {\n void (async () => {\n ensureStyles();\n const dimensions = await getVideoDimensions(src);\n\n const items: BiggerPictureItem[] = [\n {\n thumb: thumb || src,\n sources: [{ src, type: getMimeType(src) }],\n width: dimensions?.width ?? DEFAULT_MEDIA_WIDTH,\n height: dimensions?.height ?? DEFAULT_MEDIA_HEIGHT,\n },\n ];\n\n getInstance().open({\n items,\n el: document.body,\n scale: 1,\n intro: 'fadeup',\n onOpen: (container) => {\n applyDynamicSafeArea(container);\n registerBackHandler();\n },\n onClose: () => {\n removeBackHandler();\n },\n onResize: (container) => {\n if (!container) return;\n applyDynamicSafeArea(container);\n },\n });\n })();\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import type { CameraOverlayUIOptions } from '../types/ui-types';
2
+ import { CameraController } from '../controllers/camera-controller';
3
+ export interface PinchZoomConfig {
4
+ options: CameraOverlayUIOptions;
5
+ }
6
+ export type ZoomSelectionUpdater = (zoom: number) => void;
7
+ /**
8
+ * Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.
9
+ */
10
+ export declare class PinchZoomHandler {
11
+ private readonly cameraController;
12
+ private readonly config;
13
+ private readonly updateZoomSelection;
14
+ private pinchElement;
15
+ private pinchStartDistance;
16
+ private pinchStartZoom;
17
+ private pinchMinZoom;
18
+ private pinchMaxZoom;
19
+ private pinchPresetLevels;
20
+ private isPinching;
21
+ private boundPinchStart;
22
+ private boundPinchMove;
23
+ private boundPinchEnd;
24
+ constructor(cameraController: CameraController, config: PinchZoomConfig, updateZoomSelection: ZoomSelectionUpdater);
25
+ /**
26
+ * Attach pinch handlers to the given container and initialise zoom ranges.
27
+ */
28
+ attach(container: HTMLElement): Promise<void>;
29
+ /**
30
+ * Detach pinch handlers and clear state.
31
+ */
32
+ detach(): void;
33
+ private getPinchDistance;
34
+ private onPinchStart;
35
+ private onPinchMove;
36
+ private onPinchEnd;
37
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.
3
+ */
4
+ export class PinchZoomHandler {
5
+ constructor(cameraController, config, updateZoomSelection) {
6
+ this.cameraController = cameraController;
7
+ this.config = config;
8
+ this.updateZoomSelection = updateZoomSelection;
9
+ this.pinchElement = null;
10
+ this.pinchStartDistance = 0;
11
+ this.pinchStartZoom = 1;
12
+ this.pinchMinZoom = 1;
13
+ this.pinchMaxZoom = 1;
14
+ this.pinchPresetLevels = [];
15
+ this.isPinching = false;
16
+ this.boundPinchStart = null;
17
+ this.boundPinchMove = null;
18
+ this.boundPinchEnd = null;
19
+ }
20
+ /**
21
+ * Attach pinch handlers to the given container and initialise zoom ranges.
22
+ */
23
+ async attach(container) {
24
+ var _a;
25
+ try {
26
+ const zoomInfo = await this.cameraController.getAvailableZoomLevels();
27
+ this.pinchMinZoom = zoomInfo.minZoom;
28
+ this.pinchMaxZoom = zoomInfo.maxZoom;
29
+ this.pinchPresetLevels = ((_a = zoomInfo.presetLevels) === null || _a === void 0 ? void 0 : _a.length)
30
+ ? zoomInfo.presetLevels
31
+ : [zoomInfo.minZoom, 1, zoomInfo.maxZoom];
32
+ }
33
+ catch (_b) {
34
+ this.pinchMinZoom = 1;
35
+ this.pinchMaxZoom = 10;
36
+ this.pinchPresetLevels = [1, 2, 3, 5, 10];
37
+ }
38
+ this.pinchElement = container;
39
+ this.boundPinchStart = this.onPinchStart.bind(this);
40
+ this.boundPinchMove = this.onPinchMove.bind(this);
41
+ this.boundPinchEnd = this.onPinchEnd.bind(this);
42
+ container.addEventListener('touchstart', this.boundPinchStart, { passive: true });
43
+ container.addEventListener('touchmove', this.boundPinchMove, { passive: false });
44
+ container.addEventListener('touchend', this.boundPinchEnd, { passive: true });
45
+ container.addEventListener('touchcancel', this.boundPinchEnd, { passive: true });
46
+ }
47
+ /**
48
+ * Detach pinch handlers and clear state.
49
+ */
50
+ detach() {
51
+ const el = this.pinchElement;
52
+ if (!el || !this.boundPinchStart || !this.boundPinchMove || !this.boundPinchEnd)
53
+ return;
54
+ el.removeEventListener('touchstart', this.boundPinchStart);
55
+ el.removeEventListener('touchmove', this.boundPinchMove);
56
+ el.removeEventListener('touchend', this.boundPinchEnd);
57
+ el.removeEventListener('touchcancel', this.boundPinchEnd);
58
+ this.pinchElement = null;
59
+ this.boundPinchStart = null;
60
+ this.boundPinchMove = null;
61
+ this.boundPinchEnd = null;
62
+ }
63
+ getPinchDistance(touches) {
64
+ if (touches.length < 2)
65
+ return 0;
66
+ const a = touches[0];
67
+ const b = touches[1];
68
+ return Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);
69
+ }
70
+ onPinchStart(e) {
71
+ if (e.touches.length === 2) {
72
+ this.isPinching = true;
73
+ this.pinchStartDistance = this.getPinchDistance(e.touches);
74
+ this.pinchStartZoom = this.cameraController.getCurrentZoom();
75
+ }
76
+ }
77
+ onPinchMove(e) {
78
+ if (e.touches.length !== 2 || !this.isPinching || this.pinchStartDistance <= 0)
79
+ return;
80
+ e.preventDefault();
81
+ const dist = this.getPinchDistance(e.touches);
82
+ const scale = dist / this.pinchStartDistance;
83
+ let zoom = this.pinchStartZoom * scale;
84
+ zoom = Math.max(this.pinchMinZoom, Math.min(this.pinchMaxZoom, zoom));
85
+ this.cameraController.setZoom(zoom).catch((err) => console.warn('Set zoom failed', err));
86
+ this.updateZoomSelection(zoom);
87
+ }
88
+ onPinchEnd(e) {
89
+ var _a;
90
+ if (e.touches.length >= 2)
91
+ return;
92
+ if (!this.isPinching)
93
+ return;
94
+ this.isPinching = false;
95
+ this.pinchStartDistance = 0;
96
+ const lockToNearest = ((_a = this.config.options.pinchToZoom) === null || _a === void 0 ? void 0 : _a.lockToNearestStep) === true;
97
+ if (lockToNearest && this.pinchPresetLevels.length > 0) {
98
+ const current = this.cameraController.getCurrentZoom();
99
+ let nearest = this.pinchPresetLevels[0];
100
+ let best = Math.abs(nearest - current);
101
+ for (const level of this.pinchPresetLevels) {
102
+ const d = Math.abs(level - current);
103
+ if (d < best) {
104
+ best = d;
105
+ nearest = level;
106
+ }
107
+ }
108
+ this.cameraController.setZoom(nearest).catch((err) => console.warn('Set zoom failed', err));
109
+ this.updateZoomSelection(nearest);
110
+ }
111
+ else {
112
+ // Even without snapping, move the highlight to the closest zoom button
113
+ const current = this.cameraController.getCurrentZoom();
114
+ this.updateZoomSelection(current);
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=pinch-zoom-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pinch-zoom-handler.js","sourceRoot":"","sources":["../../../src/ui/pinch-zoom-handler.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CameraOverlayUIOptions } from '../types/ui-types';\nimport { CameraController } from '../controllers/camera-controller';\n\nexport interface PinchZoomConfig {\n options: CameraOverlayUIOptions;\n}\n\nexport type ZoomSelectionUpdater = (zoom: number) => void;\n\n/**\n * Encapsulates JavaScript pinch-to-zoom gesture handling for the camera overlay.\n */\nexport class PinchZoomHandler {\n private pinchElement: HTMLElement | null = null;\n private pinchStartDistance: number = 0;\n private pinchStartZoom: number = 1;\n private pinchMinZoom: number = 1;\n private pinchMaxZoom: number = 1;\n private pinchPresetLevels: number[] = [];\n private isPinching = false;\n private boundPinchStart: ((e: TouchEvent) => void) | null = null;\n private boundPinchMove: ((e: TouchEvent) => void) | null = null;\n private boundPinchEnd: ((e: TouchEvent) => void) | null = null;\n\n constructor(\n private readonly cameraController: CameraController,\n private readonly config: PinchZoomConfig,\n private readonly updateZoomSelection: ZoomSelectionUpdater\n ) {}\n\n /**\n * Attach pinch handlers to the given container and initialise zoom ranges.\n */\n async attach(container: HTMLElement): Promise<void> {\n try {\n const zoomInfo = await this.cameraController.getAvailableZoomLevels();\n this.pinchMinZoom = zoomInfo.minZoom;\n this.pinchMaxZoom = zoomInfo.maxZoom;\n this.pinchPresetLevels = zoomInfo.presetLevels?.length\n ? zoomInfo.presetLevels\n : [zoomInfo.minZoom, 1, zoomInfo.maxZoom];\n } catch {\n this.pinchMinZoom = 1;\n this.pinchMaxZoom = 10;\n this.pinchPresetLevels = [1, 2, 3, 5, 10];\n }\n\n this.pinchElement = container;\n this.boundPinchStart = this.onPinchStart.bind(this);\n this.boundPinchMove = this.onPinchMove.bind(this);\n this.boundPinchEnd = this.onPinchEnd.bind(this);\n container.addEventListener('touchstart', this.boundPinchStart, { passive: true });\n container.addEventListener('touchmove', this.boundPinchMove, { passive: false });\n container.addEventListener('touchend', this.boundPinchEnd, { passive: true });\n container.addEventListener('touchcancel', this.boundPinchEnd, { passive: true });\n }\n\n /**\n * Detach pinch handlers and clear state.\n */\n detach(): void {\n const el = this.pinchElement;\n if (!el || !this.boundPinchStart || !this.boundPinchMove || !this.boundPinchEnd) return;\n el.removeEventListener('touchstart', this.boundPinchStart);\n el.removeEventListener('touchmove', this.boundPinchMove);\n el.removeEventListener('touchend', this.boundPinchEnd);\n el.removeEventListener('touchcancel', this.boundPinchEnd);\n this.pinchElement = null;\n this.boundPinchStart = null;\n this.boundPinchMove = null;\n this.boundPinchEnd = null;\n }\n\n private getPinchDistance(touches: TouchList): number {\n if (touches.length < 2) return 0;\n const a = touches[0];\n const b = touches[1];\n return Math.hypot(b.clientX - a.clientX, b.clientY - a.clientY);\n }\n\n private onPinchStart(e: TouchEvent): void {\n if (e.touches.length === 2) {\n this.isPinching = true;\n this.pinchStartDistance = this.getPinchDistance(e.touches);\n this.pinchStartZoom = this.cameraController.getCurrentZoom();\n }\n }\n\n private onPinchMove(e: TouchEvent): void {\n if (e.touches.length !== 2 || !this.isPinching || this.pinchStartDistance <= 0) return;\n e.preventDefault();\n const dist = this.getPinchDistance(e.touches);\n const scale = dist / this.pinchStartDistance;\n let zoom = this.pinchStartZoom * scale;\n zoom = Math.max(this.pinchMinZoom, Math.min(this.pinchMaxZoom, zoom));\n this.cameraController.setZoom(zoom).catch((err) => console.warn('Set zoom failed', err));\n this.updateZoomSelection(zoom);\n }\n\n private onPinchEnd(e: TouchEvent): void {\n if (e.touches.length >= 2) return;\n if (!this.isPinching) return;\n this.isPinching = false;\n this.pinchStartDistance = 0;\n const lockToNearest = this.config.options.pinchToZoom?.lockToNearestStep === true;\n if (lockToNearest && this.pinchPresetLevels.length > 0) {\n const current = this.cameraController.getCurrentZoom();\n let nearest = this.pinchPresetLevels[0];\n let best = Math.abs(nearest - current);\n for (const level of this.pinchPresetLevels) {\n const d = Math.abs(level - current);\n if (d < best) {\n best = d;\n nearest = level;\n }\n }\n this.cameraController.setZoom(nearest).catch((err) => console.warn('Set zoom failed', err));\n this.updateZoomSelection(nearest);\n } else {\n // Even without snapping, move the highlight to the closest zoom button\n const current = this.cameraController.getCurrentZoom();\n this.updateZoomSelection(current);\n }\n }\n}\n\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Factory for creating UI elements
3
+ */
4
+ import { ButtonStyle, IconButtonConfig } from '../types/ui-types';
5
+ /**
6
+ * Sets the icon for a button element, supporting both inline SVG and file paths
7
+ */
8
+ export declare function setButtonIcon(element: HTMLButtonElement, iconSource: string): Promise<void>;
9
+ /**
10
+ * Creates a button with the specified configuration
11
+ */
12
+ export declare function createButton(config: IconButtonConfig, text?: string): HTMLButtonElement;
13
+ /**
14
+ * Applies styles to a button element
15
+ */
16
+ export declare function applyButtonStyle(element: HTMLButtonElement, style: ButtonStyle): void;
17
+ /**
18
+ * Creates a thumbnail image container
19
+ */
20
+ export declare function createThumbnailContainer(thumbnailData: string, thumbnailStyle: {
21
+ width?: string;
22
+ height?: string;
23
+ }, onRemove: (() => void) | null, options?: {
24
+ isVideo?: boolean;
25
+ duration?: number;
26
+ onTap?: () => void;
27
+ }): HTMLElement;
28
+ /**
29
+ * Creates a remove button for thumbnails
30
+ */
31
+ export declare function createRemoveButton(onRemove: (e: Event) => void): HTMLButtonElement;
32
+ /**
33
+ * Creates a shot counter element with animation
34
+ */
35
+ export declare function createShotCounter(): HTMLElement;
36
+ /**
37
+ * Updates the shot counter with animation
38
+ */
39
+ export declare function updateShotCounter(counter: HTMLElement, count: number): void;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Loads an icon from either an inline SVG string or a file path
3
+ */
4
+ async function loadIcon(iconSource) {
5
+ // Check if it's an inline SVG (starts with <svg)
6
+ if (iconSource.trim().startsWith('<svg')) {
7
+ return iconSource;
8
+ }
9
+ // Check if it's a file path (ends with .svg)
10
+ if (iconSource.endsWith('.svg')) {
11
+ try {
12
+ const response = await fetch(iconSource);
13
+ if (!response.ok) {
14
+ throw new Error(`Failed to load SVG: ${response.status} ${response.statusText}`);
15
+ }
16
+ const svgContent = await response.text();
17
+ return svgContent;
18
+ }
19
+ catch (error) {
20
+ console.error(`Failed to load SVG from path: ${iconSource}`, error);
21
+ // Re-throw error - let the existing fallback system handle it
22
+ throw error;
23
+ }
24
+ }
25
+ // If it's neither, treat as inline SVG
26
+ return iconSource;
27
+ }
28
+ /**
29
+ * Sets the icon for a button element, supporting both inline SVG and file paths
30
+ */
31
+ export async function setButtonIcon(element, iconSource) {
32
+ // Only proceed if iconSource is provided and not empty
33
+ if (!iconSource || iconSource.trim() === '') {
34
+ return;
35
+ }
36
+ const svgContent = await loadIcon(iconSource);
37
+ element.innerHTML = svgContent;
38
+ // Apply size styling if specified
39
+ const style = element.__buttonStyle;
40
+ if ((style === null || style === void 0 ? void 0 : style.size) && element.querySelector('svg')) {
41
+ const svg = element.querySelector('svg');
42
+ svg === null || svg === void 0 ? void 0 : svg.setAttribute('width', `${style.size}px`);
43
+ svg === null || svg === void 0 ? void 0 : svg.setAttribute('height', `${style.size}px`);
44
+ }
45
+ }
46
+ /**
47
+ * Creates a button with the specified configuration
48
+ */
49
+ export function createButton(config, text) {
50
+ const btn = document.createElement('button');
51
+ // Store style reference for later use in setButtonIcon
52
+ btn.__buttonStyle = config.style;
53
+ if (text) {
54
+ btn.textContent = text;
55
+ }
56
+ else if (config.text) {
57
+ btn.textContent = config.text;
58
+ }
59
+ else if (config.icon) {
60
+ // Only load icon if one is provided
61
+ setButtonIcon(btn, config.icon).catch(error => {
62
+ console.error('Failed to set button icon:', error);
63
+ });
64
+ }
65
+ const style = config.style || {};
66
+ applyButtonStyle(btn, style);
67
+ return btn;
68
+ }
69
+ /**
70
+ * Applies styles to a button element
71
+ */
72
+ export function applyButtonStyle(element, style) {
73
+ Object.assign(element.style, {
74
+ borderRadius: style.radius ? `${style.radius}px` : '30px',
75
+ padding: style.padding || '10px',
76
+ backgroundColor: style.backgroundColor || '#ffffff',
77
+ color: style.color || '#000000',
78
+ border: style.border || 'none',
79
+ opacity: style.opacity !== undefined ? style.opacity.toString() : '1',
80
+ boxShadow: style.boxShadow || 'none',
81
+ filter: style.filter || 'none',
82
+ display: 'flex',
83
+ alignItems: 'center',
84
+ justifyContent: 'center',
85
+ cursor: 'pointer',
86
+ outline: 'none',
87
+ minWidth: '44px',
88
+ minHeight: '44px'
89
+ });
90
+ if (style.size && element.querySelector('svg')) {
91
+ const svg = element.querySelector('svg');
92
+ svg === null || svg === void 0 ? void 0 : svg.setAttribute('width', `${style.size}px`);
93
+ svg === null || svg === void 0 ? void 0 : svg.setAttribute('height', `${style.size}px`);
94
+ }
95
+ }
96
+ /**
97
+ * Creates a thumbnail image container
98
+ */
99
+ export function createThumbnailContainer(thumbnailData, thumbnailStyle, onRemove, options) {
100
+ const thumbnailContainer = document.createElement('div');
101
+ const width = (thumbnailStyle === null || thumbnailStyle === void 0 ? void 0 : thumbnailStyle.width) || '80px';
102
+ const height = (thumbnailStyle === null || thumbnailStyle === void 0 ? void 0 : thumbnailStyle.height) || '80px';
103
+ Object.assign(thumbnailContainer.style, {
104
+ position: 'relative',
105
+ display: 'inline-block',
106
+ width: width,
107
+ height: height,
108
+ marginRight: '10px',
109
+ borderRadius: '4px',
110
+ overflow: 'hidden',
111
+ flexShrink: '0',
112
+ boxSizing: 'border-box',
113
+ contentVisibility: 'auto',
114
+ containIntrinsicSize: `${width} ${height}`
115
+ });
116
+ const thumbnail = document.createElement('img');
117
+ thumbnail.src = thumbnailData;
118
+ thumbnail.loading = 'lazy';
119
+ thumbnail.decoding = 'async';
120
+ Object.assign(thumbnail.style, {
121
+ width: '100%',
122
+ height: '100%',
123
+ objectFit: 'cover',
124
+ borderRadius: '4px',
125
+ cursor: 'pointer'
126
+ });
127
+ if (options === null || options === void 0 ? void 0 : options.onTap) {
128
+ const tapHandler = options.onTap;
129
+ thumbnail.addEventListener('click', (e) => {
130
+ e.stopPropagation();
131
+ tapHandler();
132
+ });
133
+ }
134
+ thumbnailContainer.appendChild(thumbnail);
135
+ if (options === null || options === void 0 ? void 0 : options.isVideo) {
136
+ const playBadge = document.createElement('div');
137
+ playBadge.innerHTML = '▶';
138
+ Object.assign(playBadge.style, {
139
+ position: 'absolute',
140
+ left: '50%',
141
+ top: '50%',
142
+ transform: 'translate(-50%, -50%)',
143
+ width: '28px',
144
+ height: '28px',
145
+ borderRadius: '50%',
146
+ background: 'rgba(0, 0, 0, 0.55)',
147
+ color: '#ffffff',
148
+ display: 'flex',
149
+ alignItems: 'center',
150
+ justifyContent: 'center',
151
+ fontSize: '14px',
152
+ zIndex: '2',
153
+ pointerEvents: 'none'
154
+ });
155
+ thumbnailContainer.appendChild(playBadge);
156
+ if (typeof options.duration === 'number') {
157
+ const durationBadge = document.createElement('div');
158
+ durationBadge.textContent = formatDuration(options.duration);
159
+ Object.assign(durationBadge.style, {
160
+ position: 'absolute',
161
+ left: '4px',
162
+ bottom: '4px',
163
+ background: 'rgba(0, 0, 0, 0.7)',
164
+ color: '#ffffff',
165
+ fontSize: '10px',
166
+ lineHeight: '14px',
167
+ borderRadius: '8px',
168
+ padding: '1px 6px',
169
+ zIndex: '2',
170
+ pointerEvents: 'none'
171
+ });
172
+ thumbnailContainer.appendChild(durationBadge);
173
+ }
174
+ }
175
+ if (onRemove) {
176
+ const removeBtn = createRemoveButton(onRemove);
177
+ thumbnailContainer.appendChild(removeBtn);
178
+ }
179
+ return thumbnailContainer;
180
+ }
181
+ function formatDuration(seconds) {
182
+ const safe = Math.max(0, Math.floor(seconds));
183
+ const mins = Math.floor(safe / 60);
184
+ const secs = safe % 60;
185
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
186
+ }
187
+ /**
188
+ * Creates a remove button for thumbnails
189
+ */
190
+ export function createRemoveButton(onRemove) {
191
+ const removeBtn = document.createElement('button');
192
+ Object.assign(removeBtn.style, {
193
+ position: 'absolute',
194
+ top: '3px',
195
+ right: '3px',
196
+ width: '24px',
197
+ height: '24px',
198
+ borderRadius: '50%',
199
+ background: 'rgba(0, 0, 0, 0.5)',
200
+ color: 'white',
201
+ border: 'none',
202
+ cursor: 'pointer',
203
+ display: 'flex',
204
+ justifyContent: 'center',
205
+ alignItems: 'center',
206
+ padding: '0',
207
+ fontSize: '14px',
208
+ fontWeight: 'bold',
209
+ zIndex: '3'
210
+ });
211
+ removeBtn.innerHTML = '×';
212
+ removeBtn.onclick = (e) => {
213
+ e.stopPropagation();
214
+ onRemove(e);
215
+ };
216
+ return removeBtn;
217
+ }
218
+ /**
219
+ * Creates a shot counter element with animation
220
+ */
221
+ export function createShotCounter() {
222
+ const counter = document.createElement('div');
223
+ counter.id = 'shot-counter';
224
+ Object.assign(counter.style, {
225
+ position: 'relative',
226
+ display: 'flex',
227
+ alignItems: 'center',
228
+ justifyContent: 'center',
229
+ width: '40px',
230
+ height: '40px',
231
+ borderRadius: '20px',
232
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
233
+ color: '#ffffff',
234
+ fontSize: '16px',
235
+ fontWeight: 'bold',
236
+ margin: '0', // Remove default margin since it's now handled by container gap
237
+ transition: 'all 0.3s ease',
238
+ transform: 'scale(1)',
239
+ opacity: '0',
240
+ pointerEvents: 'none',
241
+ zIndex: '3'
242
+ });
243
+ counter.textContent = '0';
244
+ return counter;
245
+ }
246
+ /**
247
+ * Updates the shot counter with animation
248
+ */
249
+ export function updateShotCounter(counter, count) {
250
+ const displayCount = count > 99 ? '99+' : count.toString();
251
+ // Show counter if it's hidden and count > 0
252
+ if (count > 0 && counter.style.opacity === '0') {
253
+ counter.style.opacity = '1';
254
+ }
255
+ // Hide counter if count is 0
256
+ if (count === 0) {
257
+ counter.style.opacity = '0';
258
+ counter.textContent = '0';
259
+ return;
260
+ }
261
+ // Animation effect
262
+ counter.style.transform = 'scale(1.2)';
263
+ counter.style.backgroundColor = 'rgba(40, 167, 69, 0.8)'; // Green flash
264
+ // Update the text
265
+ counter.textContent = displayCount;
266
+ // Reset animation after a short delay
267
+ setTimeout(() => {
268
+ counter.style.transform = 'scale(1)';
269
+ counter.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
270
+ }, 200);
271
+ }
272
+ //# sourceMappingURL=ui-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-factory.js","sourceRoot":"","sources":["../../../src/ui/ui-factory.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Factory for creating UI elements\n */\nimport { ButtonStyle, IconButtonConfig } from '../types/ui-types';\n\n/**\n * Loads an icon from either an inline SVG string or a file path\n */\nasync function loadIcon(iconSource: string): Promise<string> {\n // Check if it's an inline SVG (starts with <svg)\n if (iconSource.trim().startsWith('<svg')) {\n return iconSource;\n }\n \n // Check if it's a file path (ends with .svg)\n if (iconSource.endsWith('.svg')) {\n try {\n const response = await fetch(iconSource);\n if (!response.ok) {\n throw new Error(`Failed to load SVG: ${response.status} ${response.statusText}`);\n }\n const svgContent = await response.text();\n return svgContent;\n } catch (error) {\n console.error(`Failed to load SVG from path: ${iconSource}`, error);\n // Re-throw error - let the existing fallback system handle it\n throw error;\n }\n }\n \n // If it's neither, treat as inline SVG\n return iconSource;\n}\n\n/**\n * Sets the icon for a button element, supporting both inline SVG and file paths\n */\nexport async function setButtonIcon(element: HTMLButtonElement, iconSource: string): Promise<void> {\n // Only proceed if iconSource is provided and not empty\n if (!iconSource || iconSource.trim() === '') {\n return;\n }\n \n const svgContent = await loadIcon(iconSource);\n element.innerHTML = svgContent;\n \n // Apply size styling if specified\n const style = (element as any).__buttonStyle as ButtonStyle;\n if (style?.size && element.querySelector('svg')) {\n const svg = element.querySelector('svg');\n svg?.setAttribute('width', `${style.size}px`);\n svg?.setAttribute('height', `${style.size}px`);\n }\n}\n\n/**\n * Creates a button with the specified configuration\n */\nexport function createButton(config: IconButtonConfig, text?: string): HTMLButtonElement {\n const btn = document.createElement('button');\n \n // Store style reference for later use in setButtonIcon\n (btn as any).__buttonStyle = config.style;\n \n if (text) {\n btn.textContent = text;\n } else if (config.text) {\n btn.textContent = config.text;\n } else if (config.icon) {\n // Only load icon if one is provided\n setButtonIcon(btn, config.icon).catch(error => {\n console.error('Failed to set button icon:', error);\n });\n }\n \n const style = config.style || {};\n \n applyButtonStyle(btn, style);\n \n return btn;\n}\n\n/**\n * Applies styles to a button element\n */\nexport function applyButtonStyle(element: HTMLButtonElement, style: ButtonStyle): void {\n Object.assign(element.style, {\n borderRadius: style.radius ? `${style.radius}px` : '30px',\n padding: style.padding || '10px',\n backgroundColor: style.backgroundColor || '#ffffff',\n color: style.color || '#000000',\n border: style.border || 'none',\n opacity: style.opacity !== undefined ? style.opacity.toString() : '1',\n boxShadow: style.boxShadow || 'none',\n filter: style.filter || 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n cursor: 'pointer',\n outline: 'none',\n minWidth: '44px',\n minHeight: '44px'\n });\n \n if (style.size && element.querySelector('svg')) {\n const svg = element.querySelector('svg');\n svg?.setAttribute('width', `${style.size}px`);\n svg?.setAttribute('height', `${style.size}px`);\n }\n}\n\n/**\n * Creates a thumbnail image container\n */\nexport function createThumbnailContainer(\n thumbnailData: string, \n thumbnailStyle: { width?: string; height?: string },\n onRemove: (() => void) | null,\n options?: { isVideo?: boolean; duration?: number; onTap?: () => void }\n): HTMLElement {\n const thumbnailContainer = document.createElement('div');\n const width = thumbnailStyle?.width || '80px';\n const height = thumbnailStyle?.height || '80px';\n Object.assign(thumbnailContainer.style, {\n position: 'relative',\n display: 'inline-block',\n width: width,\n height: height,\n marginRight: '10px',\n borderRadius: '4px',\n overflow: 'hidden',\n flexShrink: '0',\n boxSizing: 'border-box',\n contentVisibility: 'auto',\n containIntrinsicSize: `${width} ${height}`\n });\n \n const thumbnail = document.createElement('img');\n thumbnail.src = thumbnailData;\n thumbnail.loading = 'lazy';\n thumbnail.decoding = 'async';\n \n Object.assign(thumbnail.style, {\n width: '100%',\n height: '100%',\n objectFit: 'cover',\n borderRadius: '4px',\n cursor: 'pointer'\n });\n \n if (options?.onTap) {\n const tapHandler = options.onTap;\n thumbnail.addEventListener('click', (e) => {\n e.stopPropagation();\n tapHandler();\n });\n }\n\n thumbnailContainer.appendChild(thumbnail);\n\n if (options?.isVideo) {\n const playBadge = document.createElement('div');\n playBadge.innerHTML = '▶';\n Object.assign(playBadge.style, {\n position: 'absolute',\n left: '50%',\n top: '50%',\n transform: 'translate(-50%, -50%)',\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n background: 'rgba(0, 0, 0, 0.55)',\n color: '#ffffff',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '14px',\n zIndex: '2',\n pointerEvents: 'none'\n });\n thumbnailContainer.appendChild(playBadge);\n\n if (typeof options.duration === 'number') {\n const durationBadge = document.createElement('div');\n durationBadge.textContent = formatDuration(options.duration);\n Object.assign(durationBadge.style, {\n position: 'absolute',\n left: '4px',\n bottom: '4px',\n background: 'rgba(0, 0, 0, 0.7)',\n color: '#ffffff',\n fontSize: '10px',\n lineHeight: '14px',\n borderRadius: '8px',\n padding: '1px 6px',\n zIndex: '2',\n pointerEvents: 'none'\n });\n thumbnailContainer.appendChild(durationBadge);\n }\n }\n\n if (onRemove) {\n const removeBtn = createRemoveButton(onRemove);\n thumbnailContainer.appendChild(removeBtn);\n }\n \n return thumbnailContainer;\n}\n\nfunction formatDuration(seconds: number): string {\n const safe = Math.max(0, Math.floor(seconds));\n const mins = Math.floor(safe / 60);\n const secs = safe % 60;\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n}\n\n/**\n * Creates a remove button for thumbnails\n */\nexport function createRemoveButton(onRemove: (e: Event) => void): HTMLButtonElement {\n const removeBtn = document.createElement('button');\n Object.assign(removeBtn.style, {\n position: 'absolute',\n top: '3px',\n right: '3px',\n width: '24px',\n height: '24px',\n borderRadius: '50%',\n background: 'rgba(0, 0, 0, 0.5)',\n color: 'white',\n border: 'none',\n cursor: 'pointer',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n padding: '0',\n fontSize: '14px',\n fontWeight: 'bold',\n zIndex: '3'\n });\n \n removeBtn.innerHTML = '×';\n \n removeBtn.onclick = (e) => {\n e.stopPropagation();\n onRemove(e);\n };\n \n return removeBtn;\n}\n\n/**\n * Creates a shot counter element with animation\n */\nexport function createShotCounter(): HTMLElement {\n const counter = document.createElement('div');\n counter.id = 'shot-counter';\n \n Object.assign(counter.style, {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '40px',\n height: '40px',\n borderRadius: '20px',\n backgroundColor: 'rgba(0, 0, 0, 0.6)',\n color: '#ffffff',\n fontSize: '16px',\n fontWeight: 'bold',\n margin: '0', // Remove default margin since it's now handled by container gap\n transition: 'all 0.3s ease',\n transform: 'scale(1)',\n opacity: '0',\n pointerEvents: 'none',\n zIndex: '3'\n });\n \n counter.textContent = '0';\n \n return counter;\n}\n\n/**\n * Updates the shot counter with animation\n */\nexport function updateShotCounter(counter: HTMLElement, count: number): void {\n const displayCount = count > 99 ? '99+' : count.toString();\n \n // Show counter if it's hidden and count > 0\n if (count > 0 && counter.style.opacity === '0') {\n counter.style.opacity = '1';\n }\n \n // Hide counter if count is 0\n if (count === 0) {\n counter.style.opacity = '0';\n counter.textContent = '0';\n return;\n }\n \n // Animation effect\n counter.style.transform = 'scale(1.2)';\n counter.style.backgroundColor = 'rgba(40, 167, 69, 0.8)'; // Green flash\n \n // Update the text\n counter.textContent = displayCount;\n \n // Reset animation after a short delay\n setTimeout(() => {\n counter.style.transform = 'scale(1)';\n counter.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';\n }, 200);\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare const PLUGIN_VERSION = "0.12.2";
@@ -0,0 +1,3 @@
1
+ // Auto-generated from package.json — do not edit. Run npm run build to update.
2
+ export const PLUGIN_VERSION = '0.12.2';
3
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC","sourcesContent":["// Auto-generated from package.json — do not edit. Run npm run build to update.\nexport const PLUGIN_VERSION = '0.12.2';\n"]}
@@ -0,0 +1,69 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { CameraImageData, CameraMultiCapturePlugin, CameraOverlayOptions, CameraOverlayResult, CameraPreviewRect, CameraVideoData, PermissionStatus } from './definitions';
3
+ export declare class CameraMultiCaptureWeb extends WebPlugin implements CameraMultiCapturePlugin {
4
+ capture(): Promise<{
5
+ value: CameraImageData;
6
+ }>;
7
+ stop(): Promise<void>;
8
+ start(_options?: CameraOverlayOptions): Promise<CameraOverlayResult>;
9
+ startVideoRecording(): Promise<void>;
10
+ stopVideoRecording(): Promise<{
11
+ value: CameraVideoData;
12
+ }>;
13
+ switchCamera(): Promise<void>;
14
+ setZoom(_options: {
15
+ zoom: number;
16
+ }): Promise<void>;
17
+ getFlash(): Promise<{
18
+ flashMode: 'on' | 'off' | 'auto';
19
+ }>;
20
+ setFlash(_options: {
21
+ flashMode: 'on' | 'off' | 'auto';
22
+ }): Promise<void>;
23
+ setTorch(_options: {
24
+ enabled: boolean;
25
+ }): Promise<void>;
26
+ getTorch(): Promise<{
27
+ enabled: boolean;
28
+ }>;
29
+ getAvailableZoomLevels(): Promise<{
30
+ minZoom: number;
31
+ maxZoom: number;
32
+ presetLevels: number[];
33
+ }>;
34
+ updatePreviewRect(_options: CameraPreviewRect): Promise<void>;
35
+ getAvailableCameras(): Promise<{
36
+ hasUltrawide: boolean;
37
+ hasWide: boolean;
38
+ hasTelephoto: boolean;
39
+ ultrawideZoomFactor?: number;
40
+ wideZoomFactor: number;
41
+ telephotoZoomFactor?: number;
42
+ }>;
43
+ switchToPhysicalCamera(_options: {
44
+ zoomFactor: number;
45
+ }): Promise<void>;
46
+ checkPermissions(): Promise<PermissionStatus>;
47
+ requestPermissions(): Promise<PermissionStatus>;
48
+ queueBackgroundUpload(_options: {
49
+ imageUri: string;
50
+ uploadEndpoint: string;
51
+ headers: Record<string, string>;
52
+ formData?: Record<string, string>;
53
+ method?: 'POST' | 'PUT';
54
+ deleteAfterUpload?: boolean;
55
+ }): Promise<{
56
+ jobId: string;
57
+ }>;
58
+ getUploadStatus(_options: {
59
+ jobId: string;
60
+ }): Promise<{
61
+ status: 'pending' | 'uploading' | 'completed' | 'failed';
62
+ error?: string;
63
+ }>;
64
+ getVersion(): Promise<{
65
+ version: string;
66
+ }>;
67
+ }
68
+ declare const CameraMultiCapture: CameraMultiCaptureWeb;
69
+ export { CameraMultiCapture };