@weirdfingers/boards 0.6.2 → 0.7.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/dist/index.mjs CHANGED
@@ -815,6 +815,11 @@ var RETRY_GENERATION = gql`
815
815
  }
816
816
  }
817
817
  `;
818
+ var DELETE_GENERATION = gql`
819
+ mutation DeleteGeneration($id: UUID!) {
820
+ deleteGeneration(id: $id)
821
+ }
822
+ `;
818
823
  var UPLOAD_ARTIFACT_FROM_URL = gql`
819
824
  ${GENERATION_FRAGMENT}
820
825
  mutation UploadArtifactFromUrl($input: UploadArtifactInput!) {
@@ -1103,6 +1108,7 @@ function useGeneration() {
1103
1108
  const [, createGenerationMutation] = useMutation3(CREATE_GENERATION);
1104
1109
  const [, cancelGenerationMutation] = useMutation3(CANCEL_GENERATION);
1105
1110
  const [, retryGenerationMutation] = useMutation3(RETRY_GENERATION);
1111
+ const [, deleteGenerationMutation] = useMutation3(DELETE_GENERATION);
1106
1112
  useEffect2(() => {
1107
1113
  return () => {
1108
1114
  abortControllers.current.forEach((controller) => {
@@ -1312,6 +1318,36 @@ function useGeneration() {
1312
1318
  },
1313
1319
  [retryGenerationMutation, connectToSSE]
1314
1320
  );
1321
+ const deleteGeneration = useCallback5(
1322
+ async (jobId) => {
1323
+ try {
1324
+ const result2 = await deleteGenerationMutation({ id: jobId });
1325
+ if (result2.error) {
1326
+ throw new Error(result2.error.message);
1327
+ }
1328
+ if (!result2.data?.deleteGeneration) {
1329
+ throw new Error("Failed to delete generation");
1330
+ }
1331
+ const controller = abortControllers.current.get(jobId);
1332
+ if (controller) {
1333
+ controller.abort();
1334
+ abortControllers.current.delete(jobId);
1335
+ }
1336
+ if (progress?.jobId === jobId) {
1337
+ setProgress(null);
1338
+ setResult(null);
1339
+ setIsGenerating(false);
1340
+ }
1341
+ setHistory((prev) => prev.filter((item) => item.jobId !== jobId));
1342
+ } catch (err) {
1343
+ setError(
1344
+ err instanceof Error ? err : new Error("Failed to delete generation")
1345
+ );
1346
+ throw err;
1347
+ }
1348
+ },
1349
+ [deleteGenerationMutation, progress]
1350
+ );
1315
1351
  const clearHistory = useCallback5(() => {
1316
1352
  setHistory([]);
1317
1353
  }, []);
@@ -1323,6 +1359,7 @@ function useGeneration() {
1323
1359
  submit,
1324
1360
  cancel,
1325
1361
  retry,
1362
+ deleteGeneration,
1326
1363
  history,
1327
1364
  clearHistory
1328
1365
  };
@@ -1345,21 +1382,44 @@ function useGenerators(options = {}) {
1345
1382
  };
1346
1383
  }
1347
1384
 
1348
- // src/hooks/useUpload.ts
1349
- import { useCallback as useCallback6, useState as useState5 } from "react";
1385
+ // src/hooks/useMultiUpload.ts
1386
+ import { useCallback as useCallback6, useState as useState5, useRef as useRef2 } from "react";
1350
1387
  import { useMutation as useMutation4 } from "urql";
1351
- function useUpload() {
1352
- const [isUploading, setIsUploading] = useState5(false);
1353
- const [progress, setProgress] = useState5(0);
1354
- const [error, setError] = useState5(null);
1388
+ var uploadIdCounter = 0;
1389
+ function generateUploadId() {
1390
+ return `upload-${Date.now()}-${++uploadIdCounter}`;
1391
+ }
1392
+ function getFileName(source) {
1393
+ if (typeof source === "string") {
1394
+ try {
1395
+ const url = new URL(source);
1396
+ const pathParts = url.pathname.split("/");
1397
+ return pathParts[pathParts.length - 1] || source;
1398
+ } catch {
1399
+ return source;
1400
+ }
1401
+ }
1402
+ return source.name;
1403
+ }
1404
+ function useMultiUpload() {
1405
+ const [uploads, setUploads] = useState5([]);
1406
+ const abortControllersRef = useRef2(/* @__PURE__ */ new Map());
1355
1407
  const { apiUrl } = useApiConfig();
1356
1408
  const auth = useAuth();
1357
1409
  const [, uploadFromUrlMutation] = useMutation4(UPLOAD_ARTIFACT_FROM_URL);
1358
- const upload = useCallback6(
1359
- async (request) => {
1360
- setError(null);
1361
- setProgress(0);
1362
- setIsUploading(true);
1410
+ const updateUpload = useCallback6(
1411
+ (uploadId, updates) => {
1412
+ setUploads(
1413
+ (prev) => prev.map(
1414
+ (item) => item.id === uploadId ? { ...item, ...updates } : item
1415
+ )
1416
+ );
1417
+ },
1418
+ []
1419
+ );
1420
+ const uploadSingle = useCallback6(
1421
+ async (uploadId, request) => {
1422
+ updateUpload(uploadId, { status: "uploading", progress: 0 });
1363
1423
  try {
1364
1424
  if (typeof request.source === "string") {
1365
1425
  const result2 = await uploadFromUrlMutation({
@@ -1377,9 +1437,7 @@ function useUpload() {
1377
1437
  if (!result2.data?.uploadArtifact) {
1378
1438
  throw new Error("Upload failed");
1379
1439
  }
1380
- setProgress(100);
1381
- setIsUploading(false);
1382
- return {
1440
+ const uploadResult = {
1383
1441
  id: result2.data.uploadArtifact.id,
1384
1442
  storageUrl: result2.data.uploadArtifact.storageUrl,
1385
1443
  thumbnailUrl: result2.data.uploadArtifact.thumbnailUrl,
@@ -1387,6 +1445,12 @@ function useUpload() {
1387
1445
  artifactType: result2.data.uploadArtifact.artifactType,
1388
1446
  generatorName: result2.data.uploadArtifact.generatorName
1389
1447
  };
1448
+ updateUpload(uploadId, {
1449
+ status: "completed",
1450
+ progress: 100,
1451
+ result: uploadResult
1452
+ });
1453
+ return uploadResult;
1390
1454
  }
1391
1455
  const formData = new FormData();
1392
1456
  formData.append("board_id", request.boardId);
@@ -1403,68 +1467,128 @@ function useUpload() {
1403
1467
  if (token) {
1404
1468
  headers.Authorization = `Bearer ${token}`;
1405
1469
  }
1406
- const result = await new Promise((resolve, reject) => {
1407
- const xhr = new XMLHttpRequest();
1408
- xhr.upload.addEventListener("progress", (e) => {
1409
- if (e.lengthComputable) {
1410
- const percentComplete = e.loaded / e.total * 100;
1411
- setProgress(percentComplete);
1412
- }
1413
- });
1414
- xhr.addEventListener("load", () => {
1415
- if (xhr.status === 200) {
1416
- try {
1417
- const data = JSON.parse(xhr.responseText);
1418
- resolve({
1419
- id: data.id,
1420
- storageUrl: data.storageUrl,
1421
- thumbnailUrl: data.thumbnailUrl,
1422
- status: "completed",
1423
- artifactType: data.artifactType,
1424
- generatorName: data.generatorName
1425
- });
1426
- } catch (err) {
1427
- reject(new Error("Failed to parse response"));
1470
+ const result = await new Promise(
1471
+ (resolve, reject) => {
1472
+ const xhr = new XMLHttpRequest();
1473
+ abortControllersRef.current.set(uploadId, () => xhr.abort());
1474
+ const cleanup = () => {
1475
+ abortControllersRef.current.delete(uploadId);
1476
+ };
1477
+ xhr.upload.addEventListener("progress", (e) => {
1478
+ if (e.lengthComputable) {
1479
+ const percentComplete = e.loaded / e.total * 100;
1480
+ updateUpload(uploadId, { progress: percentComplete });
1428
1481
  }
1429
- } else {
1430
- try {
1431
- const errorData = JSON.parse(xhr.responseText);
1432
- reject(new Error(errorData.detail || `Upload failed: ${xhr.statusText}`));
1433
- } catch {
1434
- reject(new Error(`Upload failed: ${xhr.statusText}`));
1482
+ });
1483
+ xhr.addEventListener("load", () => {
1484
+ cleanup();
1485
+ if (xhr.status === 200) {
1486
+ try {
1487
+ const data = JSON.parse(xhr.responseText);
1488
+ resolve({
1489
+ id: data.id,
1490
+ storageUrl: data.storageUrl,
1491
+ thumbnailUrl: data.thumbnailUrl,
1492
+ status: "completed",
1493
+ artifactType: data.artifactType,
1494
+ generatorName: data.generatorName
1495
+ });
1496
+ } catch (err) {
1497
+ reject(new Error("Failed to parse response"));
1498
+ }
1499
+ } else {
1500
+ try {
1501
+ const errorData = JSON.parse(xhr.responseText);
1502
+ reject(
1503
+ new Error(
1504
+ errorData.detail || `Upload failed: ${xhr.statusText}`
1505
+ )
1506
+ );
1507
+ } catch {
1508
+ reject(new Error(`Upload failed: ${xhr.statusText}`));
1509
+ }
1435
1510
  }
1436
- }
1437
- });
1438
- xhr.addEventListener("error", () => {
1439
- reject(new Error("Upload failed: Network error"));
1440
- });
1441
- xhr.addEventListener("abort", () => {
1442
- reject(new Error("Upload cancelled"));
1443
- });
1444
- xhr.open("POST", `${apiUrl}/api/uploads/artifact`);
1445
- Object.entries(headers).forEach(([key, value]) => {
1446
- xhr.setRequestHeader(key, value);
1447
- });
1448
- xhr.send(formData);
1511
+ });
1512
+ xhr.addEventListener("error", () => {
1513
+ cleanup();
1514
+ reject(new Error("Upload failed: Network error"));
1515
+ });
1516
+ xhr.addEventListener("abort", () => {
1517
+ cleanup();
1518
+ reject(new Error("Upload cancelled"));
1519
+ });
1520
+ xhr.addEventListener("loadend", () => {
1521
+ cleanup();
1522
+ });
1523
+ xhr.open("POST", `${apiUrl}/api/uploads/artifact`);
1524
+ Object.entries(headers).forEach(([key, value]) => {
1525
+ xhr.setRequestHeader(key, value);
1526
+ });
1527
+ xhr.send(formData);
1528
+ }
1529
+ );
1530
+ updateUpload(uploadId, {
1531
+ status: "completed",
1532
+ progress: 100,
1533
+ result
1449
1534
  });
1450
- setProgress(100);
1451
- setIsUploading(false);
1452
1535
  return result;
1453
1536
  } catch (err) {
1454
1537
  const uploadError = err instanceof Error ? err : new Error("Upload failed");
1455
- setError(uploadError);
1456
- setIsUploading(false);
1457
- setProgress(0);
1538
+ updateUpload(uploadId, {
1539
+ status: "failed",
1540
+ error: uploadError
1541
+ });
1458
1542
  throw uploadError;
1459
1543
  }
1460
1544
  },
1461
- [uploadFromUrlMutation, apiUrl, auth]
1545
+ [uploadFromUrlMutation, apiUrl, auth, updateUpload]
1462
1546
  );
1547
+ const uploadMultiple = useCallback6(
1548
+ async (requests) => {
1549
+ const newUploads = requests.map((request) => ({
1550
+ id: generateUploadId(),
1551
+ file: request.source,
1552
+ fileName: getFileName(request.source),
1553
+ status: "pending",
1554
+ progress: 0
1555
+ }));
1556
+ setUploads((prev) => [...prev, ...newUploads]);
1557
+ const results = await Promise.allSettled(
1558
+ requests.map(
1559
+ (request, index) => uploadSingle(newUploads[index].id, request)
1560
+ )
1561
+ );
1562
+ const successfulResults = [];
1563
+ results.forEach((result) => {
1564
+ if (result.status === "fulfilled") {
1565
+ successfulResults.push(result.value);
1566
+ }
1567
+ });
1568
+ return successfulResults;
1569
+ },
1570
+ [uploadSingle]
1571
+ );
1572
+ const clearUploads = useCallback6(() => {
1573
+ abortControllersRef.current.forEach((abort) => abort());
1574
+ abortControllersRef.current.clear();
1575
+ setUploads([]);
1576
+ }, []);
1577
+ const cancelUpload = useCallback6((uploadId) => {
1578
+ const abort = abortControllersRef.current.get(uploadId);
1579
+ if (abort) {
1580
+ abort();
1581
+ }
1582
+ }, []);
1583
+ const isUploading = uploads.some((u) => u.status === "uploading");
1584
+ const overallProgress = uploads.length > 0 ? uploads.reduce((sum, u) => sum + u.progress, 0) / uploads.length : 0;
1463
1585
  return {
1464
- upload,
1586
+ uploadMultiple,
1587
+ uploads,
1465
1588
  isUploading,
1466
- progress,
1467
- error
1589
+ overallProgress,
1590
+ clearUploads,
1591
+ cancelUpload
1468
1592
  };
1469
1593
  }
1470
1594
 
@@ -1581,6 +1705,7 @@ export {
1581
1705
  CREATE_BOARD,
1582
1706
  CREATE_GENERATION,
1583
1707
  DELETE_BOARD,
1708
+ DELETE_GENERATION,
1584
1709
  DESCENDANT_NODE_FRAGMENT,
1585
1710
  GENERATION_FRAGMENT,
1586
1711
  GET_ANCESTRY,
@@ -1620,6 +1745,6 @@ export {
1620
1745
  useGenerators,
1621
1746
  useInputArtifacts,
1622
1747
  useLineage,
1623
- useUpload
1748
+ useMultiUpload
1624
1749
  };
1625
1750
  //# sourceMappingURL=index.mjs.map