react-native-photo-video-editor 0.1.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 (162) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/CONTRIBUTING.md +101 -0
  3. package/LICENSE +20 -0
  4. package/PhotoVideoEditor.podspec +23 -0
  5. package/README.md +53 -0
  6. package/android/build.gradle +61 -0
  7. package/android/src/main/AndroidManifest.xml +12 -0
  8. package/android/src/main/java/com/photovideoeditor/PhotoVideoEditorModule.kt +126 -0
  9. package/android/src/main/java/com/photovideoeditor/PhotoVideoEditorPackage.kt +31 -0
  10. package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +2521 -0
  11. package/android/src/main/java/com/photovideoeditor/editor/README.md +5 -0
  12. package/android/src/main/java/com/photovideoeditor/files/SourceResolver.kt +29 -0
  13. package/android/src/main/java/com/photovideoeditor/media/BitmapSampling.kt +11 -0
  14. package/android/src/main/java/com/photovideoeditor/model/EditorRequest.kt +3 -0
  15. package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +201 -0
  16. package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +22 -0
  17. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoAdjustmentRenderer.kt +160 -0
  18. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoAdjustments.kt +55 -0
  19. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +121 -0
  20. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +64 -0
  21. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayer.kt +54 -0
  22. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +108 -0
  23. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerStack.kt +60 -0
  24. package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +52 -0
  25. package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +168 -0
  26. package/android/src/main/java/com/photovideoeditor/photo/ui/FilterThumbnailLoader.kt +57 -0
  27. package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +202 -0
  28. package/android/src/main/java/com/photovideoeditor/photo/ui/PhotoPreviewRenderCoordinator.kt +57 -0
  29. package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +96 -0
  30. package/android/src/main/java/com/photovideoeditor/stickers/OnlineStickerSheet.kt +310 -0
  31. package/android/src/main/java/com/photovideoeditor/stickers/OpenMojiCatalog.kt +172 -0
  32. package/android/src/main/java/com/photovideoeditor/stickers/RemoteImageLoader.kt +110 -0
  33. package/android/src/main/java/com/photovideoeditor/ui/DesignTokens.kt +93 -0
  34. package/android/src/main/java/com/photovideoeditor/ui/components/EditorComponents.kt +119 -0
  35. package/android/src/main/java/com/photovideoeditor/validation/EditorRequestValidator.kt +11 -0
  36. package/android/src/main/java/com/photovideoeditor/video/cache/TimelineThumbnailRepository.kt +130 -0
  37. package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +319 -0
  38. package/android/src/main/java/com/photovideoeditor/video/render/VideoClip.kt +20 -0
  39. package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +136 -0
  40. package/android/src/main/java/com/photovideoeditor/video/render/VideoThumbnailGenerator.kt +31 -0
  41. package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +48 -0
  42. package/android/src/main/java/com/photovideoeditor/video/ui/TrimRangeView.kt +104 -0
  43. package/android/src/main/res/drawable/ic_add.xml +9 -0
  44. package/android/src/main/res/drawable/ic_arrow_back.xml +9 -0
  45. package/android/src/main/res/drawable/ic_aspect_ratio.xml +9 -0
  46. package/android/src/main/res/drawable/ic_auto_fix_high.xml +9 -0
  47. package/android/src/main/res/drawable/ic_check.xml +9 -0
  48. package/android/src/main/res/drawable/ic_chevron_left.xml +9 -0
  49. package/android/src/main/res/drawable/ic_chevron_right.xml +9 -0
  50. package/android/src/main/res/drawable/ic_close.xml +9 -0
  51. package/android/src/main/res/drawable/ic_compare.xml +9 -0
  52. package/android/src/main/res/drawable/ic_content_copy.xml +9 -0
  53. package/android/src/main/res/drawable/ic_content_cut.xml +9 -0
  54. package/android/src/main/res/drawable/ic_crop.xml +9 -0
  55. package/android/src/main/res/drawable/ic_crop_square.xml +9 -0
  56. package/android/src/main/res/drawable/ic_delete.xml +9 -0
  57. package/android/src/main/res/drawable/ic_drag_indicator.xml +9 -0
  58. package/android/src/main/res/drawable/ic_face.xml +9 -0
  59. package/android/src/main/res/drawable/ic_filter_center_focus.xml +9 -0
  60. package/android/src/main/res/drawable/ic_flare.xml +9 -0
  61. package/android/src/main/res/drawable/ic_format_size.xml +9 -0
  62. package/android/src/main/res/drawable/ic_fullscreen.xml +9 -0
  63. package/android/src/main/res/drawable/ic_layers.xml +9 -0
  64. package/android/src/main/res/drawable/ic_light_mode.xml +9 -0
  65. package/android/src/main/res/drawable/ic_lock.xml +9 -0
  66. package/android/src/main/res/drawable/ic_magic_button.xml +9 -0
  67. package/android/src/main/res/drawable/ic_movie.xml +9 -0
  68. package/android/src/main/res/drawable/ic_opacity.xml +9 -0
  69. package/android/src/main/res/drawable/ic_palette.xml +9 -0
  70. package/android/src/main/res/drawable/ic_photo_filter.xml +9 -0
  71. package/android/src/main/res/drawable/ic_redo.xml +9 -0
  72. package/android/src/main/res/drawable/ic_rotate_90_degrees_ccw.xml +9 -0
  73. package/android/src/main/res/drawable/ic_rotate_left.xml +9 -0
  74. package/android/src/main/res/drawable/ic_rotate_right.xml +9 -0
  75. package/android/src/main/res/drawable/ic_sentiment_satisfied.xml +9 -0
  76. package/android/src/main/res/drawable/ic_share.xml +9 -0
  77. package/android/src/main/res/drawable/ic_speed.xml +9 -0
  78. package/android/src/main/res/drawable/ic_straighten.xml +9 -0
  79. package/android/src/main/res/drawable/ic_title.xml +9 -0
  80. package/android/src/main/res/drawable/ic_tune.xml +9 -0
  81. package/android/src/main/res/drawable/ic_undo.xml +9 -0
  82. package/android/src/main/res/drawable/ic_visibility_off.xml +9 -0
  83. package/android/src/main/res/drawable/ic_wallpaper.xml +9 -0
  84. package/android/src/main/res/values/styles.xml +10 -0
  85. package/docs/api.md +9 -0
  86. package/docs/architecture.md +9 -0
  87. package/docs/customization.md +18 -0
  88. package/docs/development.md +21 -0
  89. package/docs/formats.md +27 -0
  90. package/docs/performance.md +3 -0
  91. package/docs/permissions.md +3 -0
  92. package/docs/photo-editor.md +41 -0
  93. package/docs/roadmap.md +12 -0
  94. package/docs/technology-decisions.md +17 -0
  95. package/docs/video-editor.md +76 -0
  96. package/ios/Editor/DesignTokens.swift +75 -0
  97. package/ios/Editor/PhotoVideoEditorOutcome.swift +9 -0
  98. package/ios/Editor/PhotoVideoEditorViewController.swift +2064 -0
  99. package/ios/Editor/README.md +5 -0
  100. package/ios/Editor/UI/EditorComponents.swift +138 -0
  101. package/ios/Files/SourceResolver.swift +13 -0
  102. package/ios/Models/EditorRequest.swift +3 -0
  103. package/ios/Photo/Export/PhotoExporter.swift +144 -0
  104. package/ios/Photo/Rendering/OverlayGeometry.swift +22 -0
  105. package/ios/Photo/Rendering/PhotoAdjustmentRenderer.swift +141 -0
  106. package/ios/Photo/Rendering/PhotoAdjustments.swift +57 -0
  107. package/ios/Photo/Rendering/PhotoEditSession.swift +121 -0
  108. package/ios/Photo/Rendering/PhotoFilterPresets.swift +45 -0
  109. package/ios/Photo/Rendering/PhotoLayer.swift +59 -0
  110. package/ios/Photo/Rendering/PhotoLayerRenderer.swift +94 -0
  111. package/ios/Photo/Rendering/PhotoLayerStack.swift +60 -0
  112. package/ios/Photo/Rendering/PhotoTransformState.swift +49 -0
  113. package/ios/Photo/Rendering/PixelBuffer.swift +68 -0
  114. package/ios/Photo/UI/CropOverlayView.swift +161 -0
  115. package/ios/Photo/UI/LayerOverlayView.swift +176 -0
  116. package/ios/Photo/UI/ZoomableImageView.swift +79 -0
  117. package/ios/PhotoVideoEditor.mm +20 -0
  118. package/ios/PhotoVideoEditor.swift +70 -0
  119. package/ios/Stickers/OnlineStickerSheetViewController.swift +418 -0
  120. package/ios/Stickers/OpenMojiCatalog.swift +188 -0
  121. package/ios/Stickers/RemoteImageLoader.swift +127 -0
  122. package/ios/Validation/EditorRequestValidator.swift +9 -0
  123. package/ios/Video/Cache/TimelineThumbnailRepository.swift +148 -0
  124. package/ios/Video/Export/VideoExporter.swift +275 -0
  125. package/ios/Video/Render/VideoClip.swift +21 -0
  126. package/ios/Video/Render/VideoEditSession.swift +99 -0
  127. package/ios/Video/Render/VideoThumbnailGenerator.swift +25 -0
  128. package/ios/Video/Render/VideoTransformState.swift +23 -0
  129. package/ios/Video/UI/TrimRangeView.swift +91 -0
  130. package/lib/module/NativePhotoVideoEditor.js +5 -0
  131. package/lib/module/NativePhotoVideoEditor.js.map +1 -0
  132. package/lib/module/errors.js +29 -0
  133. package/lib/module/errors.js.map +1 -0
  134. package/lib/module/events.js +27 -0
  135. package/lib/module/events.js.map +1 -0
  136. package/lib/module/index.js +91 -0
  137. package/lib/module/index.js.map +1 -0
  138. package/lib/module/package.json +1 -0
  139. package/lib/module/project.js +4 -0
  140. package/lib/module/project.js.map +1 -0
  141. package/lib/module/types.js +2 -0
  142. package/lib/module/types.js.map +1 -0
  143. package/lib/typescript/package.json +1 -0
  144. package/lib/typescript/src/NativePhotoVideoEditor.d.ts +11 -0
  145. package/lib/typescript/src/NativePhotoVideoEditor.d.ts.map +1 -0
  146. package/lib/typescript/src/errors.d.ts +11 -0
  147. package/lib/typescript/src/errors.d.ts.map +1 -0
  148. package/lib/typescript/src/events.d.ts +5 -0
  149. package/lib/typescript/src/events.d.ts.map +1 -0
  150. package/lib/typescript/src/index.d.ts +15 -0
  151. package/lib/typescript/src/index.d.ts.map +1 -0
  152. package/lib/typescript/src/project.d.ts +24 -0
  153. package/lib/typescript/src/project.d.ts.map +1 -0
  154. package/lib/typescript/src/types.d.ts +108 -0
  155. package/lib/typescript/src/types.d.ts.map +1 -0
  156. package/package.json +154 -0
  157. package/src/NativePhotoVideoEditor.ts +12 -0
  158. package/src/errors.ts +78 -0
  159. package/src/events.ts +43 -0
  160. package/src/index.ts +123 -0
  161. package/src/project.ts +17 -0
  162. package/src/types.ts +121 -0
package/package.json ADDED
@@ -0,0 +1,154 @@
1
+ {
2
+ "name": "react-native-photo-video-editor",
3
+ "version": "0.1.0",
4
+ "description": "Native photo and video editor for React Native, powered by Kotlin and Swift.",
5
+ "main": "./lib/module/index.js",
6
+ "module": "./lib/module/index.js",
7
+ "types": "./lib/typescript/src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "react-native-photo-video-editor-source": "./src/index.ts",
11
+ "types": "./lib/typescript/src/index.d.ts",
12
+ "default": "./lib/module/index.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "src",
18
+ "lib",
19
+ "docs",
20
+ "README.md",
21
+ "CONTRIBUTING.md",
22
+ "CHANGELOG.md",
23
+ "LICENSE",
24
+ "android",
25
+ "ios",
26
+ "cpp",
27
+ "*.podspec",
28
+ "react-native.config.js",
29
+ "!ios/build",
30
+ "!android/build",
31
+ "!android/gradle",
32
+ "!android/gradlew",
33
+ "!android/gradlew.bat",
34
+ "!android/local.properties",
35
+ "!android/src/test",
36
+ "!ios/Tests",
37
+ "!**/__tests__",
38
+ "!**/__fixtures__",
39
+ "!**/__mocks__",
40
+ "!**/.*"
41
+ ],
42
+ "scripts": {
43
+ "example": "yarn workspace react-native-photo-video-editor-example",
44
+ "example:start": "yarn example start",
45
+ "example:android": "yarn example android",
46
+ "example:ios": "yarn example ios",
47
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
48
+ "prepare": "bob build",
49
+ "typecheck": "tsc",
50
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
51
+ "format": "prettier --write \"**/*.{js,json,md,ts,tsx,yml}\"",
52
+ "test": "jest",
53
+ "codegen": "yarn example react-native codegen"
54
+ },
55
+ "keywords": [
56
+ "react-native",
57
+ "photo",
58
+ "video",
59
+ "editor",
60
+ "android",
61
+ "ios",
62
+ "kotlin",
63
+ "swift",
64
+ "turbo-module"
65
+ ],
66
+ "author": "Babur171",
67
+ "license": "MIT",
68
+ "homepage": "https://github.com/Babur171/react-native-photo-video-editor#readme",
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "git+https://github.com/Babur171/react-native-photo-video-editor.git"
72
+ },
73
+ "bugs": {
74
+ "url": "https://github.com/Babur171/react-native-photo-video-editor/issues"
75
+ },
76
+ "publishConfig": {
77
+ "registry": "https://registry.npmjs.org/"
78
+ },
79
+ "devDependencies": {
80
+ "@eslint/compat": "^2.1.0",
81
+ "@eslint/eslintrc": "^3.3.5",
82
+ "@eslint/js": "^10.0.1",
83
+ "@react-native/babel-preset": "0.85.0",
84
+ "@react-native/eslint-config": "0.85.0",
85
+ "@react-native/jest-preset": "0.85.0",
86
+ "@types/jest": "^29.5.14",
87
+ "@types/react": "^19.2.0",
88
+ "del-cli": "^7.0.0",
89
+ "eslint": "^9.39.4",
90
+ "eslint-config-prettier": "^10.1.8",
91
+ "eslint-plugin-ft-flow": "^3.0.11",
92
+ "eslint-plugin-prettier": "^5.5.6",
93
+ "jest": "^29.7.0",
94
+ "prettier": "^3.8.3",
95
+ "react": "19.2.3",
96
+ "react-native": "0.85.0",
97
+ "react-native-builder-bob": "^0.43.0",
98
+ "react-test-renderer": "19.2.3",
99
+ "turbo": "^2.9.16",
100
+ "typescript": "^6.0.3"
101
+ },
102
+ "peerDependencies": {
103
+ "react": "*",
104
+ "react-native": "*"
105
+ },
106
+ "workspaces": [
107
+ "example"
108
+ ],
109
+ "packageManager": "yarn@4.11.0",
110
+ "react-native-builder-bob": {
111
+ "source": "src",
112
+ "output": "lib",
113
+ "targets": [
114
+ [
115
+ "module",
116
+ {
117
+ "esm": true
118
+ }
119
+ ],
120
+ [
121
+ "typescript",
122
+ {
123
+ "project": "tsconfig.build.json"
124
+ }
125
+ ]
126
+ ]
127
+ },
128
+ "codegenConfig": {
129
+ "name": "PhotoVideoEditorSpec",
130
+ "type": "modules",
131
+ "jsSrcsDir": "src",
132
+ "android": {
133
+ "javaPackageName": "com.photovideoeditor"
134
+ }
135
+ },
136
+ "prettier": {
137
+ "quoteProps": "consistent",
138
+ "singleQuote": true,
139
+ "tabWidth": 2,
140
+ "trailingComma": "es5",
141
+ "useTabs": false
142
+ },
143
+ "create-react-native-library": {
144
+ "type": "turbo-module",
145
+ "languages": "kotlin-objc",
146
+ "tools": [
147
+ "eslint",
148
+ "jest",
149
+ "lefthook",
150
+ "release-it"
151
+ ],
152
+ "version": "0.63.0"
153
+ }
154
+ }
@@ -0,0 +1,12 @@
1
+ import type { CodegenTypes, TurboModule } from 'react-native';
2
+ import { TurboModuleRegistry } from 'react-native';
3
+
4
+ export interface Spec extends TurboModule {
5
+ openEditor(request: string): Promise<string>;
6
+ cancelExport(jobId: string | null): Promise<boolean>;
7
+ isAvailable(): boolean;
8
+ readonly onExportProgress: CodegenTypes.EventEmitter<string>;
9
+ readonly onEditorEvent: CodegenTypes.EventEmitter<string>;
10
+ }
11
+
12
+ export default TurboModuleRegistry.getEnforcing<Spec>('PhotoVideoEditor');
package/src/errors.ts ADDED
@@ -0,0 +1,78 @@
1
+ /** Stable error codes exposed across Android and iOS. */
2
+ export type PhotoVideoEditorErrorCode =
3
+ | 'E_INVALID_OPTIONS'
4
+ | 'E_INVALID_URI'
5
+ | 'E_UNSUPPORTED_MEDIA_TYPE'
6
+ | 'E_SOURCE_NOT_FOUND'
7
+ | 'E_SOURCE_UNREADABLE'
8
+ | 'E_UNSUPPORTED_FORMAT'
9
+ | 'E_EDITOR_ALREADY_OPEN'
10
+ | 'E_EDITOR_UNAVAILABLE'
11
+ | 'E_EDITOR_CANCELLED'
12
+ | 'E_EXPORT_FAILED'
13
+ | 'E_EXPORT_IN_PROGRESS'
14
+ | 'E_EXPORT_CANCELLED'
15
+ | 'E_INSUFFICIENT_STORAGE'
16
+ | 'E_OUT_OF_MEMORY'
17
+ | 'E_CODEC_UNAVAILABLE'
18
+ | 'E_PROJECT_INVALID'
19
+ | 'E_PROJECT_VERSION_UNSUPPORTED'
20
+ | 'E_PERMISSION_DENIED'
21
+ | 'E_INTERNAL';
22
+
23
+ const codes = new Set<PhotoVideoEditorErrorCode>([
24
+ 'E_INVALID_OPTIONS',
25
+ 'E_INVALID_URI',
26
+ 'E_UNSUPPORTED_MEDIA_TYPE',
27
+ 'E_SOURCE_NOT_FOUND',
28
+ 'E_SOURCE_UNREADABLE',
29
+ 'E_UNSUPPORTED_FORMAT',
30
+ 'E_EDITOR_ALREADY_OPEN',
31
+ 'E_EDITOR_UNAVAILABLE',
32
+ 'E_EDITOR_CANCELLED',
33
+ 'E_EXPORT_FAILED',
34
+ 'E_EXPORT_IN_PROGRESS',
35
+ 'E_EXPORT_CANCELLED',
36
+ 'E_INSUFFICIENT_STORAGE',
37
+ 'E_OUT_OF_MEMORY',
38
+ 'E_CODEC_UNAVAILABLE',
39
+ 'E_PROJECT_INVALID',
40
+ 'E_PROJECT_VERSION_UNSUPPORTED',
41
+ 'E_PERMISSION_DENIED',
42
+ 'E_INTERNAL',
43
+ ]);
44
+
45
+ /** Error thrown by every public asynchronous operation. */
46
+ export class PhotoVideoEditorError extends Error {
47
+ /** Stable machine-readable code. */ readonly code: PhotoVideoEditorErrorCode;
48
+ /** Sanitized original native metadata, when available. */ readonly native?: unknown;
49
+ constructor(
50
+ code: PhotoVideoEditorErrorCode,
51
+ message: string,
52
+ native?: unknown
53
+ ) {
54
+ super(message);
55
+ this.name = 'PhotoVideoEditorError';
56
+ this.code = code;
57
+ this.native = native;
58
+ }
59
+ }
60
+
61
+ /** Converts unknown native rejection values to the stable public error class. */
62
+ export function normalizeEditorError(error: unknown): PhotoVideoEditorError {
63
+ if (error instanceof PhotoVideoEditorError) return error;
64
+ const value =
65
+ typeof error === 'object' && error !== null
66
+ ? (error as { code?: unknown; message?: unknown })
67
+ : {};
68
+ const code =
69
+ typeof value.code === 'string' &&
70
+ codes.has(value.code as PhotoVideoEditorErrorCode)
71
+ ? (value.code as PhotoVideoEditorErrorCode)
72
+ : 'E_INTERNAL';
73
+ const message =
74
+ typeof value.message === 'string' && value.message.trim()
75
+ ? value.message
76
+ : 'The native editor failed unexpectedly.';
77
+ return new PhotoVideoEditorError(code, message, { code: value.code });
78
+ }
package/src/events.ts ADDED
@@ -0,0 +1,43 @@
1
+ import NativePhotoVideoEditor from './NativePhotoVideoEditor';
2
+ import { PhotoVideoEditorError } from './errors';
3
+ import type {
4
+ EditorEvent,
5
+ EditorEventListener,
6
+ EditorSubscription,
7
+ ExportProgressEvent,
8
+ } from './types';
9
+
10
+ export function addEditorEventListener(
11
+ listener: EditorEventListener
12
+ ): EditorSubscription {
13
+ if (typeof listener !== 'function')
14
+ throw new PhotoVideoEditorError(
15
+ 'E_INVALID_OPTIONS',
16
+ 'Editor listener must be a function.'
17
+ );
18
+ return NativePhotoVideoEditor.onEditorEvent((payload) => {
19
+ try {
20
+ listener(JSON.parse(payload) as EditorEvent);
21
+ } catch {
22
+ /* malformed native event */
23
+ }
24
+ });
25
+ }
26
+
27
+ /** Subscribes to real native export updates. Milestone one emits none. */
28
+ export function addExportProgressListener(
29
+ listener: (event: ExportProgressEvent) => void
30
+ ): EditorSubscription {
31
+ if (typeof listener !== 'function')
32
+ throw new PhotoVideoEditorError(
33
+ 'E_INVALID_OPTIONS',
34
+ 'Progress listener must be a function.'
35
+ );
36
+ return NativePhotoVideoEditor.onExportProgress((payload) => {
37
+ try {
38
+ listener(JSON.parse(payload) as ExportProgressEvent);
39
+ } catch {
40
+ /* Ignore malformed native events. */
41
+ }
42
+ });
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,123 @@
1
+ import NativePhotoVideoEditor from './NativePhotoVideoEditor';
2
+ import { PhotoVideoEditorError, normalizeEditorError } from './errors';
3
+ import type {
4
+ EditorOptions,
5
+ EditorResult,
6
+ ExportQuality,
7
+ ImageExportFormat,
8
+ MediaType,
9
+ PhotoEditorOptions,
10
+ VideoExportFormat,
11
+ VideoEditorOptions,
12
+ } from './types';
13
+
14
+ export { addEditorEventListener, addExportProgressListener } from './events';
15
+ export { PhotoVideoEditorError } from './errors';
16
+ export type * from './types';
17
+ export { EDITOR_PROJECT_SCHEMA_VERSION } from './project';
18
+ export type { EditorProject } from './project';
19
+
20
+ const mediaTypes = new Set<MediaType>(['photo', 'video']);
21
+ const qualities = new Set<ExportQuality>(['low', 'medium', 'high', 'original']);
22
+ const imageFormats = new Set<ImageExportFormat>(['jpeg', 'png', 'webp']);
23
+ const videoFormats = new Set<VideoExportFormat>(['mp4', 'mov']);
24
+ const defaultFeatures = {
25
+ crop: true,
26
+ rotate: true,
27
+ trim: true,
28
+ filters: true,
29
+ text: true,
30
+ stickers: true,
31
+ overlays: true,
32
+ onlineStickers: true,
33
+ };
34
+
35
+ function invalid(message: string): never {
36
+ throw new PhotoVideoEditorError('E_INVALID_OPTIONS', message);
37
+ }
38
+ function positive(value: number | undefined, name: string): void {
39
+ if (value !== undefined && (!Number.isFinite(value) || value <= 0))
40
+ invalid(`${name} must be a positive number.`);
41
+ }
42
+ function normalizeOptions(input: EditorOptions): EditorOptions {
43
+ if (
44
+ !input ||
45
+ typeof input !== 'object' ||
46
+ !input.source ||
47
+ typeof input.source !== 'object'
48
+ )
49
+ invalid('options.source is required.');
50
+ if (typeof input.source.uri !== 'string' || !input.source.uri.trim())
51
+ throw new PhotoVideoEditorError(
52
+ 'E_INVALID_URI',
53
+ 'source.uri must be a non-empty string.'
54
+ );
55
+ if (!mediaTypes.has(input.source.type))
56
+ throw new PhotoVideoEditorError(
57
+ 'E_UNSUPPORTED_MEDIA_TYPE',
58
+ 'source.type must be photo or video.'
59
+ );
60
+ const output = input.export ?? {};
61
+ if (output.quality !== undefined && !qualities.has(output.quality))
62
+ invalid('export.quality is unsupported.');
63
+ if (output.imageFormat !== undefined && !imageFormats.has(output.imageFormat))
64
+ invalid('export.imageFormat is unsupported.');
65
+ if (output.videoFormat !== undefined && !videoFormats.has(output.videoFormat))
66
+ invalid('export.videoFormat is unsupported.');
67
+ positive(output.maxWidth, 'export.maxWidth');
68
+ positive(output.maxHeight, 'export.maxHeight');
69
+ positive(output.videoBitrate, 'export.videoBitrate');
70
+ positive(output.frameRate, 'export.frameRate');
71
+ if (input.source.type === 'photo' && output.videoFormat !== undefined)
72
+ invalid('videoFormat is only valid for video sources.');
73
+ if (input.source.type === 'video' && output.imageFormat !== undefined)
74
+ invalid('imageFormat is only valid for photo sources.');
75
+ const features = { ...defaultFeatures, ...input.features };
76
+ if (input.source.type === 'photo') {
77
+ features.trim = false;
78
+ }
79
+ return {
80
+ ...input,
81
+ source: { ...input.source, uri: input.source.uri.trim() },
82
+ features,
83
+ export: { quality: 'high', preserveMetadata: true, ...output },
84
+ saveToGallery: input.saveToGallery ?? false,
85
+ };
86
+ }
87
+
88
+ /** Opens the full-screen native editor selected by source.type. */
89
+ export async function openEditor(
90
+ options: EditorOptions
91
+ ): Promise<EditorResult> {
92
+ try {
93
+ return JSON.parse(
94
+ await NativePhotoVideoEditor.openEditor(
95
+ JSON.stringify(normalizeOptions(options))
96
+ )
97
+ ) as EditorResult;
98
+ } catch (error) {
99
+ throw normalizeEditorError(error);
100
+ }
101
+ }
102
+ export function openPhotoEditor(
103
+ options: PhotoEditorOptions
104
+ ): Promise<EditorResult> {
105
+ return openEditor(options);
106
+ }
107
+ export function openVideoEditor(
108
+ options: VideoEditorOptions
109
+ ): Promise<EditorResult> {
110
+ return openEditor(options);
111
+ }
112
+ /** Requests cancellation. Returns false in milestone one because no export job exists. */
113
+ export async function cancelExport(jobId?: string): Promise<boolean> {
114
+ try {
115
+ return await NativePhotoVideoEditor.cancelExport(jobId ?? null);
116
+ } catch (error) {
117
+ throw normalizeEditorError(error);
118
+ }
119
+ }
120
+ /** Returns whether the linked native module is available on this device. */
121
+ export function isAvailable(): boolean {
122
+ return NativePhotoVideoEditor.isAvailable();
123
+ }
package/src/project.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { EditorSource, ExportOptions } from './types';
2
+ export const EDITOR_PROJECT_SCHEMA_VERSION = 1 as const;
3
+ export interface EditorProject {
4
+ schemaVersion: typeof EDITOR_PROJECT_SCHEMA_VERSION;
5
+ id: string;
6
+ source: EditorSource;
7
+ canvas: {
8
+ width: number;
9
+ height: number;
10
+ backgroundColor: string;
11
+ durationMs?: number;
12
+ };
13
+ tracks: Array<{ id: string; items: string[] }>;
14
+ adjustments: Record<string, number>;
15
+ exportDefaults: ExportOptions;
16
+ metadata: { createdAt: string; updatedAt: string };
17
+ }
package/src/types.ts ADDED
@@ -0,0 +1,121 @@
1
+ /** Kind of local media supplied to the editor. */
2
+ export type MediaType = 'photo' | 'video';
3
+ /** Requested export quality preset. */
4
+ export type ExportQuality = 'low' | 'medium' | 'high' | 'original';
5
+ /** Supported photo output container. */
6
+ export type ImageExportFormat = 'jpeg' | 'png' | 'webp';
7
+ /** Supported video output container. */
8
+ export type VideoExportFormat = 'mp4' | 'mov';
9
+ /** Lifecycle state reported by future export jobs. */
10
+ export type EditorStatus =
11
+ | 'idle'
12
+ | 'preparing'
13
+ | 'editing'
14
+ | 'exporting'
15
+ | 'completed'
16
+ | 'cancelled'
17
+ | 'failed';
18
+
19
+ /** A local file or platform content URI. Remote URLs are not supported. */
20
+ export interface EditorSource {
21
+ /** Local file/content URI. */ uri: string;
22
+ /** Media kind. */ type: MediaType;
23
+ /** Optional MIME type hint. */ mimeType?: string;
24
+ /** Optional display filename. */ fileName?: string;
25
+ }
26
+ /** Feature switches. Video-only switches are normalized off for photos. */
27
+ export interface EditorFeatures {
28
+ /** Enables cropping. */ crop?: boolean;
29
+ /** Enables rotation. */ rotate?: boolean;
30
+ /** Enables video trimming. */ trim?: boolean;
31
+ /** Enables filters. */ filters?: boolean;
32
+ /** Enables text overlays. */ text?: boolean;
33
+ /** Enables stickers (built-in, consumer-supplied, and user-uploaded). */ stickers?: boolean;
34
+ /** Enables the user-uploaded image overlay tool. */ overlays?: boolean;
35
+ /** Enables browsing/downloading free stickers from the internet (OpenMoji). Disable to prevent any network calls. */ onlineStickers?: boolean;
36
+ }
37
+ /** Future native editor appearance options. */
38
+ export interface EditorTheme {
39
+ /** CSS-style primary color. */ primaryColor?: string;
40
+ /** Editor background color. */ backgroundColor?: string;
41
+ /** Toolbar color. */ toolbarColor?: string;
42
+ /** Foreground text color. */ textColor?: string;
43
+ /** Status bar content style. */ statusBarStyle?: 'light' | 'dark';
44
+ }
45
+ /** Export preferences. The milestone-one placeholder does not export media. */
46
+ export interface ExportOptions {
47
+ /** Quality preset. */ quality?: ExportQuality;
48
+ /** Photo output format; photo sources only. */ imageFormat?: ImageExportFormat;
49
+ /** Video output format; video sources only. */ videoFormat?: VideoExportFormat;
50
+ /** Maximum output width in pixels; must be positive. */ maxWidth?: number;
51
+ /** Maximum output height in pixels; must be positive. */ maxHeight?: number;
52
+ /** Video bitrate in bits per second; must be positive. */ videoBitrate?: number;
53
+ /** Video frame rate in frames per second; must be positive. */ frameRate?: number;
54
+ /** Whether future exports should retain safe metadata. */ preserveMetadata?: boolean;
55
+ }
56
+ /** A consumer-provided font for text layers. Licensing is the consumer's responsibility. */
57
+ export interface FontAsset {
58
+ /** Font family name used to select this font from a text layer. */ family: string;
59
+ /** Local file URI of the font file (e.g. .ttf/.otf). */ uri: string;
60
+ }
61
+ /** A consumer-provided sticker image, selectable from the Stickers tool. */
62
+ export interface StickerAsset {
63
+ /** Stable identifier, echoed back on layers created from this asset. */ id: string;
64
+ /** Local file/content URI of the sticker image. */ uri: string;
65
+ }
66
+ /** Options accepted by {@link openEditor}. */
67
+ export interface EditorOptions {
68
+ /** Required local input. */ source: EditorSource;
69
+ /** Enabled tools. */ features?: EditorFeatures;
70
+ /** Native UI theme. */ theme?: EditorTheme;
71
+ /** Export preferences. */ export?: ExportOptions;
72
+ /** Requests saving to the gallery. Currently ignored by the placeholder. */ saveToGallery?: boolean;
73
+ /** Consumer-provided stickers offered by the Stickers tool, in addition to the small built-in set and end-user uploads. */ stickerAssets?: StickerAsset[];
74
+ /** Consumer-provided fonts selectable from the Text tool. */ fonts?: FontAsset[];
75
+ }
76
+ export interface PhotoEditorOptions extends Omit<EditorOptions, 'source'> {
77
+ source: EditorSource & { type: 'photo' };
78
+ }
79
+ export interface VideoEditorOptions extends Omit<EditorOptions, 'source'> {
80
+ source: EditorSource & { type: 'video' };
81
+ }
82
+ export type EditorEventType =
83
+ | 'editorOpened'
84
+ | 'editorReady'
85
+ | 'selectionChanged'
86
+ | 'projectChanged'
87
+ | 'exportStarted'
88
+ | 'exportProgress'
89
+ | 'exportCompleted'
90
+ | 'exportCancelled'
91
+ | 'editorClosed'
92
+ | 'error';
93
+ export interface EditorEvent {
94
+ type: EditorEventType;
95
+ projectId?: string;
96
+ jobId?: string;
97
+ progress?: number;
98
+ status?: EditorStatus;
99
+ }
100
+ export type EditorEventListener = (event: EditorEvent) => void;
101
+ /** Result from the native editor contract. */
102
+ export interface EditorResult {
103
+ /** Local output URI. */ uri: string;
104
+ /** Output media kind. */ type: MediaType;
105
+ /** MIME type when known. */ mimeType?: string;
106
+ /** Width in pixels when known. */ width?: number;
107
+ /** Height in pixels when known. */ height?: number;
108
+ /** Video duration in milliseconds when known. */ duration?: number;
109
+ /** File size in bytes when known. */ fileSize?: number;
110
+ /** True only when editing was cancelled. */ cancelled: boolean;
111
+ }
112
+ /** Export progress. Progress is between 0 and 1. */
113
+ export interface ExportProgressEvent {
114
+ /** Export job identifier. */ jobId: string;
115
+ /** Fraction complete, from 0 to 1. */ progress: number;
116
+ /** Current lifecycle state. */ status: EditorStatus;
117
+ }
118
+ /** Removable event subscription. */
119
+ export interface EditorSubscription {
120
+ /** Stops future callbacks; safe to call repeatedly. */ remove(): void;
121
+ }