@autorender/sdk-core 0.1.19 → 0.1.22

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/dist/index.cjs CHANGED
@@ -183,119 +183,86 @@ var AutorenderApiClient = class {
183
183
  this.apiKey = opts.apiKey;
184
184
  this.baseUrl = (opts.baseUrl || getBaseUrl(opts.environment || "prod")).replace(/\/+$/, "");
185
185
  }
186
- async generateToken(params) {
186
+ uploadFile(params, onProgress, signal) {
187
187
  return new Promise((resolve, reject) => {
188
+ if (signal?.aborted) {
189
+ reject(new DOMException("Upload aborted", "AbortError"));
190
+ return;
191
+ }
188
192
  const xhr = new XMLHttpRequest();
193
+ if (signal) {
194
+ signal.addEventListener("abort", () => {
195
+ xhr.abort();
196
+ reject(new DOMException("Upload aborted", "AbortError"));
197
+ });
198
+ }
199
+ let lastProgress = 0;
200
+ xhr.upload.onprogress = (event) => {
201
+ if (event.lengthComputable && onProgress) {
202
+ const progress = Math.round(event.loaded / event.total * 100);
203
+ lastProgress = progress;
204
+ onProgress(progress);
205
+ } else if (onProgress && event.loaded > 0) {
206
+ const estimatedProgress = Math.min(
207
+ 95,
208
+ Math.round(event.loaded / (event.loaded + 1e6) * 100)
209
+ );
210
+ if (estimatedProgress > lastProgress) {
211
+ lastProgress = estimatedProgress;
212
+ onProgress(estimatedProgress);
213
+ }
214
+ }
215
+ };
189
216
  xhr.onerror = () => {
190
- reject(new Error(`Failed to generate token: ${xhr.statusText}`));
217
+ reject(new Error(`Upload failed: ${xhr.statusText}`));
191
218
  };
192
219
  xhr.onload = () => {
193
220
  try {
194
221
  if (xhr.status < 200 || xhr.status >= 300) {
195
222
  const errorText = xhr.responseText || xhr.statusText;
196
- reject(new Error(`Failed to generate token: ${errorText}`));
197
- return;
198
- }
199
- const response = JSON.parse(xhr.responseText);
200
- if (!response.token) {
201
- reject(new Error("Token not found in response"));
223
+ reject(new Error(`Upload failed: ${errorText}`));
202
224
  return;
203
225
  }
204
- resolve(response.token);
226
+ const newResponse = JSON.parse(xhr.responseText);
227
+ const response = {
228
+ success: true,
229
+ file_no: newResponse.file_no || newResponse.id,
230
+ name: newResponse.name,
231
+ url: newResponse.url,
232
+ file_size: newResponse.size,
233
+ format: newResponse.format,
234
+ width: newResponse.width,
235
+ height: newResponse.height,
236
+ created_at: newResponse.created_at,
237
+ path: newResponse.path,
238
+ workspace_no: newResponse.workspace_id,
239
+ isDuplicate: newResponse.is_duplicate || false
240
+ };
241
+ resolve(response);
205
242
  } catch (error) {
206
- reject(new Error("Failed to parse token response"));
243
+ reject(new Error("Failed to parse response"));
207
244
  }
208
245
  };
209
- let folder = params.folderPath || "";
210
- if (params.relativePath) {
211
- folder = folder ? `${folder}/${params.relativePath}` : params.relativePath;
246
+ const endpoint = `${this.baseUrl}/uploads`;
247
+ const formData = new FormData();
248
+ formData.append("file", params.file);
249
+ formData.append("file_name", params.file.name);
250
+ if (params.folderPath) {
251
+ formData.append("folder", params.folderPath);
212
252
  }
213
- if (folder && !folder.endsWith("/")) {
214
- folder += "/";
253
+ if (params.settings?.tags?.length) {
254
+ formData.append("tags", params.settings.tags.join(","));
255
+ }
256
+ if (params.settings?.pretransformations) {
257
+ formData.append("transform", params.settings.pretransformations);
258
+ }
259
+ if (params.settings?.custom_id) {
260
+ formData.append("custom_id", params.settings.custom_id);
215
261
  }
216
- const tokenRequest = {
217
- ...folder && { folder },
218
- ...params.settings?.tags && { tags: params.settings.tags },
219
- ...params.settings?.pretransformations && { transform: params.settings.pretransformations },
220
- max_file_size: params.file.size,
221
- allow_override: {
222
- folder: true,
223
- tags: true,
224
- transform: true
225
- },
226
- random_prefix: !params.settings?.is_unique_suffix_name
227
- };
228
- const endpoint = `${this.baseUrl}/generate-token`;
229
262
  xhr.open("POST", endpoint);
230
263
  xhr.setRequestHeader("Authorization", `Bearer ${this.apiKey}`);
231
- xhr.setRequestHeader("Content-Type", "application/json");
232
264
  xhr.setRequestHeader("accept", "application/json");
233
- xhr.send(JSON.stringify(tokenRequest));
234
- });
235
- }
236
- uploadFile(params, onProgress, signal) {
237
- return new Promise(async (resolve, reject) => {
238
- if (signal?.aborted) {
239
- reject(new DOMException("Upload aborted", "AbortError"));
240
- return;
241
- }
242
- try {
243
- const token = await this.generateToken(params);
244
- if (signal?.aborted) {
245
- reject(new DOMException("Upload aborted", "AbortError"));
246
- return;
247
- }
248
- const xhr = new XMLHttpRequest();
249
- if (signal) {
250
- signal.addEventListener("abort", () => {
251
- xhr.abort();
252
- reject(new DOMException("Upload aborted", "AbortError"));
253
- });
254
- }
255
- xhr.upload.onprogress = (event) => {
256
- if (event.lengthComputable && onProgress) {
257
- const progress = Math.round(event.loaded / event.total * 100);
258
- onProgress(progress);
259
- }
260
- };
261
- xhr.onerror = () => {
262
- reject(new Error(`Upload failed: ${xhr.statusText}`));
263
- };
264
- xhr.onload = () => {
265
- try {
266
- if (xhr.status < 200 || xhr.status >= 300) {
267
- const errorText = xhr.responseText || xhr.statusText;
268
- reject(new Error(`Upload failed: ${errorText}`));
269
- return;
270
- }
271
- const newResponse = JSON.parse(xhr.responseText);
272
- const response = {
273
- success: true,
274
- file_no: newResponse.file_no || newResponse.id,
275
- name: newResponse.name,
276
- url: newResponse.url,
277
- file_size: newResponse.size,
278
- format: newResponse.format,
279
- width: newResponse.width,
280
- height: newResponse.height,
281
- created_at: newResponse.created_at,
282
- path: newResponse.path,
283
- workspace_no: newResponse.workspace_id,
284
- isDuplicate: newResponse.is_duplicate || false
285
- };
286
- resolve(response);
287
- } catch (error) {
288
- reject(new Error("Failed to parse response"));
289
- }
290
- };
291
- const endpoint = `${this.baseUrl}/uploads/${token}`;
292
- xhr.open("POST", endpoint);
293
- xhr.setRequestHeader("accept", "application/json");
294
- xhr.setRequestHeader("Content-Type", params.file.type || "image/*");
295
- xhr.send(params.file);
296
- } catch (error) {
297
- reject(error);
298
- }
265
+ xhr.send(formData);
299
266
  });
300
267
  }
301
268
  };
@@ -409,7 +376,9 @@ function buildAssetDeliveryUrl(baseUrl, workspace, path, fallback) {
409
376
  }
410
377
 
411
378
  // src/core/uploader-controller.ts
379
+ var import_file_type = require("file-type");
412
380
  var DEFAULT_PARALLEL_UPLOADS = 4;
381
+ var MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
413
382
  var UploaderController = class extends EventTarget {
414
383
  constructor(client, target, options) {
415
384
  super();
@@ -448,19 +417,44 @@ var UploaderController = class extends EventTarget {
448
417
  duplicates: Array.from(this.duplicateConflicts)
449
418
  };
450
419
  }
451
- addFiles(files) {
420
+ async addFiles(files) {
452
421
  const existingKeys = /* @__PURE__ */ new Map();
453
422
  const basePathKey = this.normalizePath(this.options.folderPath) ?? "";
454
423
  const buildKey = (file, folderPath, relativePath) => `${basePathKey}::${folderPath ?? ""}::${relativePath ?? ""}::${file.name}::${file.size}`;
455
424
  this.items.forEach((item) => {
456
425
  existingKeys.set(buildKey(item.file, item.folderPath, item.relativePath), item);
457
426
  });
458
- const newItems = files.map(({ file, relativePath }) => {
427
+ const filesWithMimeType = await Promise.all(
428
+ files.map(async ({ file, relativePath }) => {
429
+ let mimeType = file.type;
430
+ if (!mimeType || mimeType === "application/octet-stream" || mimeType === "") {
431
+ try {
432
+ const detected = await (0, import_file_type.fileTypeFromBlob)(file);
433
+ if (detected?.mime) {
434
+ mimeType = detected.mime;
435
+ Object.defineProperty(file, "type", {
436
+ value: mimeType,
437
+ writable: false,
438
+ configurable: true
439
+ });
440
+ } else {
441
+ mimeType = "application/octet-stream";
442
+ }
443
+ } catch (error) {
444
+ mimeType = "application/octet-stream";
445
+ }
446
+ }
447
+ return { file, relativePath, mimeType };
448
+ })
449
+ );
450
+ const newItems = filesWithMimeType.map(({ file, relativePath, mimeType }) => {
459
451
  const folderPath = getRelativeFolderPath(
460
452
  this.options.folderPath,
461
453
  relativePath
462
454
  ) ?? "";
463
- const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0;
455
+ const previewUrl = mimeType.startsWith("image/") ? URL.createObjectURL(file) : void 0;
456
+ const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2);
457
+ const info = file.size > MAX_FILE_SIZE_BYTES ? `File size (${fileSizeMB}MB) exceeds the maximum limit of 100MB` : void 0;
464
458
  const baseItem = {
465
459
  id: generateId(),
466
460
  file,
@@ -468,7 +462,8 @@ var UploaderController = class extends EventTarget {
468
462
  folderPath,
469
463
  status: "pending",
470
464
  progress: 0,
471
- previewUrl
465
+ previewUrl,
466
+ info
472
467
  };
473
468
  const key = buildKey(file, folderPath, relativePath);
474
469
  if (existingKeys.has(key)) {
@@ -651,6 +646,13 @@ var UploaderController = class extends EventTarget {
651
646
  await this.runWithConcurrency(tasks, parallel);
652
647
  }
653
648
  async uploadSingleFile(item, uploadSettings) {
649
+ if (item.file.size > MAX_FILE_SIZE_BYTES) {
650
+ const fileSizeMB = (item.file.size / (1024 * 1024)).toFixed(2);
651
+ item.info = `File size (${fileSizeMB}MB) exceeds the maximum limit of 100MB`;
652
+ this.dispatch("fileprogress", { item });
653
+ this.dispatch("statechange");
654
+ return;
655
+ }
654
656
  item.status = "uploading";
655
657
  item.progress = 0;
656
658
  this.dispatch("statechange");
@@ -663,7 +665,8 @@ var UploaderController = class extends EventTarget {
663
665
  settings: uploadSettings ? {
664
666
  pretransformations: uploadSettings.pretransformations,
665
667
  tags: uploadSettings.tags,
666
- is_unique_suffix_name: uploadSettings.is_unique_suffix_name ?? false
668
+ is_unique_suffix_name: uploadSettings.is_unique_suffix_name ?? false,
669
+ custom_id: uploadSettings.custom_id
667
670
  } : void 0
668
671
  },
669
672
  (progress) => {
@@ -1163,7 +1166,8 @@ var widgetStyles = (
1163
1166
  gap: 0.45rem;
1164
1167
  position: relative;
1165
1168
  box-sizing: border-box;
1166
- height: 50px;
1169
+ min-height: 50px;
1170
+ overflow: hidden;
1167
1171
  }
1168
1172
 
1169
1173
  .ar-file-item.is-grid {
@@ -1180,6 +1184,8 @@ var widgetStyles = (
1180
1184
  display: flex;
1181
1185
  align-items: center;
1182
1186
  gap: 0.6rem;
1187
+ min-width: 0;
1188
+ flex: 1;
1183
1189
  }
1184
1190
 
1185
1191
  .ar-file-header.is-grid {
@@ -1202,6 +1208,17 @@ var widgetStyles = (
1202
1208
  position: relative;
1203
1209
  }
1204
1210
 
1211
+ .ar-file-thumb-document {
1212
+ background: rgba(148, 163, 184, 0.15);
1213
+ color: var(--ar-color-muted-foreground);
1214
+ }
1215
+
1216
+ .ar-file-thumb-document svg {
1217
+ width: 20px;
1218
+ height: 20px;
1219
+ color: currentColor;
1220
+ }
1221
+
1205
1222
  .ar-file-thumb.is-grid {
1206
1223
  width: 100%;
1207
1224
  height: 140px;
@@ -1209,6 +1226,11 @@ var widgetStyles = (
1209
1226
  font-size: 1rem;
1210
1227
  }
1211
1228
 
1229
+ .ar-file-thumb.is-grid.ar-file-thumb-document svg {
1230
+ width: 48px;
1231
+ height: 48px;
1232
+ }
1233
+
1212
1234
  .ar-file-item.is-completed .ar-file-thumb {
1213
1235
  color: #fff;
1214
1236
  }
@@ -1243,13 +1265,39 @@ var widgetStyles = (
1243
1265
  font-size: 0.8rem;
1244
1266
  margin: 0;
1245
1267
  color: var(--ar-color-text);
1268
+ overflow: hidden;
1269
+ text-overflow: ellipsis;
1270
+ white-space: nowrap;
1271
+ flex: 1;
1272
+ min-width: 0;
1246
1273
  }
1247
1274
 
1248
1275
  .ar-file-meta {
1249
- display: flex;
1250
- gap: 0.75rem;
1276
+ display: block;
1277
+ margin-top: 0.25rem;
1251
1278
  font-size: 0.8rem;
1252
1279
  color: var(--ar-color-muted-foreground);
1280
+ width: 100%;
1281
+ flex-shrink: 0;
1282
+ }
1283
+
1284
+ .ar-file-info {
1285
+ display: block;
1286
+ font-size: 0.75rem;
1287
+ color: var(--ar-color-muted-foreground);
1288
+ font-style: italic;
1289
+ padding: 0.25rem 0.5rem;
1290
+ background: rgba(148, 163, 184, 0.15);
1291
+ border-radius: calc(var(--ar-radius) * 0.5);
1292
+ width: 100%;
1293
+ box-sizing: border-box;
1294
+ word-wrap: break-word;
1295
+ overflow-wrap: break-word;
1296
+ }
1297
+
1298
+ .ar-file-item.is-grid .ar-file-info {
1299
+ font-size: 0.7rem;
1300
+ padding: 0.2rem 0.4rem;
1253
1301
  }
1254
1302
 
1255
1303
  .ar-file-item.is-grid .ar-file-meta {
@@ -2075,7 +2123,6 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2075
2123
  const themeInput = options.theme ?? previousOptions.theme ?? DEFAULT_THEME;
2076
2124
  const resolvedTheme = {
2077
2125
  appearance: themeInput.appearance ?? DEFAULT_THEME.appearance,
2078
- accentColor: themeInput.accentColor ?? DEFAULT_THEME.accentColor,
2079
2126
  borderRadius: themeInput.borderRadius ?? DEFAULT_THEME.borderRadius,
2080
2127
  dropzoneBackground: themeInput.dropzoneBackground ?? DEFAULT_THEME.dropzoneBackground,
2081
2128
  dropzoneBorder: themeInput.dropzoneBorder ?? DEFAULT_THEME.dropzoneBorder,
@@ -2451,19 +2498,19 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2451
2498
  this.dropzone?.classList.remove("is-dragging");
2452
2499
  const files = await this.extractFilesFromEvent(event);
2453
2500
  if (files.length) {
2454
- this.controller?.addFiles(files);
2501
+ await this.controller?.addFiles(files);
2455
2502
  }
2456
2503
  if (this.fileInput) {
2457
2504
  this.fileInput.value = "";
2458
2505
  }
2459
2506
  });
2460
- this.fileInput?.addEventListener("change", () => {
2507
+ this.fileInput?.addEventListener("change", async () => {
2461
2508
  const files = this.fileInput?.files ? Array.from(this.fileInput.files) : [];
2462
2509
  const mapped = files.map((file) => ({
2463
2510
  file,
2464
2511
  relativePath: file.webkitRelativePath
2465
2512
  }));
2466
- this.controller?.addFiles(mapped);
2513
+ await this.controller?.addFiles(mapped);
2467
2514
  if (this.fileInput) this.fileInput.value = "";
2468
2515
  });
2469
2516
  this.uploadButton?.addEventListener("click", () => {
@@ -2657,13 +2704,35 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2657
2704
  const thumb = document.createElement("div");
2658
2705
  thumb.className = "ar-file-thumb";
2659
2706
  thumb.classList.toggle("is-grid", isGridLayout);
2660
- if (item.previewUrl) {
2707
+ const isImage = item.file.type.startsWith("image/");
2708
+ if (item.previewUrl && isImage) {
2661
2709
  const img = document.createElement("img");
2662
2710
  img.src = item.previewUrl;
2663
2711
  img.alt = item.file.name;
2664
2712
  thumb.appendChild(img);
2665
2713
  } else {
2666
- thumb.textContent = item.file.name.charAt(0).toUpperCase();
2714
+ thumb.classList.add("ar-file-thumb-document");
2715
+ const documentIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
2716
+ documentIcon.setAttribute("viewBox", "0 0 24 24");
2717
+ documentIcon.setAttribute("width", "24");
2718
+ documentIcon.setAttribute("height", "24");
2719
+ documentIcon.setAttribute("fill", "none");
2720
+ documentIcon.setAttribute("aria-hidden", "true");
2721
+ const path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
2722
+ path1.setAttribute("d", "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6Z");
2723
+ path1.setAttribute("stroke", "currentColor");
2724
+ path1.setAttribute("stroke-width", "1.5");
2725
+ path1.setAttribute("stroke-linecap", "round");
2726
+ path1.setAttribute("stroke-linejoin", "round");
2727
+ const path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
2728
+ path2.setAttribute("d", "M14 2v6h6");
2729
+ path2.setAttribute("stroke", "currentColor");
2730
+ path2.setAttribute("stroke-width", "1.5");
2731
+ path2.setAttribute("stroke-linecap", "round");
2732
+ path2.setAttribute("stroke-linejoin", "round");
2733
+ documentIcon.appendChild(path1);
2734
+ documentIcon.appendChild(path2);
2735
+ thumb.appendChild(documentIcon);
2667
2736
  }
2668
2737
  if (item.status === "completed") {
2669
2738
  const statusOverlay = document.createElement("div");
@@ -2710,6 +2779,15 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2710
2779
  header.appendChild(name);
2711
2780
  header.appendChild(removeButton);
2712
2781
  li.appendChild(header);
2782
+ if (item.info) {
2783
+ const metaRow = document.createElement("div");
2784
+ metaRow.className = "ar-file-meta";
2785
+ const infoText = document.createElement("span");
2786
+ infoText.className = "ar-file-info";
2787
+ infoText.textContent = item.info;
2788
+ metaRow.appendChild(infoText);
2789
+ li.appendChild(metaRow);
2790
+ }
2713
2791
  const progressBorder = document.createElement("div");
2714
2792
  progressBorder.className = "ar-progress-border";
2715
2793
  const progressFill = document.createElement("div");
@@ -2720,8 +2798,8 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2720
2798
  const rawProgress = Math.max(0, Math.min(100, item.progress ?? 0));
2721
2799
  const calculatedTrain = !isCompleteStatus && rawProgress < 20;
2722
2800
  const shouldAnimateTrain = (isUploadingStatus || isUploading && isPendingStatus) && calculatedTrain;
2723
- const isActive = shouldAnimateTrain || rawProgress > 0 && !isCompleteStatus;
2724
- const stateLabel = isCompleteStatus ? "complete" : shouldAnimateTrain ? "train" : rawProgress >= 20 ? "progress" : "idle";
2801
+ const isActive = isUploadingStatus || shouldAnimateTrain || rawProgress > 0 && !isCompleteStatus;
2802
+ const stateLabel = isCompleteStatus ? "complete" : shouldAnimateTrain ? "train" : rawProgress >= 20 ? "progress" : isUploadingStatus ? "progress" : "idle";
2725
2803
  progressBorder.dataset.state = stateLabel;
2726
2804
  progressBorder.dataset.status = item.status;
2727
2805
  progressBorder.dataset.progress = rawProgress.toFixed(0);
@@ -2735,17 +2813,14 @@ var AutorenderUploaderElement = class extends HTMLElementShim {
2735
2813
  progressFill.style.width = "100%";
2736
2814
  } else if (shouldAnimateTrain) {
2737
2815
  progressFill.style.width = "100%";
2816
+ } else if (isUploadingStatus && rawProgress === 0) {
2817
+ progressFill.style.width = "3%";
2738
2818
  } else {
2739
2819
  const computedWidth = Math.max(rawProgress, 3);
2740
2820
  progressFill.style.width = `${Math.min(computedWidth, 100)}%`;
2741
2821
  }
2742
2822
  progressBorder.appendChild(progressFill);
2743
- const metaRow = document.createElement("div");
2744
- metaRow.className = "ar-file-meta";
2745
2823
  li.appendChild(progressBorder);
2746
- if (metaRow.childNodes.length > 0) {
2747
- li.appendChild(metaRow);
2748
- }
2749
2824
  this.fileList?.appendChild(li);
2750
2825
  });
2751
2826
  }
@@ -3054,6 +3129,8 @@ function buildTransformString(transform, defaults, enableDPR = true, connectionQ
3054
3129
  sortKey = "f_" + param;
3055
3130
  } else if (param.startsWith("fl_")) {
3056
3131
  sortKey = "fl_" + param;
3132
+ } else if (param.startsWith("fo_")) {
3133
+ sortKey = "fo_" + param;
3057
3134
  } else if (param.startsWith("flip_")) {
3058
3135
  sortKey = "flip_" + param;
3059
3136
  } else if (param.startsWith("h_")) {
@@ -3133,6 +3210,7 @@ function buildTransformString(transform, defaults, enableDPR = true, connectionQ
3133
3210
  if (transform.ar) addPart("ar_", transform.ar);
3134
3211
  if (transform.p) addPart("p_", transform.p);
3135
3212
  if (transform.ps) addPart("ps_", transform.ps);
3213
+ if (transform.focus) addPart("fo_", transform.focus);
3136
3214
  if (transform.r !== void 0) addPart("r_", transform.r);
3137
3215
  if (transform.z !== void 0) addPart("z_", transform.z);
3138
3216
  if (transform.flip) addPart("flip_", transform.flip);
@@ -3233,6 +3311,7 @@ function buildTransformString(transform, defaults, enableDPR = true, connectionQ
3233
3311
  "ar",
3234
3312
  "p",
3235
3313
  "ps",
3314
+ "focus",
3236
3315
  "r",
3237
3316
  "z",
3238
3317
  "flip",