@agent-native/core 0.182.1-nightly-20260917183518 → 0.182.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +6 -0
- package/corpus/templates/calendar/CHANGELOG.md +1 -0
- package/corpus/templates/clips/CHANGELOG.md +7 -0
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +2 -2
- package/corpus/templates/clips/changelog/2026-09-17-clips-waits-for-authentication-before-showing-the-recording-.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-17-the-clips-sort-control-keeps-its-keyboard-focus-ring-visible.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/popup.html +1 -1
- package/corpus/templates/clips/chrome-extension/src/popup.ts +30 -21
- package/corpus/templates/content/CHANGELOG.md +7 -0
- package/corpus/templates/content/actions/resolve-preview-document-draft.ts +181 -16
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +28 -15
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +11 -0
- package/corpus/templates/content/app/components/editor/document-save-rebase.ts +13 -10
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +80 -0
- package/corpus/templates/content/changelog/2026-09-16-recovery-choices-stay-attached-to-the-draft-you-reviewed.md +6 -0
- package/corpus/templates/design/CHANGELOG.md +29 -0
- package/corpus/templates/design/actions/import-design-source.ts +42 -2
- package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +26 -15
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +83 -1
- package/corpus/templates/design/app/i18n-data.ts +3 -1
- package/corpus/templates/design/app/lib/design-file-upload.ts +6 -4
- package/corpus/templates/design/app/lib/design-import.ts +2 -0
- package/corpus/templates/design/app/lib/fig-client-import.ts +207 -49
- package/corpus/templates/design/changelog/2026-09-17-large-fig-files-import-directly-in-the-browser-without-the-o.md +6 -0
- package/corpus/templates/design/server/lib/fig-file-decoder.ts +31 -8
- package/corpus/templates/design/server/lib/import-design-files.ts +188 -75
- package/corpus/templates/design/server/plugins/db.ts +16 -7
- package/corpus/templates/design/shared/fig-to-frames.ts +55 -25
- package/corpus/templates/factory/CHANGELOG.md +6 -0
- package/corpus/templates/mail/CHANGELOG.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +12 -0
- package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +1 -2
- package/corpus/templates/slides/app/hooks/use-agent-generating.ts +82 -2
- package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +9 -2
- package/corpus/templates/slides/app/lib/slide-image-replacement.ts +1 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
- package/corpus/templates/slides/server/handlers/assets.ts +134 -8
- package/corpus/templates/slides/server/handlers/uploads.ts +23 -10
- package/corpus/templates/slides/shared/upload-types.ts +1 -0
- package/dist/application-state/store.d.ts +9 -0
- package/dist/application-state/store.js +18 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.js +7 -0
- package/dist/file-upload/actions/upload-image.d.ts +41 -10
- package/dist/file-upload/actions/upload-image.js +399 -19
- package/dist/jobs/scheduler.js +11 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +3 -3
- package/package.json +4 -4
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
* entirely: a Netlify function request is capped at ~6MB while real `.fig`
|
|
7
7
|
* files run to tens of megabytes, which is why the server route has to chunk.
|
|
8
8
|
* Nothing large crosses the network on this path — embedded images go up one
|
|
9
|
-
* at a time through `upload-image`, and each frame's HTML is its own request
|
|
10
|
-
*
|
|
9
|
+
* at a time through `upload-image`, and each frame's HTML is its own request.
|
|
10
|
+
* The individual image budget below keeps base64 action requests under the
|
|
11
|
+
* serverless transport cap while the `.fig` container itself can exceed the
|
|
12
|
+
* old 50 MB server upload ceiling up to a finite browser safety limit.
|
|
11
13
|
*
|
|
12
14
|
* The server route stays for callers that are not a browser (the agent, A2A,
|
|
13
15
|
* the fidelity harness) and as the fallback when decoding here fails.
|
|
@@ -19,13 +21,20 @@ import { decodeFig } from "../../server/lib/fig-file-decoder.js";
|
|
|
19
21
|
import type { DecodedFig } from "../../server/lib/fig-file-decoder.js";
|
|
20
22
|
import { bytesToBase64 } from "../../shared/fig-bytes.js";
|
|
21
23
|
import {
|
|
24
|
+
assertEmbeddedImageBudget,
|
|
22
25
|
convertDecodedFigToEditableHtml,
|
|
26
|
+
MAX_FIG_FRAME_HTML_BYTES,
|
|
23
27
|
inspectDecodedFig,
|
|
24
28
|
shouldWarnForFigImport,
|
|
25
29
|
type FigImportSummary,
|
|
26
30
|
} from "../../shared/fig-to-frames.js";
|
|
27
31
|
import type { ImportResult } from "./design-import";
|
|
28
32
|
|
|
33
|
+
/** Base64 plus action JSON must stay below the serverless request ceiling. */
|
|
34
|
+
export const MAX_CLIENT_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
35
|
+
/** Finite browser allocation ceiling; this is far above the old 50 MB upload cap. */
|
|
36
|
+
export const MAX_CLIENT_FIG_BYTES = 512 * 1024 * 1024;
|
|
37
|
+
|
|
29
38
|
export interface FigClientImportProgress {
|
|
30
39
|
phase: "decoding" | "images" | "saving";
|
|
31
40
|
/** 0-1 within the current phase, when it is countable. */
|
|
@@ -40,6 +49,16 @@ export interface FigClientImportOptions {
|
|
|
40
49
|
onProgress?: (progress: FigClientImportProgress) => void;
|
|
41
50
|
}
|
|
42
51
|
|
|
52
|
+
export class FigClientImportError extends Error {
|
|
53
|
+
constructor(
|
|
54
|
+
message: string,
|
|
55
|
+
readonly remoteMutationStarted: boolean,
|
|
56
|
+
) {
|
|
57
|
+
super(message);
|
|
58
|
+
this.name = "FigClientImportError";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
43
62
|
export interface PreparedFigImport {
|
|
44
63
|
file: File;
|
|
45
64
|
decoded: DecodedFig;
|
|
@@ -56,6 +75,28 @@ function mimeForExt(ext: string): string {
|
|
|
56
75
|
return "application/octet-stream";
|
|
57
76
|
}
|
|
58
77
|
|
|
78
|
+
function browserImportId(): string {
|
|
79
|
+
return (
|
|
80
|
+
globalThis.crypto?.randomUUID?.() ??
|
|
81
|
+
`fig-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function callWithOneRetry<T>(
|
|
86
|
+
action: string,
|
|
87
|
+
input: Record<string, unknown>,
|
|
88
|
+
): Promise<T> {
|
|
89
|
+
try {
|
|
90
|
+
return (await callAction(action, input)) as T;
|
|
91
|
+
} catch (firstError) {
|
|
92
|
+
try {
|
|
93
|
+
return (await callAction(action, input)) as T;
|
|
94
|
+
} catch {
|
|
95
|
+
throw firstError;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
59
100
|
/**
|
|
60
101
|
* Decode, upload the embedded images one request each, then save each frame in
|
|
61
102
|
* its own request. Returns the same shape the server route returns so the
|
|
@@ -64,59 +105,168 @@ function mimeForExt(ext: string): string {
|
|
|
64
105
|
export async function importFigInBrowser(
|
|
65
106
|
options: FigClientImportOptions,
|
|
66
107
|
): Promise<ImportResult> {
|
|
67
|
-
const { designId, file, onProgress } = options;
|
|
108
|
+
const { designId, file, onProgress, selection } = options;
|
|
109
|
+
if (file.size > MAX_CLIENT_FIG_BYTES) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`.fig file is too large for browser import (max ${MAX_CLIENT_FIG_BYTES / 1024 / 1024} MB).`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
68
114
|
let decoded = options.decoded;
|
|
69
115
|
if (!decoded) {
|
|
70
116
|
onProgress?.({ phase: "decoding" });
|
|
71
|
-
|
|
117
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
118
|
+
// The file never crosses the network on this path. Keep the decoder's
|
|
119
|
+
// decompression, node, image, and generated-HTML budgets, but remove the
|
|
120
|
+
// server-only raw upload ceiling.
|
|
121
|
+
decoded = decodeFig(bytes, { maxFileBytes: MAX_CLIENT_FIG_BYTES });
|
|
72
122
|
}
|
|
73
|
-
|
|
123
|
+
assertEmbeddedImageBudget(decoded.images);
|
|
124
|
+
const oversizedImages = decoded.images.filter(
|
|
125
|
+
(image) => image.bytes.byteLength > MAX_CLIENT_IMAGE_BYTES,
|
|
126
|
+
);
|
|
127
|
+
const decodedForBrowserImport =
|
|
128
|
+
oversizedImages.length > 0
|
|
129
|
+
? {
|
|
130
|
+
...decoded,
|
|
131
|
+
images: decoded.images.filter(
|
|
132
|
+
(image) => image.bytes.byteLength <= MAX_CLIENT_IMAGE_BYTES,
|
|
133
|
+
),
|
|
134
|
+
}
|
|
135
|
+
: decoded;
|
|
136
|
+
let remoteMutationStarted = false;
|
|
74
137
|
let uploaded = 0;
|
|
75
|
-
const total =
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
const total = decodedForBrowserImport.images.length;
|
|
139
|
+
const importId = browserImportId();
|
|
140
|
+
let converted;
|
|
141
|
+
try {
|
|
142
|
+
converted = await convertDecodedFigToEditableHtml(decodedForBrowserImport, {
|
|
143
|
+
originalName: file.name,
|
|
144
|
+
// The upload action resolves the owner from the session; this value is only
|
|
145
|
+
// read by the server-side uploader this path replaces.
|
|
146
|
+
ownerEmail: "",
|
|
147
|
+
// The action wraps the document; nothing to do here.
|
|
148
|
+
normalizeHtml: (content: string) => content,
|
|
149
|
+
maxFrameHtmlBytes: MAX_FIG_FRAME_HTML_BYTES,
|
|
150
|
+
selection,
|
|
151
|
+
uploader: async ({ data, filename, mimeType }) => {
|
|
152
|
+
remoteMutationStarted = true;
|
|
153
|
+
const idempotencyKey = `${importId}:${filename}`;
|
|
154
|
+
const uploadInput = {
|
|
155
|
+
data: `data:${mimeType ?? mimeForExt("")};base64,${bytesToBase64(
|
|
156
|
+
data instanceof Uint8Array
|
|
157
|
+
? data
|
|
158
|
+
: new Uint8Array(data as ArrayBuffer),
|
|
159
|
+
)}`,
|
|
160
|
+
filename,
|
|
161
|
+
idempotencyKey,
|
|
162
|
+
};
|
|
163
|
+
const url = await callWithOneRetry<{ url?: string }>(
|
|
164
|
+
"upload-image",
|
|
165
|
+
uploadInput,
|
|
166
|
+
);
|
|
167
|
+
uploaded += 1;
|
|
168
|
+
onProgress?.({
|
|
169
|
+
phase: "images",
|
|
170
|
+
ratio: total ? uploaded / total : 1,
|
|
171
|
+
});
|
|
172
|
+
// A null result means storage is unavailable; the converter rejects the
|
|
173
|
+
// import rather than persisting frames with missing images.
|
|
174
|
+
if (!url?.url) return null;
|
|
175
|
+
return {
|
|
176
|
+
url: url.url,
|
|
177
|
+
cleanup: async () => {
|
|
178
|
+
const result = await callWithOneRetry<{
|
|
179
|
+
alreadyMissing?: boolean;
|
|
180
|
+
committed?: boolean;
|
|
181
|
+
deleted?: boolean;
|
|
182
|
+
}>("upload-image", { idempotencyKey, cleanup: "delete" });
|
|
183
|
+
return (
|
|
184
|
+
result.deleted === true ||
|
|
185
|
+
result.alreadyMissing === true ||
|
|
186
|
+
result.committed === true
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
finalize: async () => {
|
|
190
|
+
const result = await callWithOneRetry<{
|
|
191
|
+
alreadyMissing?: boolean;
|
|
192
|
+
released?: boolean;
|
|
193
|
+
}>("upload-image", { idempotencyKey, cleanup: "release" });
|
|
194
|
+
return result.released === true || result.alreadyMissing === true;
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
} catch (error) {
|
|
200
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
201
|
+
throw new FigClientImportError(message, remoteMutationStarted);
|
|
202
|
+
}
|
|
100
203
|
|
|
101
|
-
const saved: ImportResult = {
|
|
204
|
+
const saved: ImportResult = {
|
|
205
|
+
files: [],
|
|
206
|
+
warnings: converted.warnings,
|
|
207
|
+
skippedEmbeddedImageCount: oversizedImages.length || undefined,
|
|
208
|
+
};
|
|
102
209
|
let index = 0;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
210
|
+
const savedFileIds: string[] = [];
|
|
211
|
+
try {
|
|
212
|
+
for (const [frameIndex, frame] of converted.files.entries()) {
|
|
213
|
+
onProgress?.({
|
|
214
|
+
phase: "saving",
|
|
215
|
+
ratio: converted.files.length ? index / converted.files.length : 1,
|
|
216
|
+
});
|
|
217
|
+
// Do not fall back after this point: the action may have committed even
|
|
218
|
+
// if the browser lost its response.
|
|
219
|
+
remoteMutationStarted = true;
|
|
220
|
+
const result = await callWithOneRetry<ImportResult>(
|
|
221
|
+
"import-design-source",
|
|
222
|
+
{
|
|
223
|
+
designId,
|
|
224
|
+
sourceType: "fig-frame",
|
|
225
|
+
content: frame.content,
|
|
226
|
+
originalName: frame.filename,
|
|
227
|
+
frameTitle: frame.preferredFrame?.title,
|
|
228
|
+
frameWidth: frame.preferredFrame?.width,
|
|
229
|
+
frameHeight: frame.preferredFrame?.height,
|
|
230
|
+
clientImportId: `${importId}:frame:${frameIndex}`,
|
|
231
|
+
clientImportBatchId: importId,
|
|
232
|
+
clientImportFinalFrame: frameIndex === converted.files.length - 1,
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
if (result.error) throw new Error(result.error);
|
|
236
|
+
saved.designId = result.designId ?? saved.designId;
|
|
237
|
+
saved.files = [...(saved.files ?? []), ...(result.files ?? [])];
|
|
238
|
+
savedFileIds.push(...(result.files ?? []).map((file) => file.id));
|
|
239
|
+
index += 1;
|
|
240
|
+
}
|
|
241
|
+
// Receipt release is best-effort after all frames are saved. A partial
|
|
242
|
+
// release must not roll back valid frames; the server-side receipt sweep
|
|
243
|
+
// expires abandoned staged receipts.
|
|
244
|
+
await converted.finalize?.();
|
|
245
|
+
} catch (error) {
|
|
246
|
+
const cleanup = await Promise.allSettled(
|
|
247
|
+
savedFileIds.map((id) =>
|
|
248
|
+
callAction("delete-file", { id, allowLockedLayers: true }),
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
const cleanupFailures = cleanup.filter(
|
|
252
|
+
(result) => result.status === "rejected",
|
|
253
|
+
).length;
|
|
254
|
+
const imageCleanupFailures = (await converted.cleanup?.()) ?? 0;
|
|
255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
256
|
+
const cleanupMessage = [
|
|
257
|
+
cleanupFailures > 0
|
|
258
|
+
? `Cleanup failed for ${cleanupFailures} partially imported screen${cleanupFailures === 1 ? "" : "s"}.`
|
|
259
|
+
: "",
|
|
260
|
+
imageCleanupFailures > 0
|
|
261
|
+
? `Storage cleanup failed for ${imageCleanupFailures} uploaded image${imageCleanupFailures === 1 ? "" : "s"}.`
|
|
262
|
+
: "",
|
|
263
|
+
]
|
|
264
|
+
.filter(Boolean)
|
|
265
|
+
.join(" ");
|
|
266
|
+
throw new FigClientImportError(
|
|
267
|
+
cleanupMessage ? `${message} ${cleanupMessage}` : message,
|
|
268
|
+
remoteMutationStarted,
|
|
269
|
+
);
|
|
120
270
|
}
|
|
121
271
|
return {
|
|
122
272
|
...saved,
|
|
@@ -128,7 +278,15 @@ export async function prepareFigImport(
|
|
|
128
278
|
file: File,
|
|
129
279
|
onProgress?: (progress: FigClientImportProgress) => void,
|
|
130
280
|
): Promise<PreparedFigImport> {
|
|
281
|
+
if (file.size > MAX_CLIENT_FIG_BYTES) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`.fig file is too large for browser import (max ${MAX_CLIENT_FIG_BYTES / 1024 / 1024} MB).`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
131
286
|
onProgress?.({ phase: "decoding" });
|
|
132
|
-
const decoded = decodeFig(new Uint8Array(await file.arrayBuffer())
|
|
287
|
+
const decoded = decodeFig(new Uint8Array(await file.arrayBuffer()), {
|
|
288
|
+
maxFileBytes: MAX_CLIENT_FIG_BYTES,
|
|
289
|
+
});
|
|
290
|
+
assertEmbeddedImageBudget(decoded.images);
|
|
133
291
|
return { file, decoded, summary: inspectDecodedFig(decoded) };
|
|
134
292
|
}
|
|
@@ -75,6 +75,17 @@ export interface DecodedFig {
|
|
|
75
75
|
thumbnail: Uint8Array | null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
export interface DecodeFigOptions {
|
|
79
|
+
/** `null` removes only the raw-file cap for browser-local decoding. */
|
|
80
|
+
maxFileBytes?: number | null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function maxFileBytes(options?: DecodeFigOptions): number | null {
|
|
84
|
+
return options?.maxFileBytes === undefined
|
|
85
|
+
? MAX_FIG_FILE_BYTES
|
|
86
|
+
: options.maxFileBytes;
|
|
87
|
+
}
|
|
88
|
+
|
|
78
89
|
function sha1(buf: Uint8Array): string {
|
|
79
90
|
return sha1Hex(buf);
|
|
80
91
|
}
|
|
@@ -205,9 +216,15 @@ function decompressChunk(buf: Uint8Array): Uint8Array {
|
|
|
205
216
|
return checkDecompressedSize(buf.slice());
|
|
206
217
|
}
|
|
207
218
|
|
|
208
|
-
export function decodeKiwiContainer(
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
export function decodeKiwiContainer(
|
|
220
|
+
file: Uint8Array,
|
|
221
|
+
options?: DecodeFigOptions,
|
|
222
|
+
): DecodedFigKiwi {
|
|
223
|
+
const fileLimit = maxFileBytes(options);
|
|
224
|
+
if (fileLimit !== null && file.length > fileLimit) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`.fig file is too large (max ${Math.round(fileLimit / 1024 / 1024)} MB).`,
|
|
227
|
+
);
|
|
211
228
|
}
|
|
212
229
|
if (
|
|
213
230
|
!bytesEqual(file.subarray(0, 8), FIG_KIWI_MAGIC) &&
|
|
@@ -824,9 +841,15 @@ function assertSafeBinarySchemaShape(schemaBuf: Uint8Array): void {
|
|
|
824
841
|
|
|
825
842
|
// Handles both modern fig-kiwi files and legacy zip-format archives.
|
|
826
843
|
// `document` is null if kiwi decoding failed.
|
|
827
|
-
export function decodeFig(
|
|
828
|
-
|
|
829
|
-
|
|
844
|
+
export function decodeFig(
|
|
845
|
+
file: Uint8Array,
|
|
846
|
+
options?: DecodeFigOptions,
|
|
847
|
+
): DecodedFig {
|
|
848
|
+
const fileLimit = maxFileBytes(options);
|
|
849
|
+
if (fileLimit !== null && file.length > fileLimit) {
|
|
850
|
+
throw new Error(
|
|
851
|
+
`.fig file is too large (max ${Math.round(fileLimit / 1024 / 1024)} MB).`,
|
|
852
|
+
);
|
|
830
853
|
}
|
|
831
854
|
if (isZip(file)) {
|
|
832
855
|
const entries = readZip(file);
|
|
@@ -837,7 +860,7 @@ export function decodeFig(file: Uint8Array): DecodedFig {
|
|
|
837
860
|
let version: number | undefined;
|
|
838
861
|
let extraBlobs: Uint8Array[] = [];
|
|
839
862
|
if (!canvasEntry) throw new Error(".fig zip is missing canvas.fig.");
|
|
840
|
-
const inner = decodeKiwiContainer(canvasEntry.data);
|
|
863
|
+
const inner = decodeKiwiContainer(canvasEntry.data, options);
|
|
841
864
|
version = inner.version;
|
|
842
865
|
extraBlobs = inner.blobs;
|
|
843
866
|
const kiwiResult = decodeKiwiDocument(inner.schema, inner.document);
|
|
@@ -872,7 +895,7 @@ export function decodeFig(file: Uint8Array): DecodedFig {
|
|
|
872
895
|
};
|
|
873
896
|
}
|
|
874
897
|
|
|
875
|
-
const decoded = decodeKiwiContainer(file);
|
|
898
|
+
const decoded = decodeKiwiContainer(file, options);
|
|
876
899
|
const kiwiResult = decodeKiwiDocument(decoded.schema, decoded.document);
|
|
877
900
|
const images = collectImagesFromBlobs(decoded.blobs);
|
|
878
901
|
const thumbnail = findThumbnail(decoded.document, decoded.blobs);
|