jazz-tools 0.16.5 → 0.17.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.
- 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 +44 -48
- package/CHANGELOG.md +28 -0
- package/dist/{chunk-H3BIFFQG.js → chunk-2SH44VLX.js} +35 -40
- package/dist/chunk-2SH44VLX.js.map +1 -0
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/inspector/{custom-element-TUXKXSZU.js → custom-element-I7L56H6B.js} +3 -5
- package/dist/inspector/{custom-element-TUXKXSZU.js.map → custom-element-I7L56H6B.js.map} +1 -1
- package/dist/inspector/index.js +2 -4
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/co-plain-text-view.d.ts +1 -1
- package/dist/inspector/viewer/co-plain-text-view.d.ts.map +1 -1
- package/dist/inspector/viewer/role-display.d.ts.map +1 -1
- package/dist/media/chunk-BBSS3NEY.js +211 -0
- package/dist/media/chunk-BBSS3NEY.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/ssr.d.ts.map +1 -1
- package/dist/react/ssr.js.map +1 -1
- 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/tests/testUtils.d.ts.map +1 -1
- package/dist/react-core/auth/PassphraseAuth.d.ts +1 -1
- package/dist/react-core/auth/PassphraseAuth.d.ts.map +1 -1
- package/dist/react-core/index.js +1 -3
- package/dist/react-core/index.js.map +1 -1
- package/dist/react-core/tests/testUtils.d.ts.map +1 -1
- 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/testing.js.map +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/deepLoading.d.ts +10 -10
- package/dist/tools/coValues/deepLoading.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/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts +1 -0
- package/dist/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.d.ts.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/inspector/viewer/co-plain-text-view.tsx +1 -5
- package/src/inspector/viewer/co-stream-view.tsx +1 -1
- package/src/inspector/viewer/role-display.tsx +4 -1
- package/src/{browser-media-images/index.test.browser.ts → media/create-image.test.ts} +146 -24
- 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 +327 -0
- package/src/media/utils.ts +202 -0
- package/src/react/index.ts +1 -2
- package/src/react/media/image.tsx +210 -0
- package/src/react/ssr.ts +1 -3
- package/src/react/tests/media/image.test.tsx +588 -0
- package/src/react/tests/testUtils.tsx +2 -10
- package/src/react-core/auth/PassphraseAuth.tsx +1 -5
- package/src/react-core/tests/testUtils.tsx +2 -10
- 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 +40 -7
- package/src/tools/coValues/deepLoading.ts +46 -32
- package/src/tools/coValues/extensions/imageDef.ts +3 -49
- package/src/tools/implementation/zodSchema/schemaTypes/FileStreamSchema.ts +6 -0
- package/src/tools/index.ts +0 -1
- package/src/tools/testing.ts +3 -1
- package/src/tools/tests/coList.test.ts +1 -1
- package/src/tools/tests/coMap.record.test-d.ts +105 -0
- package/src/tools/tests/coMap.record.test.ts +48 -2
- package/src/tools/tests/coMap.test-d.ts +50 -0
- 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 -21
- 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/browser-media-images/index.test.browser.d.ts +0 -2
- package/dist/browser-media-images/index.test.browser.d.ts.map +0 -1
- package/dist/chunk-H3BIFFQG.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
@@ -1,5 +1,5 @@
|
|
1
1
|
import { JsonObject } from "cojson";
|
2
|
-
export declare function CoPlainTextView({ data
|
2
|
+
export declare function CoPlainTextView({ data }: {
|
3
3
|
data: JsonObject;
|
4
4
|
}): import("react/jsx-runtime").JSX.Element | undefined;
|
5
5
|
//# sourceMappingURL=co-plain-text-view.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"co-plain-text-view.d.ts","sourceRoot":"","sources":["../../../src/inspector/viewer/co-plain-text-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC,wBAAgB,eAAe,CAAC,
|
1
|
+
{"version":3,"file":"co-plain-text-view.d.ts","sourceRoot":"","sources":["../../../src/inspector/viewer/co-plain-text-view.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGpC,wBAAgB,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,uDAS7D"}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"role-display.d.ts","sourceRoot":"","sources":["../../../src/inspector/viewer/role-display.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAI/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,GACN,EAAE;
|
1
|
+
{"version":3,"file":"role-display.d.ts","sourceRoot":"","sources":["../../../src/inspector/viewer/role-display.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAI/C,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,KAAK,GACN,EAAE;IACD,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;CACnB,kDAoBA"}
|
@@ -0,0 +1,211 @@
|
|
1
|
+
// src/media/create-image.ts
|
2
|
+
import {
|
3
|
+
ImageDefinition
|
4
|
+
} from "jazz-tools";
|
5
|
+
function createImageFactory(impl) {
|
6
|
+
return (source, options) => createImage(source, options, impl);
|
7
|
+
}
|
8
|
+
async function createImage(imageBlobOrFile, options, impl) {
|
9
|
+
const { width: originalWidth, height: originalHeight } = await impl.getImageSize(imageBlobOrFile);
|
10
|
+
const def = {
|
11
|
+
originalSize: [originalWidth, originalHeight],
|
12
|
+
progressive: false,
|
13
|
+
placeholderDataURL: void 0,
|
14
|
+
files: {}
|
15
|
+
};
|
16
|
+
if (options?.placeholder === "blur") {
|
17
|
+
def.placeholderDataURL = await impl.getPlaceholderBase64(imageBlobOrFile);
|
18
|
+
}
|
19
|
+
if (options?.maxSize === void 0) {
|
20
|
+
def.original = await impl.createFileStreamFromSource(
|
21
|
+
imageBlobOrFile,
|
22
|
+
options?.owner
|
23
|
+
);
|
24
|
+
def.files[`${originalWidth}x${originalHeight}`] = def.original;
|
25
|
+
} else if (options?.maxSize >= originalWidth && options?.maxSize >= originalHeight) {
|
26
|
+
def.original = await impl.createFileStreamFromSource(
|
27
|
+
imageBlobOrFile,
|
28
|
+
options?.owner
|
29
|
+
);
|
30
|
+
def.files[`${originalWidth}x${originalHeight}`] = def.original;
|
31
|
+
} else {
|
32
|
+
const { width, height } = getNewDimensions(
|
33
|
+
originalWidth,
|
34
|
+
originalHeight,
|
35
|
+
options.maxSize
|
36
|
+
);
|
37
|
+
const blob = await impl.resize(imageBlobOrFile, width, height);
|
38
|
+
def.originalSize = [width, height];
|
39
|
+
def.original = await impl.createFileStreamFromSource(blob, options?.owner);
|
40
|
+
def.files[`${width}x${height}`] = def.original;
|
41
|
+
}
|
42
|
+
const imageCoValue = ImageDefinition.create(
|
43
|
+
{
|
44
|
+
originalSize: def.originalSize,
|
45
|
+
progressive: def.progressive,
|
46
|
+
placeholderDataURL: def.placeholderDataURL,
|
47
|
+
original: def.original,
|
48
|
+
...def.files
|
49
|
+
},
|
50
|
+
options?.owner
|
51
|
+
);
|
52
|
+
if (options?.progressive) {
|
53
|
+
imageCoValue.progressive = true;
|
54
|
+
const resizes = [256, 1024, 2048].filter(
|
55
|
+
(s) => s < Math.max(imageCoValue.originalSize[0], imageCoValue.originalSize[1])
|
56
|
+
);
|
57
|
+
for (const size of resizes) {
|
58
|
+
const { width, height } = getNewDimensions(
|
59
|
+
originalWidth,
|
60
|
+
originalHeight,
|
61
|
+
size
|
62
|
+
);
|
63
|
+
const blob = await impl.resize(imageBlobOrFile, width, height);
|
64
|
+
imageCoValue[`${width}x${height}`] = await impl.createFileStreamFromSource(blob, options?.owner);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
return imageCoValue;
|
68
|
+
}
|
69
|
+
var getNewDimensions = (originalWidth, originalHeight, maxSize) => {
|
70
|
+
if (originalWidth > originalHeight) {
|
71
|
+
return {
|
72
|
+
width: maxSize,
|
73
|
+
height: Math.round(maxSize * (originalHeight / originalWidth))
|
74
|
+
};
|
75
|
+
}
|
76
|
+
return {
|
77
|
+
width: Math.round(maxSize * (originalWidth / originalHeight)),
|
78
|
+
height: maxSize
|
79
|
+
};
|
80
|
+
};
|
81
|
+
|
82
|
+
// src/media/utils.ts
|
83
|
+
import { Account as Account2, ImageDefinition as ImageDefinition2 } from "jazz-tools";
|
84
|
+
function highestResAvailable(image, wantedWidth, wantedHeight) {
|
85
|
+
const availableSizes = image._raw.keys().filter((key) => /^\d+x\d+$/.test(key)).map((key) => {
|
86
|
+
const [w, h] = key.split("x").map(Number);
|
87
|
+
return [w, h, key];
|
88
|
+
});
|
89
|
+
if (availableSizes.length === 0) {
|
90
|
+
return image.original ? {
|
91
|
+
width: image.originalSize[0],
|
92
|
+
height: image.originalSize[1],
|
93
|
+
image: image.original
|
94
|
+
} : null;
|
95
|
+
}
|
96
|
+
const sortedSizes = availableSizes.map((size) => {
|
97
|
+
return {
|
98
|
+
size,
|
99
|
+
match: sizesMatchWanted(size[0], size[1], wantedWidth, wantedHeight),
|
100
|
+
isLoaded: isLoaded(image._raw.get(size[2]))
|
101
|
+
};
|
102
|
+
}).sort((a, b) => a.match - b.match);
|
103
|
+
const bestLoaded = [...sortedSizes].reverse().find((el) => el.isLoaded && image[el.size[2]]?.getChunks());
|
104
|
+
const bestTarget = sortedSizes.find((el) => el.match > 0.95) || sortedSizes.at(-1);
|
105
|
+
if (image[bestTarget.size[2]]?.getChunks()) {
|
106
|
+
return image[bestTarget.size[2]] ? {
|
107
|
+
width: bestTarget.size[0],
|
108
|
+
height: bestTarget.size[1],
|
109
|
+
image: image[bestTarget.size[2]]
|
110
|
+
} : null;
|
111
|
+
}
|
112
|
+
if (bestLoaded) {
|
113
|
+
image[bestTarget.size[2]]?.getChunks();
|
114
|
+
return image[bestLoaded.size[2]] ? {
|
115
|
+
width: bestLoaded.size[0],
|
116
|
+
height: bestLoaded.size[1],
|
117
|
+
image: image[bestLoaded.size[2]]
|
118
|
+
} : null;
|
119
|
+
}
|
120
|
+
for (let size of sortedSizes) {
|
121
|
+
if (size.match <= bestTarget.match) {
|
122
|
+
image[size.size[2]]?.getChunks();
|
123
|
+
}
|
124
|
+
}
|
125
|
+
return null;
|
126
|
+
}
|
127
|
+
function sizesMatchWanted(w, h, wantedW, wantedH) {
|
128
|
+
const area1 = w * h;
|
129
|
+
const area2 = wantedW * wantedH;
|
130
|
+
const areaRatio = area1 / area2;
|
131
|
+
return areaRatio;
|
132
|
+
}
|
133
|
+
function isLoaded(id) {
|
134
|
+
if (!id) {
|
135
|
+
return false;
|
136
|
+
}
|
137
|
+
return !!Account2.getMe()._raw.core.node.getLoaded(id);
|
138
|
+
}
|
139
|
+
async function loadImage(imageOrId) {
|
140
|
+
if (typeof imageOrId === "string") {
|
141
|
+
const image = await ImageDefinition2.load(imageOrId, {
|
142
|
+
resolve: {
|
143
|
+
original: true
|
144
|
+
}
|
145
|
+
});
|
146
|
+
if (image === null || image.original === null) {
|
147
|
+
return null;
|
148
|
+
}
|
149
|
+
return {
|
150
|
+
width: image.originalSize[0],
|
151
|
+
height: image.originalSize[1],
|
152
|
+
image: image.original
|
153
|
+
};
|
154
|
+
}
|
155
|
+
await imageOrId.ensureLoaded({
|
156
|
+
resolve: {
|
157
|
+
original: true
|
158
|
+
}
|
159
|
+
});
|
160
|
+
if (!imageOrId.original) {
|
161
|
+
console.warn("Unable to find the original image");
|
162
|
+
return null;
|
163
|
+
}
|
164
|
+
return {
|
165
|
+
width: imageOrId.originalSize[0],
|
166
|
+
height: imageOrId.originalSize[1],
|
167
|
+
image: imageOrId.original
|
168
|
+
};
|
169
|
+
}
|
170
|
+
async function loadImageBySize(imageOrId, wantedWidth, wantedHeight) {
|
171
|
+
const image = typeof imageOrId === "string" ? await ImageDefinition2.load(imageOrId) : imageOrId;
|
172
|
+
if (image === null) {
|
173
|
+
return null;
|
174
|
+
}
|
175
|
+
if (image.progressive === false) {
|
176
|
+
return loadImage(imageOrId);
|
177
|
+
}
|
178
|
+
const availableSizes = image._raw.keys().filter((key) => /^\d+x\d+$/.test(key)).map((key) => {
|
179
|
+
const [w, h] = key.split("x").map(Number);
|
180
|
+
return [w, h, key];
|
181
|
+
});
|
182
|
+
if (availableSizes.length === 0) {
|
183
|
+
return null;
|
184
|
+
}
|
185
|
+
const sortedSizes = availableSizes.map((size) => ({
|
186
|
+
size,
|
187
|
+
match: sizesMatchWanted(size[0], size[1], wantedWidth, wantedHeight)
|
188
|
+
})).sort((a, b) => a.match - b.match);
|
189
|
+
const bestTarget = sortedSizes.find((el) => el.match > 0.95) || sortedSizes.at(-1);
|
190
|
+
const deepLoaded = await ImageDefinition2.load(image.id, {
|
191
|
+
resolve: {
|
192
|
+
[bestTarget.size[2]]: true
|
193
|
+
}
|
194
|
+
});
|
195
|
+
if (deepLoaded === null || deepLoaded[bestTarget.size[2]] === void 0) {
|
196
|
+
return null;
|
197
|
+
}
|
198
|
+
return {
|
199
|
+
width: bestTarget.size[0],
|
200
|
+
height: bestTarget.size[1],
|
201
|
+
image: deepLoaded[bestTarget.size[2]]
|
202
|
+
};
|
203
|
+
}
|
204
|
+
|
205
|
+
export {
|
206
|
+
createImageFactory,
|
207
|
+
highestResAvailable,
|
208
|
+
loadImage,
|
209
|
+
loadImageBySize
|
210
|
+
};
|
211
|
+
//# sourceMappingURL=chunk-BBSS3NEY.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/media/create-image.ts","../../src/media/utils.ts"],"sourcesContent":["import {\n Account,\n FileStream,\n Group,\n ImageDefinition,\n type Loaded,\n} from \"jazz-tools\";\n\nexport type SourceType = Blob | File | string;\n\nexport type CreateImageOptions = {\n /** The owner of the image. Can be either a Group or Account. If not specified, the current user will be the owner. */\n owner?: Group | Account;\n /**\n * Controls placeholder generation for the image.\n * - `\"blur\"`: Generates a blurred placeholder image (default)\n * - `false`: No placeholder is generated\n * @default \"blur\"\n */\n placeholder?: \"blur\" | false;\n /**\n * Maximum size constraint for the image. The image will be resized to fit within this size while maintaining aspect ratio.\n * If the image is smaller than maxSize in both dimensions, no resizing occurs.\n * @example 1024 // Resizes image to fit within 1024px in the largest dimension\n */\n maxSize?: number; // | [number, number];\n /**\n * The progressive loading pattern is a technique that allows images to load incrementally, starting with a small version and gradually replacing it with a larger version as it becomes available.\n * This is useful for improving the user experience by showing a placeholder while the image is loading.\n *\n * Passing progressive: true to createImage() will create internal smaller versions of the image for future uses.\n *\n * @default false\n */\n progressive?: boolean;\n};\n\nexport type CreateImageImpl = {\n createFileStreamFromSource: (\n imageBlobOrFile: SourceType,\n owner?: Group | Account,\n ) => Promise<FileStream>;\n getImageSize: (\n imageBlobOrFile: SourceType,\n ) => Promise<{ width: number; height: number }>;\n getPlaceholderBase64: (imageBlobOrFile: SourceType) => Promise<string>;\n resize: (\n imageBlobOrFile: SourceType,\n width: number,\n height: number,\n ) => Promise<Blob | string>;\n};\n\nexport function createImageFactory(impl: CreateImageImpl) {\n return (source: SourceType, options: CreateImageOptions) =>\n createImage(source, options, impl);\n}\n\nasync function createImage(\n imageBlobOrFile: SourceType,\n options: CreateImageOptions,\n impl: CreateImageImpl,\n): Promise<Loaded<typeof ImageDefinition, { $each: true }>> {\n // Get the original size of the image\n const { width: originalWidth, height: originalHeight } =\n await impl.getImageSize(imageBlobOrFile);\n\n const def: {\n originalSize: [number, number];\n progressive: boolean;\n placeholderDataURL: string | undefined;\n original?: FileStream;\n files: Record<string, FileStream>;\n } = {\n originalSize: [originalWidth, originalHeight],\n progressive: false,\n placeholderDataURL: undefined,\n files: {},\n };\n\n // Placeholder\n if (options?.placeholder === \"blur\") {\n def.placeholderDataURL = await impl.getPlaceholderBase64(imageBlobOrFile);\n }\n\n /**\n * Original\n *\n * Save the original image.\n * If the maxSize is set, resize the image to the maxSize if needed\n */\n if (options?.maxSize === undefined) {\n def.original = await impl.createFileStreamFromSource(\n imageBlobOrFile,\n options?.owner,\n );\n def.files[`${originalWidth}x${originalHeight}`] = def.original;\n } else if (\n options?.maxSize >= originalWidth &&\n options?.maxSize >= originalHeight\n ) {\n // no resizes required, just return the original image\n def.original = await impl.createFileStreamFromSource(\n imageBlobOrFile,\n options?.owner,\n );\n def.files[`${originalWidth}x${originalHeight}`] = def.original;\n } else {\n const { width, height } = getNewDimensions(\n originalWidth,\n originalHeight,\n options.maxSize,\n );\n\n const blob = await impl.resize(imageBlobOrFile, width, height);\n def.originalSize = [width, height];\n def.original = await impl.createFileStreamFromSource(blob, options?.owner);\n def.files[`${width}x${height}`] = def.original;\n }\n\n const imageCoValue = ImageDefinition.create(\n {\n originalSize: def.originalSize,\n progressive: def.progressive,\n placeholderDataURL: def.placeholderDataURL,\n original: def.original,\n ...def.files,\n },\n options?.owner,\n );\n\n /**\n * Progressive loading\n *\n * Save a set of resized images using three sizes: 256, 1024, 2048\n *\n * On the client side, the image will be loaded progressively, starting from the smallest size and increasing the size until the original size is reached.\n */\n if (options?.progressive) {\n imageCoValue.progressive = true;\n\n const resizes = ([256, 1024, 2048] as const).filter(\n (s) =>\n s <\n Math.max(imageCoValue.originalSize[0], imageCoValue.originalSize[1]),\n );\n\n for (const size of resizes) {\n const { width, height } = getNewDimensions(\n originalWidth,\n originalHeight,\n size,\n );\n\n const blob = await impl.resize(imageBlobOrFile, width, height);\n imageCoValue[`${width}x${height}`] =\n await impl.createFileStreamFromSource(blob, options?.owner);\n }\n }\n\n return imageCoValue;\n}\n\nconst getNewDimensions = (\n originalWidth: number,\n originalHeight: number,\n maxSize: number,\n) => {\n if (originalWidth > originalHeight) {\n return {\n width: maxSize,\n height: Math.round(maxSize * (originalHeight / originalWidth)),\n };\n }\n\n return {\n width: Math.round(maxSize * (originalWidth / originalHeight)),\n height: maxSize,\n };\n};\n","import type { CoID } from \"cojson\";\nimport { Account, FileStream, ImageDefinition } from \"jazz-tools\";\n\nexport function highestResAvailable(\n image: ImageDefinition,\n wantedWidth: number,\n wantedHeight: number,\n): { width: number; height: number; image: FileStream } | null {\n const availableSizes: [number, number, string][] = image._raw\n .keys()\n .filter((key) => /^\\d+x\\d+$/.test(key))\n .map((key) => {\n const [w, h] = key.split(\"x\").map(Number) as [number, number];\n return [w, h, key];\n });\n\n if (availableSizes.length === 0) {\n return image.original\n ? {\n width: image.originalSize[0],\n height: image.originalSize[1],\n image: image.original,\n }\n : null;\n }\n\n const sortedSizes = availableSizes\n .map((size) => {\n return {\n size,\n match: sizesMatchWanted(size[0], size[1], wantedWidth, wantedHeight),\n isLoaded: isLoaded(image._raw.get(size[2]) as CoID<any> | undefined),\n };\n })\n .sort((a, b) => a.match - b.match);\n\n // We try to find the better already loaded image\n // note: `toReversed` is not available in react-native.\n const bestLoaded = [...sortedSizes]\n .reverse()\n .find((el) => el.isLoaded && image[el.size[2]]?.getChunks());\n\n // if I can't find a good match, let's use the highest resolution\n const bestTarget =\n sortedSizes.find((el) => el.match > 0.95) || sortedSizes.at(-1);\n\n // if the best target is already loaded, we are done\n if (image[bestTarget!.size[2]]?.getChunks()) {\n return image[bestTarget!.size[2]]\n ? {\n width: bestTarget!.size[0],\n height: bestTarget!.size[1],\n image: image[bestTarget!.size[2]]!,\n }\n : null;\n }\n\n // if the best already loaded is not the best target\n // let's trigger the load of the best target\n if (bestLoaded) {\n image[bestTarget!.size[2]]?.getChunks();\n return image[bestLoaded.size[2]]\n ? {\n width: bestLoaded.size[0],\n height: bestLoaded.size[1],\n image: image[bestLoaded.size[2]]!,\n }\n : null;\n }\n\n // if nothing is loaded, then start fetching all the images till the best\n for (let size of sortedSizes) {\n if (size.match <= bestTarget!.match) {\n image[size.size[2]]?.getChunks();\n }\n }\n\n return null;\n}\n\nfunction sizesMatchWanted(\n w: number,\n h: number,\n wantedW: number,\n wantedH: number,\n): number {\n const area1 = w * h;\n const area2 = wantedW * wantedH;\n\n const areaRatio = area1 / area2;\n\n // // Below 0.95 means the image is too small, we don't want to upscale it\n // if (areaRatio < 0.95) {\n // return 9999;\n // }\n\n return areaRatio;\n}\n\nfunction isLoaded(id: CoID<any> | null | undefined): boolean {\n if (!id) {\n return false;\n }\n\n return !!Account.getMe()._raw.core.node.getLoaded(id);\n}\n\nexport async function loadImage(\n imageOrId: ImageDefinition | string,\n): Promise<{ width: number; height: number; image: FileStream } | null> {\n if (typeof imageOrId === \"string\") {\n const image = await ImageDefinition.load(imageOrId, {\n resolve: {\n original: true,\n },\n });\n\n if (image === null || image.original === null) {\n return null;\n }\n\n return {\n width: image.originalSize[0],\n height: image.originalSize[1],\n image: image.original,\n };\n }\n\n await imageOrId.ensureLoaded({\n resolve: {\n original: true,\n },\n });\n\n if (!imageOrId.original) {\n console.warn(\"Unable to find the original image\");\n return null;\n }\n\n return {\n width: imageOrId.originalSize[0],\n height: imageOrId.originalSize[1],\n image: imageOrId.original,\n };\n}\n\nexport async function loadImageBySize(\n imageOrId: ImageDefinition | string,\n wantedWidth: number,\n wantedHeight: number,\n): Promise<{ width: number; height: number; image: FileStream } | null> {\n const image =\n typeof imageOrId === \"string\"\n ? await ImageDefinition.load(imageOrId)\n : imageOrId;\n\n if (image === null) {\n return null;\n }\n\n if (image.progressive === false) {\n return loadImage(imageOrId);\n }\n\n const availableSizes: [number, number, string][] = image._raw\n .keys()\n .filter((key) => /^\\d+x\\d+$/.test(key))\n .map((key) => {\n const [w, h] = key.split(\"x\").map(Number) as [number, number];\n return [w, h, key];\n });\n\n if (availableSizes.length === 0) {\n return null;\n }\n\n const sortedSizes = availableSizes\n .map((size) => ({\n size,\n match: sizesMatchWanted(size[0], size[1], wantedWidth, wantedHeight),\n }))\n .sort((a, b) => a.match - b.match);\n\n const bestTarget =\n sortedSizes.find((el) => el.match > 0.95) || sortedSizes.at(-1)!;\n\n const deepLoaded = await ImageDefinition.load(image.id, {\n resolve: {\n [bestTarget.size[2]]: true,\n },\n });\n\n if (deepLoaded === null || deepLoaded[bestTarget.size[2]] === undefined) {\n return null;\n }\n\n return {\n width: bestTarget.size[0],\n height: bestTarget.size[1],\n image: deepLoaded[bestTarget.size[2]]!,\n };\n}\n"],"mappings":";AAAA;AAAA,EAIE;AAAA,OAEK;AA+CA,SAAS,mBAAmB,MAAuB;AACxD,SAAO,CAAC,QAAoB,YAC1B,YAAY,QAAQ,SAAS,IAAI;AACrC;AAEA,eAAe,YACb,iBACA,SACA,MAC0D;AAE1D,QAAM,EAAE,OAAO,eAAe,QAAQ,eAAe,IACnD,MAAM,KAAK,aAAa,eAAe;AAEzC,QAAM,MAMF;AAAA,IACF,cAAc,CAAC,eAAe,cAAc;AAAA,IAC5C,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,OAAO,CAAC;AAAA,EACV;AAGA,MAAI,SAAS,gBAAgB,QAAQ;AACnC,QAAI,qBAAqB,MAAM,KAAK,qBAAqB,eAAe;AAAA,EAC1E;AAQA,MAAI,SAAS,YAAY,QAAW;AAClC,QAAI,WAAW,MAAM,KAAK;AAAA,MACxB;AAAA,MACA,SAAS;AAAA,IACX;AACA,QAAI,MAAM,GAAG,aAAa,IAAI,cAAc,EAAE,IAAI,IAAI;AAAA,EACxD,WACE,SAAS,WAAW,iBACpB,SAAS,WAAW,gBACpB;AAEA,QAAI,WAAW,MAAM,KAAK;AAAA,MACxB;AAAA,MACA,SAAS;AAAA,IACX;AACA,QAAI,MAAM,GAAG,aAAa,IAAI,cAAc,EAAE,IAAI,IAAI;AAAA,EACxD,OAAO;AACL,UAAM,EAAE,OAAO,OAAO,IAAI;AAAA,MACxB;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,UAAM,OAAO,MAAM,KAAK,OAAO,iBAAiB,OAAO,MAAM;AAC7D,QAAI,eAAe,CAAC,OAAO,MAAM;AACjC,QAAI,WAAW,MAAM,KAAK,2BAA2B,MAAM,SAAS,KAAK;AACzE,QAAI,MAAM,GAAG,KAAK,IAAI,MAAM,EAAE,IAAI,IAAI;AAAA,EACxC;AAEA,QAAM,eAAe,gBAAgB;AAAA,IACnC;AAAA,MACE,cAAc,IAAI;AAAA,MAClB,aAAa,IAAI;AAAA,MACjB,oBAAoB,IAAI;AAAA,MACxB,UAAU,IAAI;AAAA,MACd,GAAG,IAAI;AAAA,IACT;AAAA,IACA,SAAS;AAAA,EACX;AASA,MAAI,SAAS,aAAa;AACxB,iBAAa,cAAc;AAE3B,UAAM,UAAW,CAAC,KAAK,MAAM,IAAI,EAAY;AAAA,MAC3C,CAAC,MACC,IACA,KAAK,IAAI,aAAa,aAAa,CAAC,GAAG,aAAa,aAAa,CAAC,CAAC;AAAA,IACvE;AAEA,eAAW,QAAQ,SAAS;AAC1B,YAAM,EAAE,OAAO,OAAO,IAAI;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,KAAK,OAAO,iBAAiB,OAAO,MAAM;AAC7D,mBAAa,GAAG,KAAK,IAAI,MAAM,EAAE,IAC/B,MAAM,KAAK,2BAA2B,MAAM,SAAS,KAAK;AAAA,IAC9D;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,mBAAmB,CACvB,eACA,gBACA,YACG;AACH,MAAI,gBAAgB,gBAAgB;AAClC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ,KAAK,MAAM,WAAW,iBAAiB,cAAc;AAAA,IAC/D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,KAAK,MAAM,WAAW,gBAAgB,eAAe;AAAA,IAC5D,QAAQ;AAAA,EACV;AACF;;;AClLA,SAAS,WAAAA,UAAqB,mBAAAC,wBAAuB;AAE9C,SAAS,oBACd,OACA,aACA,cAC6D;AAC7D,QAAM,iBAA6C,MAAM,KACtD,KAAK,EACL,OAAO,CAAC,QAAQ,YAAY,KAAK,GAAG,CAAC,EACrC,IAAI,CAAC,QAAQ;AACZ,UAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM;AACxC,WAAO,CAAC,GAAG,GAAG,GAAG;AAAA,EACnB,CAAC;AAEH,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO,MAAM,WACT;AAAA,MACE,OAAO,MAAM,aAAa,CAAC;AAAA,MAC3B,QAAQ,MAAM,aAAa,CAAC;AAAA,MAC5B,OAAO,MAAM;AAAA,IACf,IACA;AAAA,EACN;AAEA,QAAM,cAAc,eACjB,IAAI,CAAC,SAAS;AACb,WAAO;AAAA,MACL;AAAA,MACA,OAAO,iBAAiB,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,aAAa,YAAY;AAAA,MACnE,UAAU,SAAS,MAAM,KAAK,IAAI,KAAK,CAAC,CAAC,CAA0B;AAAA,IACrE;AAAA,EACF,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAInC,QAAM,aAAa,CAAC,GAAG,WAAW,EAC/B,QAAQ,EACR,KAAK,CAAC,OAAO,GAAG,YAAY,MAAM,GAAG,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;AAG7D,QAAM,aACJ,YAAY,KAAK,CAAC,OAAO,GAAG,QAAQ,IAAI,KAAK,YAAY,GAAG,EAAE;AAGhE,MAAI,MAAM,WAAY,KAAK,CAAC,CAAC,GAAG,UAAU,GAAG;AAC3C,WAAO,MAAM,WAAY,KAAK,CAAC,CAAC,IAC5B;AAAA,MACE,OAAO,WAAY,KAAK,CAAC;AAAA,MACzB,QAAQ,WAAY,KAAK,CAAC;AAAA,MAC1B,OAAO,MAAM,WAAY,KAAK,CAAC,CAAC;AAAA,IAClC,IACA;AAAA,EACN;AAIA,MAAI,YAAY;AACd,UAAM,WAAY,KAAK,CAAC,CAAC,GAAG,UAAU;AACtC,WAAO,MAAM,WAAW,KAAK,CAAC,CAAC,IAC3B;AAAA,MACE,OAAO,WAAW,KAAK,CAAC;AAAA,MACxB,QAAQ,WAAW,KAAK,CAAC;AAAA,MACzB,OAAO,MAAM,WAAW,KAAK,CAAC,CAAC;AAAA,IACjC,IACA;AAAA,EACN;AAGA,WAAS,QAAQ,aAAa;AAC5B,QAAI,KAAK,SAAS,WAAY,OAAO;AACnC,YAAM,KAAK,KAAK,CAAC,CAAC,GAAG,UAAU;AAAA,IACjC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,iBACP,GACA,GACA,SACA,SACQ;AACR,QAAM,QAAQ,IAAI;AAClB,QAAM,QAAQ,UAAU;AAExB,QAAM,YAAY,QAAQ;AAO1B,SAAO;AACT;AAEA,SAAS,SAAS,IAA2C;AAC3D,MAAI,CAAC,IAAI;AACP,WAAO;AAAA,EACT;AAEA,SAAO,CAAC,CAACD,SAAQ,MAAM,EAAE,KAAK,KAAK,KAAK,UAAU,EAAE;AACtD;AAEA,eAAsB,UACpB,WACsE;AACtE,MAAI,OAAO,cAAc,UAAU;AACjC,UAAM,QAAQ,MAAMC,iBAAgB,KAAK,WAAW;AAAA,MAClD,SAAS;AAAA,QACP,UAAU;AAAA,MACZ;AAAA,IACF,CAAC;AAED,QAAI,UAAU,QAAQ,MAAM,aAAa,MAAM;AAC7C,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,MACL,OAAO,MAAM,aAAa,CAAC;AAAA,MAC3B,QAAQ,MAAM,aAAa,CAAC;AAAA,MAC5B,OAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,QAAM,UAAU,aAAa;AAAA,IAC3B,SAAS;AAAA,MACP,UAAU;AAAA,IACZ;AAAA,EACF,CAAC;AAED,MAAI,CAAC,UAAU,UAAU;AACvB,YAAQ,KAAK,mCAAmC;AAChD,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,OAAO,UAAU,aAAa,CAAC;AAAA,IAC/B,QAAQ,UAAU,aAAa,CAAC;AAAA,IAChC,OAAO,UAAU;AAAA,EACnB;AACF;AAEA,eAAsB,gBACpB,WACA,aACA,cACsE;AACtE,QAAM,QACJ,OAAO,cAAc,WACjB,MAAMA,iBAAgB,KAAK,SAAS,IACpC;AAEN,MAAI,UAAU,MAAM;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,gBAAgB,OAAO;AAC/B,WAAO,UAAU,SAAS;AAAA,EAC5B;AAEA,QAAM,iBAA6C,MAAM,KACtD,KAAK,EACL,OAAO,CAAC,QAAQ,YAAY,KAAK,GAAG,CAAC,EACrC,IAAI,CAAC,QAAQ;AACZ,UAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM;AACxC,WAAO,CAAC,GAAG,GAAG,GAAG;AAAA,EACnB,CAAC;AAEH,MAAI,eAAe,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,eACjB,IAAI,CAAC,UAAU;AAAA,IACd;AAAA,IACA,OAAO,iBAAiB,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,aAAa,YAAY;AAAA,EACrE,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAEnC,QAAM,aACJ,YAAY,KAAK,CAAC,OAAO,GAAG,QAAQ,IAAI,KAAK,YAAY,GAAG,EAAE;AAEhE,QAAM,aAAa,MAAMA,iBAAgB,KAAK,MAAM,IAAI;AAAA,IACtD,SAAS;AAAA,MACP,CAAC,WAAW,KAAK,CAAC,CAAC,GAAG;AAAA,IACxB;AAAA,EACF,CAAC;AAED,MAAI,eAAe,QAAQ,WAAW,WAAW,KAAK,CAAC,CAAC,MAAM,QAAW;AACvE,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,OAAO,WAAW,KAAK,CAAC;AAAA,IACxB,QAAQ,WAAW,KAAK,CAAC;AAAA,IACzB,OAAO,WAAW,WAAW,KAAK,CAAC,CAAC;AAAA,EACtC;AACF;","names":["Account","ImageDefinition"]}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import { Account, FileStream, Group } from "jazz-tools";
|
2
|
+
export type SourceType = Blob | File | string;
|
3
|
+
export type CreateImageOptions = {
|
4
|
+
/** The owner of the image. Can be either a Group or Account. If not specified, the current user will be the owner. */
|
5
|
+
owner?: Group | Account;
|
6
|
+
/**
|
7
|
+
* Controls placeholder generation for the image.
|
8
|
+
* - `"blur"`: Generates a blurred placeholder image (default)
|
9
|
+
* - `false`: No placeholder is generated
|
10
|
+
* @default "blur"
|
11
|
+
*/
|
12
|
+
placeholder?: "blur" | false;
|
13
|
+
/**
|
14
|
+
* Maximum size constraint for the image. The image will be resized to fit within this size while maintaining aspect ratio.
|
15
|
+
* If the image is smaller than maxSize in both dimensions, no resizing occurs.
|
16
|
+
* @example 1024 // Resizes image to fit within 1024px in the largest dimension
|
17
|
+
*/
|
18
|
+
maxSize?: number;
|
19
|
+
/**
|
20
|
+
* The progressive loading pattern is a technique that allows images to load incrementally, starting with a small version and gradually replacing it with a larger version as it becomes available.
|
21
|
+
* This is useful for improving the user experience by showing a placeholder while the image is loading.
|
22
|
+
*
|
23
|
+
* Passing progressive: true to createImage() will create internal smaller versions of the image for future uses.
|
24
|
+
*
|
25
|
+
* @default false
|
26
|
+
*/
|
27
|
+
progressive?: boolean;
|
28
|
+
};
|
29
|
+
export type CreateImageImpl = {
|
30
|
+
createFileStreamFromSource: (imageBlobOrFile: SourceType, owner?: Group | Account) => Promise<FileStream>;
|
31
|
+
getImageSize: (imageBlobOrFile: SourceType) => Promise<{
|
32
|
+
width: number;
|
33
|
+
height: number;
|
34
|
+
}>;
|
35
|
+
getPlaceholderBase64: (imageBlobOrFile: SourceType) => Promise<string>;
|
36
|
+
resize: (imageBlobOrFile: SourceType, width: number, height: number) => Promise<Blob | string>;
|
37
|
+
};
|
38
|
+
export declare function createImageFactory(impl: CreateImageImpl): (source: SourceType, options: CreateImageOptions) => Promise<{
|
39
|
+
[key: string]: FileStream;
|
40
|
+
} & {
|
41
|
+
original: FileStream | null;
|
42
|
+
originalSize: [number, number];
|
43
|
+
placeholderDataURL: string | undefined;
|
44
|
+
progressive: boolean;
|
45
|
+
} & {
|
46
|
+
[key: string]: FileStream | null;
|
47
|
+
} & import("jazz-tools").CoMap>;
|
48
|
+
//# sourceMappingURL=create-image.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"create-image.d.ts","sourceRoot":"","sources":["../../src/media/create-image.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,UAAU,EACV,KAAK,EAGN,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC;AAE9C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,sHAAsH;IACtH,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACxB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,0BAA0B,EAAE,CAC1B,eAAe,EAAE,UAAU,EAC3B,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,KACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,YAAY,EAAE,CACZ,eAAe,EAAE,UAAU,KACxB,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,oBAAoB,EAAE,CAAC,eAAe,EAAE,UAAU,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,MAAM,EAAE,CACN,eAAe,EAAE,UAAU,EAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;CAC7B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,YACtC,UAAU,WAAW,kBAAkB;;;;;;;;;gCAExD"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"create-image.test.d.ts","sourceRoot":"","sources":["../../src/media/create-image.test.ts"],"names":[],"mappings":""}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { FileStream } from "jazz-tools";
|
2
|
+
import { CreateImageOptions, createImageFactory } from "./create-image.js";
|
3
|
+
export { highestResAvailable, loadImage, loadImageBySize } from "./utils.js";
|
4
|
+
export { createImageFactory };
|
5
|
+
export declare function createImage(imageBlobOrFile: Blob | File | string, options?: CreateImageOptions): Promise<{
|
6
|
+
[key: string]: FileStream;
|
7
|
+
} & {
|
8
|
+
original: FileStream | null;
|
9
|
+
originalSize: [number, number];
|
10
|
+
placeholderDataURL: string | undefined;
|
11
|
+
progressive: boolean;
|
12
|
+
} & {
|
13
|
+
[key: string]: FileStream | null;
|
14
|
+
} & import("jazz-tools").CoMap>;
|
15
|
+
//# sourceMappingURL=index.browser.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../../src/media/index.browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,UAAU,EAA0B,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE3E,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,wBAAsB,WAAW,CAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACrC,OAAO,CAAC,EAAE,kBAAkB;;;;;;;;;gCAQ7B"}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import {
|
2
|
+
createImageFactory,
|
3
|
+
highestResAvailable,
|
4
|
+
loadImage,
|
5
|
+
loadImageBySize
|
6
|
+
} from "./chunk-BBSS3NEY.js";
|
7
|
+
|
8
|
+
// src/media/index.browser.ts
|
9
|
+
import { FileStream } from "jazz-tools";
|
10
|
+
async function createImage(imageBlobOrFile, options) {
|
11
|
+
return createImageFactory({
|
12
|
+
createFileStreamFromSource,
|
13
|
+
getImageSize,
|
14
|
+
getPlaceholderBase64,
|
15
|
+
resize
|
16
|
+
})(imageBlobOrFile, options || {});
|
17
|
+
}
|
18
|
+
async function createFileStreamFromSource(imageBlobOrFile, owner) {
|
19
|
+
if (typeof imageBlobOrFile === "string") {
|
20
|
+
throw new Error(
|
21
|
+
"createFileStreamFromSource(string) is not supported on this platform"
|
22
|
+
);
|
23
|
+
}
|
24
|
+
return FileStream.createFromBlob(imageBlobOrFile, owner);
|
25
|
+
}
|
26
|
+
function getImageFromBlob(blob) {
|
27
|
+
return new Promise((resolve, reject) => {
|
28
|
+
const img = new Image();
|
29
|
+
img.onload = () => {
|
30
|
+
resolve(img);
|
31
|
+
URL.revokeObjectURL(img.src);
|
32
|
+
};
|
33
|
+
img.onerror = () => {
|
34
|
+
reject(new Error("Failed to load image"));
|
35
|
+
URL.revokeObjectURL(img.src);
|
36
|
+
};
|
37
|
+
img.src = URL.createObjectURL(blob);
|
38
|
+
});
|
39
|
+
}
|
40
|
+
async function getImageSize(imageBlobOrFile) {
|
41
|
+
if (typeof imageBlobOrFile === "string") {
|
42
|
+
throw new Error("getImageSize(string) is not supported on browser");
|
43
|
+
}
|
44
|
+
const image = await getImageFromBlob(imageBlobOrFile);
|
45
|
+
return { width: image.width, height: image.height };
|
46
|
+
}
|
47
|
+
async function getPlaceholderBase64(imageBlobOrFile) {
|
48
|
+
if (typeof imageBlobOrFile === "string") {
|
49
|
+
throw new Error("getPlaceholderBase64(string) is not supported on browser");
|
50
|
+
}
|
51
|
+
const image = await getImageFromBlob(imageBlobOrFile);
|
52
|
+
const { width, height } = resizeDimensionsKeepingAspectRatio(
|
53
|
+
image.width,
|
54
|
+
image.height,
|
55
|
+
8
|
56
|
+
);
|
57
|
+
const canvas = document.createElement("canvas");
|
58
|
+
canvas.width = width;
|
59
|
+
canvas.height = height;
|
60
|
+
const ctx = canvas.getContext("2d");
|
61
|
+
if (!ctx) {
|
62
|
+
throw new Error("Failed to get context");
|
63
|
+
}
|
64
|
+
ctx.drawImage(image, 0, 0, width, height);
|
65
|
+
return canvas.toDataURL("image/png");
|
66
|
+
}
|
67
|
+
var resizeDimensionsKeepingAspectRatio = (width, height, maxSize) => {
|
68
|
+
if (width <= maxSize && height <= maxSize) {
|
69
|
+
return { width, height };
|
70
|
+
}
|
71
|
+
const aspectRatio = width / height;
|
72
|
+
if (width >= height) {
|
73
|
+
return { width: maxSize, height: Math.round(maxSize / aspectRatio) };
|
74
|
+
} else {
|
75
|
+
return { width: Math.round(maxSize * aspectRatio), height: maxSize };
|
76
|
+
}
|
77
|
+
};
|
78
|
+
async function resize(imageBlobOrFile, width, height) {
|
79
|
+
if (typeof imageBlobOrFile === "string") {
|
80
|
+
throw new Error("resize(string) is not supported on browser");
|
81
|
+
}
|
82
|
+
const mimeType = imageBlobOrFile.type;
|
83
|
+
const image = await getImageFromBlob(imageBlobOrFile);
|
84
|
+
const canvas = document.createElement("canvas");
|
85
|
+
canvas.width = width;
|
86
|
+
canvas.height = height;
|
87
|
+
const ctx = canvas.getContext("2d");
|
88
|
+
if (!ctx) {
|
89
|
+
throw new Error("Failed to get context");
|
90
|
+
}
|
91
|
+
ctx.drawImage(image, 0, 0, width, height);
|
92
|
+
return new Promise((resolve, reject) => {
|
93
|
+
canvas.toBlob(
|
94
|
+
(blob) => {
|
95
|
+
if (!blob) {
|
96
|
+
reject(new Error("Failed to convert canvas to blob"));
|
97
|
+
return;
|
98
|
+
}
|
99
|
+
resolve(blob);
|
100
|
+
},
|
101
|
+
mimeType,
|
102
|
+
0.8
|
103
|
+
);
|
104
|
+
});
|
105
|
+
}
|
106
|
+
export {
|
107
|
+
createImage,
|
108
|
+
createImageFactory,
|
109
|
+
highestResAvailable,
|
110
|
+
loadImage,
|
111
|
+
loadImageBySize
|
112
|
+
};
|
113
|
+
//# sourceMappingURL=index.browser.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../src/media/index.browser.ts"],"sourcesContent":["import { Account, FileStream, Group, ImageDefinition } from \"jazz-tools\";\nimport { CreateImageOptions, createImageFactory } from \"./create-image.js\";\n\nexport { highestResAvailable, loadImage, loadImageBySize } from \"./utils.js\";\n\nexport { createImageFactory };\n\nexport async function createImage(\n imageBlobOrFile: Blob | File | string,\n options?: CreateImageOptions,\n) {\n return createImageFactory({\n createFileStreamFromSource,\n getImageSize,\n getPlaceholderBase64,\n resize,\n })(imageBlobOrFile, options || {});\n}\n\n// Image Manipulations\nasync function createFileStreamFromSource(\n imageBlobOrFile: Blob | File | string,\n owner?: Account | Group,\n): Promise<FileStream> {\n if (typeof imageBlobOrFile === \"string\") {\n throw new Error(\n \"createFileStreamFromSource(string) is not supported on this platform\",\n );\n }\n\n return FileStream.createFromBlob(imageBlobOrFile, owner);\n}\n\n// using createImageBitmap is ~10x slower than Image object\n// Image object: 640 milliseconds\n// createImageBitmap: 8128 milliseconds\nfunction getImageFromBlob(blob: Blob): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n resolve(img);\n URL.revokeObjectURL(img.src);\n };\n img.onerror = () => {\n reject(new Error(\"Failed to load image\"));\n URL.revokeObjectURL(img.src);\n };\n img.src = URL.createObjectURL(blob);\n });\n}\n\nasync function getImageSize(\n imageBlobOrFile: Blob | File | string,\n): Promise<{ width: number; height: number }> {\n if (typeof imageBlobOrFile === \"string\") {\n throw new Error(\"getImageSize(string) is not supported on browser\");\n }\n\n const image = await getImageFromBlob(imageBlobOrFile);\n\n return { width: image.width, height: image.height };\n}\n\nasync function getPlaceholderBase64(\n imageBlobOrFile: Blob | File | string,\n): Promise<string> {\n if (typeof imageBlobOrFile === \"string\") {\n throw new Error(\"getPlaceholderBase64(string) is not supported on browser\");\n }\n\n const image = await getImageFromBlob(imageBlobOrFile);\n\n const { width, height } = resizeDimensionsKeepingAspectRatio(\n image.width,\n image.height,\n 8,\n );\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get context\");\n }\n\n ctx.drawImage(image, 0, 0, width, height);\n\n return canvas.toDataURL(\"image/png\");\n}\n\nconst resizeDimensionsKeepingAspectRatio = (\n width: number,\n height: number,\n maxSize: number,\n): { width: number; height: number } => {\n if (width <= maxSize && height <= maxSize) {\n return { width, height };\n }\n\n const aspectRatio = width / height;\n\n if (width >= height) {\n return { width: maxSize, height: Math.round(maxSize / aspectRatio) };\n } else {\n return { width: Math.round(maxSize * aspectRatio), height: maxSize };\n }\n};\n\nasync function resize(\n imageBlobOrFile: Blob | File | string,\n width: number,\n height: number,\n): Promise<Blob> {\n if (typeof imageBlobOrFile === \"string\") {\n throw new Error(\"resize(string) is not supported on browser\");\n }\n\n const mimeType = imageBlobOrFile.type;\n\n const image = await getImageFromBlob(imageBlobOrFile);\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n throw new Error(\"Failed to get context\");\n }\n\n ctx.drawImage(image, 0, 0, width, height);\n\n return new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error(\"Failed to convert canvas to blob\"));\n return;\n }\n resolve(blob);\n },\n mimeType,\n 0.8,\n );\n });\n}\n"],"mappings":"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","names":[]}
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import type { ImageDefinition } from "jazz-tools";
|
2
|
+
import { CreateImageOptions, SourceType, createImageFactory } from "./create-image.js";
|
3
|
+
export { highestResAvailable, loadImage, loadImageBySize } from "./utils.js";
|
4
|
+
export { createImageFactory };
|
5
|
+
/**
|
6
|
+
* Creates an ImageDefinition from an image file or blob with built-in UX features.
|
7
|
+
*
|
8
|
+
* This function creates a specialized CoValue for managing images in Jazz applications.
|
9
|
+
* It supports blurry placeholders, built-in resizing, and progressive loading patterns.
|
10
|
+
*
|
11
|
+
* @returns Promise that resolves to an ImageDefinition
|
12
|
+
*
|
13
|
+
* @example
|
14
|
+
* ```ts
|
15
|
+
* import { createImage } from "jazz-tools/media";
|
16
|
+
*
|
17
|
+
* // Create an image from a file input
|
18
|
+
* async function handleFileUpload(event: React.ChangeEvent<HTMLInputElement>) {
|
19
|
+
* const file = event.target.files?.[0];
|
20
|
+
* if (file) {
|
21
|
+
* // Creates ImageDefinition with a blurry placeholder, limited to 1024px
|
22
|
+
* // on the longest side, and multiple resolutions automatically
|
23
|
+
* const image = await createImage(file, {
|
24
|
+
* owner: me._owner,
|
25
|
+
* maxSize: 1024,
|
26
|
+
* placeholder: "blur",
|
27
|
+
* progressive: true,
|
28
|
+
* });
|
29
|
+
*
|
30
|
+
* // Store the image in your application data
|
31
|
+
* me.profile.image = image;
|
32
|
+
* }
|
33
|
+
* }
|
34
|
+
* ```
|
35
|
+
*
|
36
|
+
* @example
|
37
|
+
* ```ts
|
38
|
+
* // React Native example
|
39
|
+
* import { createImage } from "jazz-tools/media";
|
40
|
+
*
|
41
|
+
* async function uploadImageFromCamera(imagePath: string) {
|
42
|
+
* const image = await createImage(imagePath, {
|
43
|
+
* maxSize: 800,
|
44
|
+
* placeholder: "blur",
|
45
|
+
* progressive: false,
|
46
|
+
* });
|
47
|
+
*
|
48
|
+
* return image;
|
49
|
+
* }
|
50
|
+
* ```
|
51
|
+
*/
|
52
|
+
export declare function createImage(imageBlobOrFile: SourceType, options?: CreateImageOptions): Promise<ImageDefinition>;
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/media/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,eAAe,EAAE,UAAU,EAC3B,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,eAAe,CAAC,CAAC"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import type { Account, Group } from "jazz-tools";
|
2
|
+
import { FileStream } from "jazz-tools";
|
3
|
+
import { CreateImageOptions, SourceType, createImageFactory } from "./create-image.js";
|
4
|
+
export { highestResAvailable, loadImage, loadImageBySize } from "./utils.js";
|
5
|
+
export { createImageFactory };
|
6
|
+
export declare function createImage(imageBlobOrFile: Blob | File | string, options?: CreateImageOptions): Promise<{
|
7
|
+
[key: string]: FileStream;
|
8
|
+
} & {
|
9
|
+
original: FileStream | null;
|
10
|
+
originalSize: [number, number];
|
11
|
+
placeholderDataURL: string | undefined;
|
12
|
+
progressive: boolean;
|
13
|
+
} & {
|
14
|
+
[key: string]: FileStream | null;
|
15
|
+
} & import("jazz-tools").CoMap>;
|
16
|
+
export declare function createFileStreamFromSource(filePath: SourceType, owner?: Account | Group): Promise<FileStream>;
|
17
|
+
//# sourceMappingURL=index.native.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"index.native.d.ts","sourceRoot":"","sources":["../../src/media/index.native.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAmB,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B,wBAAsB,WAAW,CAC/B,eAAe,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,EACrC,OAAO,CAAC,EAAE,kBAAkB;;;;;;;;;gCAQ7B;AAmFD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,UAAU,EACpB,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,GACtB,OAAO,CAAC,UAAU,CAAC,CAarB"}
|