jazz-tools 0.16.6 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.svelte-kit/__package__/index.d.ts +1 -0
- package/.svelte-kit/__package__/index.d.ts.map +1 -1
- package/.svelte-kit/__package__/index.js +1 -0
- package/.svelte-kit/__package__/media/image.svelte +131 -0
- package/.svelte-kit/__package__/media/image.svelte.d.ts +10 -0
- package/.svelte-kit/__package__/media/image.svelte.d.ts.map +1 -0
- package/.svelte-kit/__package__/media/index.d.ts +2 -0
- package/.svelte-kit/__package__/media/index.d.ts.map +1 -0
- package/.svelte-kit/__package__/media/index.js +1 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts +2 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/media/image.svelte.test.js +430 -0
- package/.svelte-kit/__package__/tests/testUtils.d.ts +11 -0
- package/.svelte-kit/__package__/tests/testUtils.d.ts.map +1 -0
- package/.svelte-kit/__package__/tests/testUtils.js +17 -0
- package/.svelte-kit/__package__/tests/types.d.ts +3 -0
- package/.turbo/turbo-build.log +47 -51
- package/CHANGELOG.md +24 -0
- package/dist/{chunk-R2VNCMG6.js → chunk-2SH44VLX.js} +33 -38
- package/dist/chunk-2SH44VLX.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/media/chunk-JBLT443O.js +211 -0
- package/dist/media/chunk-JBLT443O.js.map +1 -0
- package/dist/media/create-image.d.ts +48 -0
- package/dist/media/create-image.d.ts.map +1 -0
- package/dist/media/create-image.test.d.ts +2 -0
- package/dist/media/create-image.test.d.ts.map +1 -0
- package/dist/media/index.browser.d.ts +15 -0
- package/dist/media/index.browser.d.ts.map +1 -0
- package/dist/media/index.browser.js +113 -0
- package/dist/media/index.browser.js.map +1 -0
- package/dist/media/index.d.ts +53 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +13 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/index.native.d.ts +17 -0
- package/dist/media/index.native.d.ts.map +1 -0
- package/dist/media/index.native.js +126 -0
- package/dist/media/index.native.js.map +1 -0
- package/dist/media/utils.d.ts +17 -0
- package/dist/media/utils.d.ts.map +1 -0
- package/dist/media/utils.test.d.ts +2 -0
- package/dist/media/utils.test.d.ts.map +1 -0
- package/dist/react/index.d.ts +1 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +176 -59
- package/dist/react/index.js.map +1 -1
- package/dist/react/media/image.d.ts +62 -0
- package/dist/react/media/image.d.ts.map +1 -0
- package/dist/react/tests/media/image.test.d.ts +2 -0
- package/dist/react/tests/media/image.test.d.ts.map +1 -0
- package/dist/react-core/tests/useDemoAuth.test.d.ts +2 -0
- package/dist/react-core/tests/useDemoAuth.test.d.ts.map +1 -0
- package/dist/react-native-core/index.d.ts +1 -1
- package/dist/react-native-core/index.d.ts.map +1 -1
- package/dist/react-native-core/index.js +84 -66
- package/dist/react-native-core/index.js.map +1 -1
- package/dist/react-native-core/media/image.d.ts +93 -0
- package/dist/react-native-core/media/image.d.ts.map +1 -0
- package/dist/react-native-core/testing.d.ts +2 -0
- package/dist/react-native-core/testing.d.ts.map +1 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/media/image.svelte +131 -0
- package/dist/svelte/media/image.svelte.d.ts +10 -0
- package/dist/svelte/media/image.svelte.d.ts.map +1 -0
- package/dist/svelte/media/index.d.ts +2 -0
- package/dist/svelte/media/index.d.ts.map +1 -0
- package/dist/svelte/media/index.js +1 -0
- package/dist/svelte/tests/media/image.svelte.test.d.ts +2 -0
- package/dist/svelte/tests/media/image.svelte.test.d.ts.map +1 -0
- package/dist/svelte/tests/media/image.svelte.test.js +430 -0
- package/dist/svelte/tests/testUtils.d.ts +11 -0
- package/dist/svelte/tests/testUtils.d.ts.map +1 -0
- package/dist/svelte/tests/testUtils.js +17 -0
- package/dist/svelte/tests/types.d.ts +3 -0
- package/dist/testing.js +1 -1
- package/dist/tools/coValues/coFeed.d.ts +15 -0
- package/dist/tools/coValues/coFeed.d.ts.map +1 -1
- package/dist/tools/coValues/extensions/imageDef.d.ts +3 -9
- package/dist/tools/coValues/extensions/imageDef.d.ts.map +1 -1
- package/dist/tools/coValues/request.d.ts +1 -1
- package/dist/tools/coValues/request.d.ts.map +1 -1
- package/dist/tools/exports.d.ts +1 -1
- package/dist/tools/exports.d.ts.map +1 -1
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +1 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/media/create-image.test.ts +195 -0
- package/src/media/create-image.ts +180 -0
- package/src/media/index.browser.ts +150 -0
- package/src/media/index.native.ts +153 -0
- package/src/media/index.ts +61 -0
- package/src/media/utils.test.ts +373 -0
- package/src/media/utils.ts +205 -0
- package/src/react/index.ts +1 -2
- package/src/react/media/image.tsx +210 -0
- package/src/react/tests/media/image.test.tsx +588 -0
- package/src/react-native-core/index.ts +1 -1
- package/src/react-native-core/media/image.tsx +159 -0
- package/src/svelte/index.ts +1 -0
- package/src/svelte/media/image.svelte +131 -0
- package/src/svelte/media/index.ts +1 -0
- package/src/svelte/tests/media/image.svelte.test.ts +583 -0
- package/src/svelte/tests/testUtils.ts +33 -0
- package/src/svelte/tests/types.d.ts +3 -0
- package/src/tools/coValues/coFeed.ts +37 -5
- package/src/tools/coValues/extensions/imageDef.ts +3 -49
- package/src/tools/coValues/request.ts +1 -1
- package/src/tools/exports.ts +1 -0
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +6 -0
- package/src/tools/tests/coMap.record.test.ts +3 -2
- package/src/tools/tests/coOptional.test.ts +3 -1
- package/tsconfig.json +1 -0
- package/tsup.config.ts +4 -9
- package/vitest.config.ts +14 -1
- package/dist/browser-media-images/index.d.ts +0 -9
- package/dist/browser-media-images/index.d.ts.map +0 -1
- package/dist/browser-media-images/index.js +0 -72
- package/dist/browser-media-images/index.js.map +0 -1
- package/dist/chunk-R2VNCMG6.js.map +0 -1
- package/dist/react/media.d.ts +0 -24
- package/dist/react/media.d.ts.map +0 -1
- package/dist/react-native-core/media.d.ts +0 -24
- package/dist/react-native-core/media.d.ts.map +0 -1
- package/dist/react-native-media-images/index.d.ts +0 -7
- package/dist/react-native-media-images/index.d.ts.map +0 -1
- package/dist/react-native-media-images/index.js +0 -177
- package/dist/react-native-media-images/index.js.map +0 -1
- package/dist/tools/tests/imageDef.test.d.ts +0 -2
- package/dist/tools/tests/imageDef.test.d.ts.map +0 -1
- package/src/browser-media-images/index.ts +0 -131
- package/src/react/media.tsx +0 -74
- package/src/react/scratch.tsx +0 -50
- package/src/react-native-core/media.tsx +0 -79
- package/src/react-native-media-images/index.ts +0 -238
- package/src/tools/tests/imageDef.test.ts +0 -278
package/dist/react/media.d.ts
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
import { ImageDefinition, Loaded } from "jazz-tools";
|
2
|
-
import React from "react";
|
3
|
-
/** @category Media */
|
4
|
-
export declare function useProgressiveImg({ image, maxWidth, targetWidth, }: {
|
5
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
6
|
-
maxWidth?: number;
|
7
|
-
targetWidth?: number;
|
8
|
-
}): {
|
9
|
-
src: string | undefined;
|
10
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
11
|
-
originalSize: [number, number] | undefined;
|
12
|
-
};
|
13
|
-
/** @category Media */
|
14
|
-
export declare function ProgressiveImg({ children, image, maxWidth, targetWidth, }: {
|
15
|
-
children: (result: {
|
16
|
-
src: string | undefined;
|
17
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
18
|
-
originalSize: readonly [number, number] | undefined;
|
19
|
-
}) => React.ReactNode;
|
20
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
21
|
-
maxWidth?: number;
|
22
|
-
targetWidth?: number;
|
23
|
-
}): React.ReactNode;
|
24
|
-
//# sourceMappingURL=media.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/react/media.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,sBAAsB;AACtB,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC,OAAO,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;;;;EAyCA;AAED,sBAAsB;AACtB,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,CAAC,MAAM,EAAE;QACjB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,GAAG,aAAa,GAAG,SAAS,CAAC;QACvD,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;KACrD,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,OAAO,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,KAAK,CAAC,SAAS,CAGlB"}
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import { ImageDefinition, Loaded } from "jazz-tools";
|
2
|
-
import React from "react";
|
3
|
-
/** @category Media */
|
4
|
-
export declare function useProgressiveImgNative({ image, maxWidth, targetWidth, }: {
|
5
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
6
|
-
maxWidth?: number;
|
7
|
-
targetWidth?: number;
|
8
|
-
}): {
|
9
|
-
src: string | undefined;
|
10
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
11
|
-
originalSize: [number, number] | undefined;
|
12
|
-
};
|
13
|
-
/** @category Media */
|
14
|
-
export declare function ProgressiveImgNative({ children, image, maxWidth, targetWidth, }: {
|
15
|
-
children: (result: {
|
16
|
-
src: string | undefined;
|
17
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
18
|
-
originalSize: readonly [number, number] | undefined;
|
19
|
-
}) => React.ReactNode;
|
20
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
21
|
-
maxWidth?: number;
|
22
|
-
targetWidth?: number;
|
23
|
-
}): React.ReactNode;
|
24
|
-
//# sourceMappingURL=media.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../../src/react-native-core/media.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,sBAAsB;AACtB,wBAAgB,uBAAuB,CAAC,EACtC,KAAK,EACL,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,KAAK,EAAE,MAAM,CAAC,OAAO,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;;;;EA8CA;AAED,sBAAsB;AACtB,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,WAAW,GACZ,EAAE;IACD,QAAQ,EAAE,CAAC,MAAM,EAAE;QACjB,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,GAAG,EAAE,GAAG,MAAM,IAAI,MAAM,EAAE,GAAG,aAAa,GAAG,SAAS,CAAC;QACvD,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;KACrD,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,OAAO,eAAe,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,mBAGA"}
|
@@ -1,7 +0,0 @@
|
|
1
|
-
import { Account, Group, ImageDefinition, Loaded } from "jazz-tools";
|
2
|
-
/** @category Image creation */
|
3
|
-
export declare function createImageNative(base64ImageDataURI: string, options?: {
|
4
|
-
owner?: Group | Account;
|
5
|
-
maxSize?: 256 | 1024 | 2048;
|
6
|
-
}): Promise<Loaded<typeof ImageDefinition>>;
|
7
|
-
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react-native-media-images/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,OAAO,EAEP,KAAK,EACL,eAAe,EACf,MAAM,EACP,MAAM,YAAY,CAAC;AAwFpB,+BAA+B;AAC/B,wBAAsB,iBAAiB,CACrC,kBAAkB,EAAE,MAAM,EAC1B,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;CACxB,GACL,OAAO,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC,CAsIzC"}
|
@@ -1,177 +0,0 @@
|
|
1
|
-
// src/react-native-media-images/index.ts
|
2
|
-
import ImageResizer from "@bam.tech/react-native-image-resizer";
|
3
|
-
import * as FileSystem from "expo-file-system";
|
4
|
-
import {
|
5
|
-
FileStream,
|
6
|
-
ImageDefinition
|
7
|
-
} from "jazz-tools";
|
8
|
-
import { Image } from "react-native";
|
9
|
-
function arrayBuffer(blob) {
|
10
|
-
return new Promise((resolve, reject) => {
|
11
|
-
const reader = new FileReader();
|
12
|
-
reader.onloadend = () => {
|
13
|
-
resolve(reader.result);
|
14
|
-
};
|
15
|
-
reader.onerror = (error) => {
|
16
|
-
reject(error);
|
17
|
-
};
|
18
|
-
reader.readAsArrayBuffer(blob);
|
19
|
-
});
|
20
|
-
}
|
21
|
-
async function fileUriToBlob(uri) {
|
22
|
-
try {
|
23
|
-
const response = await fetch(uri);
|
24
|
-
const blob = await response.blob();
|
25
|
-
blob.arrayBuffer = () => arrayBuffer(blob);
|
26
|
-
return blob;
|
27
|
-
} catch (error) {
|
28
|
-
console.error("Failed to convert file URI to Blob:", error);
|
29
|
-
throw new Error("Failed to convert file URI to Blob");
|
30
|
-
}
|
31
|
-
}
|
32
|
-
async function convertFileContentsToBase64DataURI(fileUri, contentType) {
|
33
|
-
try {
|
34
|
-
const base64 = await FileSystem.readAsStringAsync(fileUri, {
|
35
|
-
encoding: FileSystem.EncodingType.Base64
|
36
|
-
});
|
37
|
-
return `data:${contentType};base64,${base64}`;
|
38
|
-
} catch (error) {
|
39
|
-
console.error("Failed to convert file to base64:", error);
|
40
|
-
return null;
|
41
|
-
}
|
42
|
-
}
|
43
|
-
function base64DataURIToParts(base64Data) {
|
44
|
-
const parts = base64Data.split(",");
|
45
|
-
const contentType = parts[0]?.split(":")?.[1]?.split(";")?.[0] || "";
|
46
|
-
const data = parts[1] || "";
|
47
|
-
return { contentType, data };
|
48
|
-
}
|
49
|
-
function contentTypeToFormat(contentType) {
|
50
|
-
if (contentType.includes("image/png")) return "PNG";
|
51
|
-
if (contentType.includes("image/jpeg")) return "JPEG";
|
52
|
-
if (contentType.includes("image/webp")) return "WEBP";
|
53
|
-
return "PNG";
|
54
|
-
}
|
55
|
-
async function base64DataURIToBlob(base64Data) {
|
56
|
-
const { contentType, data } = base64DataURIToParts(base64Data);
|
57
|
-
const byteCharacters = atob(data);
|
58
|
-
const byteNumbers = new Array(byteCharacters.length);
|
59
|
-
for (let i = 0; i < byteCharacters.length; i++) {
|
60
|
-
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
61
|
-
}
|
62
|
-
const byteArray = new Uint8Array(byteNumbers);
|
63
|
-
const blob = new Blob(byteArray, { type: contentType });
|
64
|
-
blob.arrayBuffer = () => arrayBuffer(blob);
|
65
|
-
return blob;
|
66
|
-
}
|
67
|
-
async function getImageDimensions(uri) {
|
68
|
-
return new Promise((resolve, reject) => {
|
69
|
-
Image.getSize(
|
70
|
-
uri,
|
71
|
-
(width, height) => resolve({ width, height }),
|
72
|
-
(error) => {
|
73
|
-
console.error("Failed to get image dimensions:", error);
|
74
|
-
reject(new Error("Failed to get image dimensions"));
|
75
|
-
}
|
76
|
-
);
|
77
|
-
});
|
78
|
-
}
|
79
|
-
async function createImageNative(base64ImageDataURI, options = {}) {
|
80
|
-
try {
|
81
|
-
const { contentType } = base64DataURIToParts(base64ImageDataURI);
|
82
|
-
const format = contentTypeToFormat(contentType);
|
83
|
-
let originalWidth, originalHeight;
|
84
|
-
try {
|
85
|
-
({ width: originalWidth, height: originalHeight } = await getImageDimensions(base64ImageDataURI));
|
86
|
-
} catch (error) {
|
87
|
-
console.error("Error getting image dimensions:", error);
|
88
|
-
throw new Error("Failed to get image dimensions");
|
89
|
-
}
|
90
|
-
let placeholderImage;
|
91
|
-
try {
|
92
|
-
placeholderImage = await ImageResizer.createResizedImage(
|
93
|
-
base64ImageDataURI,
|
94
|
-
8,
|
95
|
-
8,
|
96
|
-
format,
|
97
|
-
100,
|
98
|
-
0
|
99
|
-
);
|
100
|
-
} catch (error) {
|
101
|
-
console.error("Error creating placeholder image:", error);
|
102
|
-
throw new Error("Failed to create placeholder image");
|
103
|
-
}
|
104
|
-
const placeholderDataURL = await convertFileContentsToBase64DataURI(
|
105
|
-
placeholderImage.uri,
|
106
|
-
contentType
|
107
|
-
);
|
108
|
-
if (!placeholderDataURL) {
|
109
|
-
throw new Error("Failed to create placeholder data URL");
|
110
|
-
}
|
111
|
-
const imageDefinition = ImageDefinition.create(
|
112
|
-
{
|
113
|
-
originalSize: [originalWidth, originalHeight],
|
114
|
-
placeholderDataURL
|
115
|
-
},
|
116
|
-
options.owner
|
117
|
-
);
|
118
|
-
const addImageStream = async (width, height, label) => {
|
119
|
-
try {
|
120
|
-
const resizedImage = await ImageResizer.createResizedImage(
|
121
|
-
base64ImageDataURI,
|
122
|
-
width,
|
123
|
-
height,
|
124
|
-
format,
|
125
|
-
80,
|
126
|
-
0
|
127
|
-
);
|
128
|
-
const binaryStream = await FileStream.createFromBlob(
|
129
|
-
await fileUriToBlob(resizedImage.uri),
|
130
|
-
imageDefinition._owner
|
131
|
-
);
|
132
|
-
imageDefinition[label] = binaryStream;
|
133
|
-
} catch (error) {
|
134
|
-
console.error(`Error adding image stream for ${label}:`, error);
|
135
|
-
throw new Error(`Failed to add image stream for ${label}`);
|
136
|
-
}
|
137
|
-
};
|
138
|
-
if (originalWidth > 256 || originalHeight > 256) {
|
139
|
-
const width = originalWidth > originalHeight ? 256 : Math.round(256 * (originalWidth / originalHeight));
|
140
|
-
const height = originalHeight > originalWidth ? 256 : Math.round(256 * (originalHeight / originalWidth));
|
141
|
-
await addImageStream(width, height, `${width}x${height}`);
|
142
|
-
}
|
143
|
-
if (options.maxSize === 256) return imageDefinition;
|
144
|
-
if (originalWidth > 1024 || originalHeight > 1024) {
|
145
|
-
const width = originalWidth > originalHeight ? 1024 : Math.round(1024 * (originalWidth / originalHeight));
|
146
|
-
const height = originalHeight > originalWidth ? 1024 : Math.round(1024 * (originalHeight / originalWidth));
|
147
|
-
await addImageStream(width, height, `${width}x${height}`);
|
148
|
-
}
|
149
|
-
if (options.maxSize === 1024) return imageDefinition;
|
150
|
-
if (originalWidth > 2048 || originalHeight > 2048) {
|
151
|
-
const width = originalWidth > originalHeight ? 2048 : Math.round(2048 * (originalWidth / originalHeight));
|
152
|
-
const height = originalHeight > originalWidth ? 2048 : Math.round(2048 * (originalHeight / originalWidth));
|
153
|
-
await addImageStream(width, height, `${width}x${height}`);
|
154
|
-
}
|
155
|
-
if (options.maxSize === 2048) return imageDefinition;
|
156
|
-
if (options.maxSize === void 0 || options.maxSize > 2048) {
|
157
|
-
try {
|
158
|
-
const originalBinaryStream = await FileStream.createFromBlob(
|
159
|
-
await base64DataURIToBlob(base64ImageDataURI),
|
160
|
-
imageDefinition._owner
|
161
|
-
);
|
162
|
-
imageDefinition[`${originalWidth}x${originalHeight}`] = originalBinaryStream;
|
163
|
-
} catch (error) {
|
164
|
-
console.error("Error adding original image stream:", error);
|
165
|
-
throw new Error("Failed to add original image stream");
|
166
|
-
}
|
167
|
-
}
|
168
|
-
return imageDefinition;
|
169
|
-
} catch (error) {
|
170
|
-
console.error("Error in createImage:", error);
|
171
|
-
throw error;
|
172
|
-
}
|
173
|
-
}
|
174
|
-
export {
|
175
|
-
createImageNative
|
176
|
-
};
|
177
|
-
//# sourceMappingURL=index.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"sources":["../../src/react-native-media-images/index.ts"],"sourcesContent":["import ImageResizer from \"@bam.tech/react-native-image-resizer\";\nimport * as FileSystem from \"expo-file-system\";\nimport {\n Account,\n FileStream,\n Group,\n ImageDefinition,\n Loaded,\n} from \"jazz-tools\";\nimport { Image } from \"react-native\";\n\nfunction arrayBuffer(blob: Blob): Promise<ArrayBuffer> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => {\n resolve(reader.result as ArrayBuffer);\n };\n reader.onerror = (error) => {\n reject(error);\n };\n reader.readAsArrayBuffer(blob);\n });\n}\n\nasync function fileUriToBlob(uri: string): Promise<Blob> {\n try {\n const response = await fetch(uri);\n const blob = await response.blob();\n blob.arrayBuffer = () => arrayBuffer(blob);\n return blob;\n } catch (error) {\n console.error(\"Failed to convert file URI to Blob:\", error);\n throw new Error(\"Failed to convert file URI to Blob\");\n }\n}\n\nasync function convertFileContentsToBase64DataURI(\n fileUri: string,\n contentType: string,\n) {\n try {\n const base64 = await FileSystem.readAsStringAsync(fileUri, {\n encoding: FileSystem.EncodingType.Base64,\n });\n return `data:${contentType};base64,${base64}`;\n } catch (error) {\n console.error(\"Failed to convert file to base64:\", error);\n return null;\n }\n}\n\nfunction base64DataURIToParts(base64Data: string) {\n const parts = base64Data.split(\",\");\n const contentType = parts[0]?.split(\":\")?.[1]?.split(\";\")?.[0] || \"\";\n const data = parts[1] || \"\";\n return { contentType, data };\n}\n\nfunction contentTypeToFormat(contentType: string) {\n if (contentType.includes(\"image/png\")) return \"PNG\";\n if (contentType.includes(\"image/jpeg\")) return \"JPEG\";\n if (contentType.includes(\"image/webp\")) return \"WEBP\";\n return \"PNG\";\n}\n\nasync function base64DataURIToBlob(base64Data: string) {\n const { contentType, data } = base64DataURIToParts(base64Data);\n const byteCharacters = atob(data);\n\n const byteNumbers = new Array(byteCharacters.length);\n for (let i = 0; i < byteCharacters.length; i++) {\n byteNumbers[i] = byteCharacters.charCodeAt(i);\n }\n const byteArray = new Uint8Array(byteNumbers);\n\n // @ts-expect-error byteArray has data\n const blob = new Blob(byteArray, { type: contentType });\n blob.arrayBuffer = () => arrayBuffer(blob);\n return blob;\n}\n\nasync function getImageDimensions(\n uri: string,\n): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n Image.getSize(\n uri,\n (width, height) => resolve({ width, height }),\n (error) => {\n console.error(\"Failed to get image dimensions:\", error);\n reject(new Error(\"Failed to get image dimensions\"));\n },\n );\n });\n}\n\n/** @category Image creation */\nexport async function createImageNative(\n base64ImageDataURI: string,\n options: {\n owner?: Group | Account;\n maxSize?: 256 | 1024 | 2048;\n } = {},\n): Promise<Loaded<typeof ImageDefinition>> {\n try {\n const { contentType } = base64DataURIToParts(base64ImageDataURI);\n const format = contentTypeToFormat(contentType);\n\n let originalWidth, originalHeight;\n try {\n ({ width: originalWidth, height: originalHeight } =\n await getImageDimensions(base64ImageDataURI));\n } catch (error) {\n console.error(\"Error getting image dimensions:\", error);\n throw new Error(\"Failed to get image dimensions\");\n }\n\n let placeholderImage;\n try {\n placeholderImage = await ImageResizer.createResizedImage(\n base64ImageDataURI,\n 8,\n 8,\n format,\n 100,\n 0,\n );\n } catch (error) {\n console.error(\"Error creating placeholder image:\", error);\n throw new Error(\"Failed to create placeholder image\");\n }\n\n const placeholderDataURL = await convertFileContentsToBase64DataURI(\n placeholderImage.uri,\n contentType,\n );\n\n if (!placeholderDataURL) {\n throw new Error(\"Failed to create placeholder data URL\");\n }\n\n const imageDefinition = ImageDefinition.create(\n {\n originalSize: [originalWidth, originalHeight],\n placeholderDataURL,\n },\n options.owner,\n );\n\n const addImageStream = async (\n width: number,\n height: number,\n label: string,\n ) => {\n try {\n const resizedImage = await ImageResizer.createResizedImage(\n base64ImageDataURI,\n width,\n height,\n format,\n 80,\n 0,\n );\n\n const binaryStream = await FileStream.createFromBlob(\n await fileUriToBlob(resizedImage.uri),\n imageDefinition._owner,\n );\n\n imageDefinition[label] = binaryStream;\n } catch (error) {\n console.error(`Error adding image stream for ${label}:`, error);\n throw new Error(`Failed to add image stream for ${label}`);\n }\n };\n\n if (originalWidth > 256 || originalHeight > 256) {\n const width =\n originalWidth > originalHeight\n ? 256\n : Math.round(256 * (originalWidth / originalHeight));\n const height =\n originalHeight > originalWidth\n ? 256\n : Math.round(256 * (originalHeight / originalWidth));\n await addImageStream(width, height, `${width}x${height}`);\n }\n\n if (options.maxSize === 256) return imageDefinition;\n\n if (originalWidth > 1024 || originalHeight > 1024) {\n const width =\n originalWidth > originalHeight\n ? 1024\n : Math.round(1024 * (originalWidth / originalHeight));\n const height =\n originalHeight > originalWidth\n ? 1024\n : Math.round(1024 * (originalHeight / originalWidth));\n await addImageStream(width, height, `${width}x${height}`);\n }\n\n if (options.maxSize === 1024) return imageDefinition;\n\n if (originalWidth > 2048 || originalHeight > 2048) {\n const width =\n originalWidth > originalHeight\n ? 2048\n : Math.round(2048 * (originalWidth / originalHeight));\n const height =\n originalHeight > originalWidth\n ? 2048\n : Math.round(2048 * (originalHeight / originalWidth));\n await addImageStream(width, height, `${width}x${height}`);\n }\n\n if (options.maxSize === 2048) return imageDefinition;\n\n if (options.maxSize === undefined || options.maxSize > 2048) {\n try {\n const originalBinaryStream = await FileStream.createFromBlob(\n await base64DataURIToBlob(base64ImageDataURI),\n imageDefinition._owner,\n );\n imageDefinition[`${originalWidth}x${originalHeight}`] =\n originalBinaryStream;\n } catch (error) {\n console.error(\"Error adding original image stream:\", error);\n throw new Error(\"Failed to add original image stream\");\n }\n }\n\n return imageDefinition;\n } catch (error) {\n console.error(\"Error in createImage:\", error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"imageDef.test.d.ts","sourceRoot":"","sources":["../../../src/tools/tests/imageDef.test.ts"],"names":[],"mappings":""}
|
@@ -1,131 +0,0 @@
|
|
1
|
-
import ImageBlobReduce from "image-blob-reduce";
|
2
|
-
import {
|
3
|
-
Account,
|
4
|
-
FileStream,
|
5
|
-
Group,
|
6
|
-
ImageDefinition,
|
7
|
-
Loaded,
|
8
|
-
} from "jazz-tools";
|
9
|
-
import Pica from "pica";
|
10
|
-
|
11
|
-
let reducer: ImageBlobReduce.ImageBlobReduce | undefined;
|
12
|
-
|
13
|
-
/** @category Image creation */
|
14
|
-
export async function createImage(
|
15
|
-
imageBlobOrFile: Blob | File,
|
16
|
-
options?: {
|
17
|
-
owner?: Group | Account;
|
18
|
-
maxSize?: 256 | 1024 | 2048;
|
19
|
-
},
|
20
|
-
): Promise<Loaded<typeof ImageDefinition, { $each: true }>> {
|
21
|
-
// Get the original size of the image
|
22
|
-
const { width: originalWidth, height: originalHeight } =
|
23
|
-
await getImageSize(imageBlobOrFile);
|
24
|
-
|
25
|
-
const highestDimension = Math.max(originalWidth, originalHeight);
|
26
|
-
|
27
|
-
// Calculate the sizes to resize the image to
|
28
|
-
const resizes = [256, 1024, 2048, highestDimension]
|
29
|
-
.filter((s) => s <= (options?.maxSize ?? highestDimension))
|
30
|
-
.toSorted((a, b) => a - b);
|
31
|
-
|
32
|
-
// Get the highest resolution to use as final original size
|
33
|
-
// In case of options.maxSize, it's not the originalWidth/Height
|
34
|
-
const { width: finalWidth, height: finalHeight } = getNewDimensions(
|
35
|
-
originalWidth,
|
36
|
-
originalHeight,
|
37
|
-
resizes.at(-1)!,
|
38
|
-
);
|
39
|
-
|
40
|
-
const imageDefinition = ImageDefinition.create(
|
41
|
-
{ originalSize: [finalWidth, finalHeight] },
|
42
|
-
options?.owner,
|
43
|
-
);
|
44
|
-
const owner = imageDefinition._owner;
|
45
|
-
|
46
|
-
// Placeholder 8x8
|
47
|
-
imageDefinition.placeholderDataURL =
|
48
|
-
await getPlaceholderBase64(imageBlobOrFile);
|
49
|
-
|
50
|
-
// Resizes for progressive loading
|
51
|
-
for (let size of resizes) {
|
52
|
-
// Calculate width and height respecting the aspect ratio
|
53
|
-
const { width, height } = getNewDimensions(
|
54
|
-
originalWidth,
|
55
|
-
originalHeight,
|
56
|
-
size,
|
57
|
-
);
|
58
|
-
|
59
|
-
const image = await resize(imageBlobOrFile, width, height);
|
60
|
-
|
61
|
-
const binaryStream = await FileStream.createFromBlob(image, owner);
|
62
|
-
imageDefinition[`${width}x${height}`] = binaryStream;
|
63
|
-
}
|
64
|
-
|
65
|
-
return imageDefinition;
|
66
|
-
}
|
67
|
-
|
68
|
-
async function getImageSize(
|
69
|
-
imageBlobOrFile: Blob | File,
|
70
|
-
): Promise<{ width: number; height: number }> {
|
71
|
-
const { width, height } = await new Promise<{
|
72
|
-
width: number;
|
73
|
-
height: number;
|
74
|
-
}>((resolve, reject) => {
|
75
|
-
const img = new Image();
|
76
|
-
img.onload = () => {
|
77
|
-
resolve({ width: img.width, height: img.height });
|
78
|
-
URL.revokeObjectURL(img.src);
|
79
|
-
};
|
80
|
-
img.onerror = () => {
|
81
|
-
reject(new Error("Failed to load image"));
|
82
|
-
URL.revokeObjectURL(img.src);
|
83
|
-
};
|
84
|
-
img.src = URL.createObjectURL(imageBlobOrFile);
|
85
|
-
});
|
86
|
-
|
87
|
-
return { width, height };
|
88
|
-
}
|
89
|
-
|
90
|
-
async function getPlaceholderBase64(
|
91
|
-
imageBlobOrFile: Blob | File,
|
92
|
-
): Promise<string> {
|
93
|
-
// Inizialize Reducer here to not have module side effects
|
94
|
-
if (!reducer) {
|
95
|
-
reducer = new ImageBlobReduce({ pica: new Pica() });
|
96
|
-
}
|
97
|
-
|
98
|
-
const canvas = await reducer.toCanvas(imageBlobOrFile, { max: 8 });
|
99
|
-
return canvas.toDataURL("image/png");
|
100
|
-
}
|
101
|
-
|
102
|
-
async function resize(
|
103
|
-
imageBlobOrFile: Blob | File,
|
104
|
-
width: number,
|
105
|
-
height: number,
|
106
|
-
): Promise<Blob> {
|
107
|
-
// Inizialize Reducer here to not have module side effects
|
108
|
-
if (!reducer) {
|
109
|
-
reducer = new ImageBlobReduce({ pica: new Pica() });
|
110
|
-
}
|
111
|
-
|
112
|
-
return reducer.toBlob(imageBlobOrFile, { max: Math.max(width, height) });
|
113
|
-
}
|
114
|
-
|
115
|
-
const getNewDimensions = (
|
116
|
-
originalWidth: number,
|
117
|
-
originalHeight: number,
|
118
|
-
maxSize: number,
|
119
|
-
) => {
|
120
|
-
const width =
|
121
|
-
originalWidth > originalHeight
|
122
|
-
? maxSize
|
123
|
-
: Math.round(maxSize * (originalWidth / originalHeight));
|
124
|
-
|
125
|
-
const height =
|
126
|
-
originalHeight > originalWidth
|
127
|
-
? maxSize
|
128
|
-
: Math.round(maxSize * (originalHeight / originalWidth));
|
129
|
-
|
130
|
-
return { width, height };
|
131
|
-
};
|
package/src/react/media.tsx
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
import { ImageDefinition, Loaded } from "jazz-tools";
|
2
|
-
import React, { useEffect, useState } from "react";
|
3
|
-
|
4
|
-
/** @category Media */
|
5
|
-
export function useProgressiveImg({
|
6
|
-
image,
|
7
|
-
maxWidth,
|
8
|
-
targetWidth,
|
9
|
-
}: {
|
10
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
11
|
-
maxWidth?: number;
|
12
|
-
targetWidth?: number;
|
13
|
-
}) {
|
14
|
-
const [current, setCurrent] = useState<
|
15
|
-
{ src?: string; res?: `${number}x${number}` | "placeholder" } | undefined
|
16
|
-
>(undefined);
|
17
|
-
|
18
|
-
useEffect(() => {
|
19
|
-
let lastHighestRes: string | undefined;
|
20
|
-
if (!image) return;
|
21
|
-
const unsub = image.subscribe({}, (update) => {
|
22
|
-
const highestRes = ImageDefinition.highestResAvailable(update, {
|
23
|
-
maxWidth,
|
24
|
-
targetWidth,
|
25
|
-
});
|
26
|
-
if (highestRes) {
|
27
|
-
if (highestRes.res !== lastHighestRes) {
|
28
|
-
lastHighestRes = highestRes.res;
|
29
|
-
const blob = highestRes.stream.toBlob();
|
30
|
-
if (blob) {
|
31
|
-
const blobURI = URL.createObjectURL(blob);
|
32
|
-
setCurrent({ src: blobURI, res: highestRes.res });
|
33
|
-
return () => {
|
34
|
-
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
|
35
|
-
};
|
36
|
-
}
|
37
|
-
}
|
38
|
-
} else {
|
39
|
-
setCurrent({
|
40
|
-
src: update?.placeholderDataURL,
|
41
|
-
res: "placeholder",
|
42
|
-
});
|
43
|
-
}
|
44
|
-
});
|
45
|
-
|
46
|
-
return unsub;
|
47
|
-
}, [image?.id, maxWidth]);
|
48
|
-
|
49
|
-
return {
|
50
|
-
src: current?.src,
|
51
|
-
res: current?.res,
|
52
|
-
originalSize: image?.originalSize,
|
53
|
-
};
|
54
|
-
}
|
55
|
-
|
56
|
-
/** @category Media */
|
57
|
-
export function ProgressiveImg({
|
58
|
-
children,
|
59
|
-
image,
|
60
|
-
maxWidth,
|
61
|
-
targetWidth,
|
62
|
-
}: {
|
63
|
-
children: (result: {
|
64
|
-
src: string | undefined;
|
65
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
66
|
-
originalSize: readonly [number, number] | undefined;
|
67
|
-
}) => React.ReactNode;
|
68
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
69
|
-
maxWidth?: number;
|
70
|
-
targetWidth?: number;
|
71
|
-
}): React.ReactNode {
|
72
|
-
const result = useProgressiveImg({ image, maxWidth, targetWidth });
|
73
|
-
return result ? children(result) : null;
|
74
|
-
}
|
package/src/react/scratch.tsx
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
import { FileStream, ImageDefinition } from "jazz-tools";
|
2
|
-
import { createJazzTestAccount } from "jazz-tools/testing";
|
3
|
-
|
4
|
-
// Simple document environment
|
5
|
-
global.document = {
|
6
|
-
createElement: () =>
|
7
|
-
({ src: "", onload: null }) as unknown as HTMLImageElement,
|
8
|
-
} as unknown as Document;
|
9
|
-
global.window = { innerWidth: 1000 } as unknown as Window & typeof globalThis;
|
10
|
-
|
11
|
-
const me = await createJazzTestAccount();
|
12
|
-
|
13
|
-
const mediumSizeBlob = new Blob([], { type: "image/jpeg" });
|
14
|
-
const image = ImageDefinition.create(
|
15
|
-
{
|
16
|
-
originalSize: [1920, 1080],
|
17
|
-
},
|
18
|
-
{ owner: me },
|
19
|
-
);
|
20
|
-
image["100x100"] = await FileStream.createFromBlob(mediumSizeBlob, {
|
21
|
-
owner: me,
|
22
|
-
});
|
23
|
-
image["1920x1080"] = await FileStream.createFromBlob(mediumSizeBlob, {
|
24
|
-
owner: me,
|
25
|
-
});
|
26
|
-
const imageElement = document.createElement("img");
|
27
|
-
// ---cut---
|
28
|
-
// Start with placeholder for immediate display
|
29
|
-
if (image.placeholderDataURL) {
|
30
|
-
imageElement.src = image.placeholderDataURL;
|
31
|
-
}
|
32
|
-
|
33
|
-
// Then load the best resolution for the current display
|
34
|
-
const screenWidth = window.innerWidth;
|
35
|
-
const bestRes = ImageDefinition.highestResAvailable(image, {
|
36
|
-
targetWidth: screenWidth,
|
37
|
-
});
|
38
|
-
|
39
|
-
if (bestRes) {
|
40
|
-
const blob = bestRes.stream.toBlob();
|
41
|
-
if (blob) {
|
42
|
-
const url = URL.createObjectURL(blob);
|
43
|
-
imageElement.src = url;
|
44
|
-
|
45
|
-
// Remember to revoke the URL when no longer needed
|
46
|
-
imageElement.onload = () => {
|
47
|
-
URL.revokeObjectURL(url);
|
48
|
-
};
|
49
|
-
}
|
50
|
-
}
|
@@ -1,79 +0,0 @@
|
|
1
|
-
import { ImageDefinition, Loaded } from "jazz-tools";
|
2
|
-
import React, { useEffect, useState } from "react";
|
3
|
-
|
4
|
-
/** @category Media */
|
5
|
-
export function useProgressiveImgNative({
|
6
|
-
image,
|
7
|
-
maxWidth,
|
8
|
-
targetWidth,
|
9
|
-
}: {
|
10
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
11
|
-
maxWidth?: number;
|
12
|
-
targetWidth?: number;
|
13
|
-
}) {
|
14
|
-
const [current, setCurrent] = useState<
|
15
|
-
{ src?: string; res?: `${number}x${number}` | "placeholder" } | undefined
|
16
|
-
>(undefined);
|
17
|
-
|
18
|
-
useEffect(() => {
|
19
|
-
let lastHighestRes: string | undefined;
|
20
|
-
if (!image) return;
|
21
|
-
const unsub = image.subscribe({}, (update) => {
|
22
|
-
const highestRes = ImageDefinition.highestResAvailable(update, {
|
23
|
-
maxWidth,
|
24
|
-
targetWidth,
|
25
|
-
});
|
26
|
-
if (highestRes && highestRes.res !== lastHighestRes) {
|
27
|
-
lastHighestRes = highestRes.res;
|
28
|
-
// use the base64 data directly
|
29
|
-
const dataUrl = highestRes.stream.asBase64({ dataURL: true });
|
30
|
-
if (dataUrl) {
|
31
|
-
setCurrent({
|
32
|
-
src: dataUrl,
|
33
|
-
res: highestRes.res,
|
34
|
-
});
|
35
|
-
} else {
|
36
|
-
// Fallback to placeholder if chunks aren't available
|
37
|
-
console.warn("No chunks available for image", image.id);
|
38
|
-
setCurrent({
|
39
|
-
src: update?.placeholderDataURL,
|
40
|
-
res: "placeholder",
|
41
|
-
});
|
42
|
-
}
|
43
|
-
} else if (!highestRes) {
|
44
|
-
setCurrent({
|
45
|
-
src: update?.placeholderDataURL,
|
46
|
-
res: "placeholder",
|
47
|
-
});
|
48
|
-
}
|
49
|
-
});
|
50
|
-
|
51
|
-
return unsub;
|
52
|
-
}, [image?.id, maxWidth]);
|
53
|
-
|
54
|
-
return {
|
55
|
-
src: current?.src,
|
56
|
-
res: current?.res,
|
57
|
-
originalSize: image?.originalSize,
|
58
|
-
};
|
59
|
-
}
|
60
|
-
|
61
|
-
/** @category Media */
|
62
|
-
export function ProgressiveImgNative({
|
63
|
-
children,
|
64
|
-
image,
|
65
|
-
maxWidth,
|
66
|
-
targetWidth,
|
67
|
-
}: {
|
68
|
-
children: (result: {
|
69
|
-
src: string | undefined;
|
70
|
-
res: `${number}x${number}` | "placeholder" | undefined;
|
71
|
-
originalSize: readonly [number, number] | undefined;
|
72
|
-
}) => React.ReactNode;
|
73
|
-
image: Loaded<typeof ImageDefinition> | null | undefined;
|
74
|
-
maxWidth?: number;
|
75
|
-
targetWidth?: number;
|
76
|
-
}) {
|
77
|
-
const result = useProgressiveImgNative({ image, maxWidth, targetWidth });
|
78
|
-
return result && children(result);
|
79
|
-
}
|