@rslsp1/fa-app-tools 1.3.12 → 1.3.14

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.d.mts CHANGED
@@ -479,6 +479,6 @@ declare function hfLoadMetadata(token: string): Promise<any[]>;
479
479
  declare function hfUploadImage(base64: string, id: string, token: string, mimeType?: string): Promise<void>;
480
480
  declare function hfLoadImageAsBase64(id: string, token: string): Promise<string | null>;
481
481
 
482
- declare const LIB_VERSION = "1.3.12";
482
+ declare const LIB_VERSION = "1.3.14";
483
483
 
484
484
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFMetadataEntry, HistoryPanel, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type WorkspaceTags, autoLabel, buildBlendInstruction, buildCompareInstruction, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken, groupGenerationsToLabItems, hfDeleteProject, hfDownloadProject, hfListProjects, hfLoadImageAsBase64, hfLoadMetadata, hfLoadTags, hfSaveMetadata, hfSaveTags, hfUploadImage, hfUploadProjectForm, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, setHFToken, useKeyboardNavigation, useOnClickOutside };
package/dist/index.d.ts CHANGED
@@ -479,6 +479,6 @@ declare function hfLoadMetadata(token: string): Promise<any[]>;
479
479
  declare function hfUploadImage(base64: string, id: string, token: string, mimeType?: string): Promise<void>;
480
480
  declare function hfLoadImageAsBase64(id: string, token: string): Promise<string | null>;
481
481
 
482
- declare const LIB_VERSION = "1.3.12";
482
+ declare const LIB_VERSION = "1.3.14";
483
483
 
484
484
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, type HFMetadataEntry, HistoryPanel, InspectPanel, LIB_VERSION, LabBlend, LabCompare, type LabFrame, LabImagePicker, type LabItem, LabLoop, LabRemix, type LabServices, LabsTab, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedLabImage, type SelectedTag, SetupPanel, type SyncDiff, TagManagerPanel, type TagOption, type WorkspaceTags, autoLabel, buildBlendInstruction, buildCompareInstruction, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildLoopInstruction, buildPromptTabPayload, buildReferenceImageMediaIds, buildRemixInstruction, buildScanInstruction, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, frameToGeneration, getFormattedTimestamp, getHFToken, groupGenerationsToLabItems, hfDeleteProject, hfDownloadProject, hfListProjects, hfLoadImageAsBase64, hfLoadMetadata, hfLoadTags, hfSaveMetadata, hfSaveTags, hfUploadImage, hfUploadProjectForm, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, setHFToken, useKeyboardNavigation, useOnClickOutside };
package/dist/index.js CHANGED
@@ -3474,6 +3474,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3474
3474
  const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react21.useState)(false);
3475
3475
  const [hfMetadata, setHfMetadata] = (0, import_react21.useState)([]);
3476
3476
  const hfTagSaveTimer = (0, import_react21.useRef)(null);
3477
+ const hfMetaSaveQueue = (0, import_react21.useRef)(Promise.resolve());
3477
3478
  const wsInputRef = (0, import_react21.useRef)(null);
3478
3479
  const startApp = (choice) => {
3479
3480
  try {
@@ -3487,6 +3488,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3487
3488
  const [edges, setEdges] = (0, import_react21.useState)([]);
3488
3489
  const [history, setHistory] = (0, import_react21.useState)([]);
3489
3490
  const [galleryItems, setGalleryItems] = (0, import_react21.useState)([]);
3491
+ const galleryItemsRef = (0, import_react21.useRef)([]);
3492
+ (0, import_react21.useEffect)(() => {
3493
+ galleryItemsRef.current = galleryItems;
3494
+ }, [galleryItems]);
3490
3495
  const [activePrompt, setActivePrompt] = (0, import_react21.useState)("");
3491
3496
  const [isSynthesizing, setIsSynthesizing] = (0, import_react21.useState)(false);
3492
3497
  const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react21.useState)(0);
@@ -3747,15 +3752,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3747
3752
  };
3748
3753
  hfUploadImage(base64, genId, hfToken).catch(() => {
3749
3754
  });
3750
- hfLoadMetadata(hfToken).then((existing) => {
3751
- const ids = new Set((existing || []).map((e) => e.id));
3752
- if (!ids.has(genId)) {
3753
- const next = [...existing || [], entry];
3754
- hfSaveMetadata(next, hfToken).catch(() => {
3755
- });
3756
- setHfMetadata(next);
3755
+ const token = hfToken;
3756
+ hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
3757
+ try {
3758
+ const existing = await hfLoadMetadata(token);
3759
+ const ids = new Set((existing || []).map((e) => e.id));
3760
+ if (!ids.has(genId)) {
3761
+ const next = [...existing || [], entry];
3762
+ await hfSaveMetadata(next, token);
3763
+ setHfMetadata(next);
3764
+ }
3765
+ } catch {
3757
3766
  }
3758
- }).catch(() => {
3759
3767
  });
3760
3768
  }
3761
3769
  } catch (err) {
@@ -4048,7 +4056,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4048
4056
  const entries = await hfLoadMetadata(token);
4049
4057
  if (!Array.isArray(entries) || entries.length === 0) return;
4050
4058
  setHfMetadata(entries);
4051
- const skeletons = entries.map((e) => ({
4059
+ const hfIds = new Set(entries.map((e) => e.id));
4060
+ const hfSkeletons = entries.map((e) => ({
4052
4061
  id: e.id,
4053
4062
  nodeId: e.id,
4054
4063
  prompt: e.prompt,
@@ -4058,18 +4067,55 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4058
4067
  timestamp: e.timestamp,
4059
4068
  status: "done"
4060
4069
  }));
4061
- setGalleryItems(skeletons);
4062
- setHistory(skeletons);
4063
- if (skeletons.length > 0) setCurrentResult(skeletons[0]);
4070
+ setGalleryItems((prev) => {
4071
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
4072
+ const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4073
+ return [...localOnly, ...merged];
4074
+ });
4075
+ setHistory((prev) => {
4076
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
4077
+ const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4078
+ return [...localOnly, ...merged];
4079
+ });
4064
4080
  for (const entry of entries) {
4065
4081
  hfLoadImageAsBase64(entry.id, token).then((b64) => {
4066
4082
  if (!b64) return;
4067
4083
  const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4068
- setGalleryItems((prev) => prev.map((g) => g.id === entry.id ? { ...g, base64: prefix + b64 } : g));
4069
- setHistory((prev) => prev.map((g) => g.id === entry.id ? { ...g, base64: prefix + b64 } : g));
4084
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4085
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4070
4086
  }).catch(() => {
4071
4087
  });
4072
4088
  }
4089
+ const localOnlyItems = galleryItemsRef.current.filter((g) => !hfIds.has(g.id) && g.base64 && g.status === "done");
4090
+ if (localOnlyItems.length > 0) {
4091
+ hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
4092
+ try {
4093
+ let currentMeta = await hfLoadMetadata(token);
4094
+ const existingIds = new Set((currentMeta || []).map((e) => e.id));
4095
+ for (const gen of localOnlyItems) {
4096
+ if (existingIds.has(gen.id)) continue;
4097
+ const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
4098
+ const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
4099
+ await hfUploadImage(raw, gen.id, token, mimeType).catch(() => {
4100
+ });
4101
+ const entry = {
4102
+ id: gen.id,
4103
+ prompt: gen.prompt || void 0,
4104
+ seed: gen.seed,
4105
+ model: gen.model,
4106
+ tags: gen.tags || [],
4107
+ timestamp: gen.timestamp,
4108
+ mimeType
4109
+ };
4110
+ currentMeta = [...currentMeta || [], entry];
4111
+ existingIds.add(gen.id);
4112
+ }
4113
+ await hfSaveMetadata(currentMeta, token);
4114
+ setHfMetadata(currentMeta);
4115
+ } catch {
4116
+ }
4117
+ });
4118
+ }
4073
4119
  } finally {
4074
4120
  setIsHfRefreshing(false);
4075
4121
  }
@@ -4908,7 +4954,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4908
4954
 
4909
4955
  // src/index.ts
4910
4956
  init_hfStateService();
4911
- var LIB_VERSION = "1.3.12";
4957
+ var LIB_VERSION = "1.3.14";
4912
4958
  // Annotate the CommonJS export names for ESM import in node:
4913
4959
  0 && (module.exports = {
4914
4960
  AvatarArchitectApp,
package/dist/index.mjs CHANGED
@@ -2917,6 +2917,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2917
2917
  const [isLoadingFromHF, setIsLoadingFromHF] = useState14(false);
2918
2918
  const [hfMetadata, setHfMetadata] = useState14([]);
2919
2919
  const hfTagSaveTimer = useRef6(null);
2920
+ const hfMetaSaveQueue = useRef6(Promise.resolve());
2920
2921
  const wsInputRef = useRef6(null);
2921
2922
  const startApp = (choice) => {
2922
2923
  try {
@@ -2930,6 +2931,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2930
2931
  const [edges, setEdges] = useState14([]);
2931
2932
  const [history, setHistory] = useState14([]);
2932
2933
  const [galleryItems, setGalleryItems] = useState14([]);
2934
+ const galleryItemsRef = useRef6([]);
2935
+ useEffect5(() => {
2936
+ galleryItemsRef.current = galleryItems;
2937
+ }, [galleryItems]);
2933
2938
  const [activePrompt, setActivePrompt] = useState14("");
2934
2939
  const [isSynthesizing, setIsSynthesizing] = useState14(false);
2935
2940
  const [activeGenerationsCount, setActiveGenerationsCount] = useState14(0);
@@ -3190,15 +3195,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3190
3195
  };
3191
3196
  hfUploadImage(base64, genId, hfToken).catch(() => {
3192
3197
  });
3193
- hfLoadMetadata(hfToken).then((existing) => {
3194
- const ids = new Set((existing || []).map((e) => e.id));
3195
- if (!ids.has(genId)) {
3196
- const next = [...existing || [], entry];
3197
- hfSaveMetadata(next, hfToken).catch(() => {
3198
- });
3199
- setHfMetadata(next);
3198
+ const token = hfToken;
3199
+ hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
3200
+ try {
3201
+ const existing = await hfLoadMetadata(token);
3202
+ const ids = new Set((existing || []).map((e) => e.id));
3203
+ if (!ids.has(genId)) {
3204
+ const next = [...existing || [], entry];
3205
+ await hfSaveMetadata(next, token);
3206
+ setHfMetadata(next);
3207
+ }
3208
+ } catch {
3200
3209
  }
3201
- }).catch(() => {
3202
3210
  });
3203
3211
  }
3204
3212
  } catch (err) {
@@ -3491,7 +3499,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3491
3499
  const entries = await hfLoadMetadata(token);
3492
3500
  if (!Array.isArray(entries) || entries.length === 0) return;
3493
3501
  setHfMetadata(entries);
3494
- const skeletons = entries.map((e) => ({
3502
+ const hfIds = new Set(entries.map((e) => e.id));
3503
+ const hfSkeletons = entries.map((e) => ({
3495
3504
  id: e.id,
3496
3505
  nodeId: e.id,
3497
3506
  prompt: e.prompt,
@@ -3501,18 +3510,55 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3501
3510
  timestamp: e.timestamp,
3502
3511
  status: "done"
3503
3512
  }));
3504
- setGalleryItems(skeletons);
3505
- setHistory(skeletons);
3506
- if (skeletons.length > 0) setCurrentResult(skeletons[0]);
3513
+ setGalleryItems((prev) => {
3514
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3515
+ const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3516
+ return [...localOnly, ...merged];
3517
+ });
3518
+ setHistory((prev) => {
3519
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3520
+ const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3521
+ return [...localOnly, ...merged];
3522
+ });
3507
3523
  for (const entry of entries) {
3508
3524
  hfLoadImageAsBase64(entry.id, token).then((b64) => {
3509
3525
  if (!b64) return;
3510
3526
  const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
3511
- setGalleryItems((prev) => prev.map((g) => g.id === entry.id ? { ...g, base64: prefix + b64 } : g));
3512
- setHistory((prev) => prev.map((g) => g.id === entry.id ? { ...g, base64: prefix + b64 } : g));
3527
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3528
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3513
3529
  }).catch(() => {
3514
3530
  });
3515
3531
  }
3532
+ const localOnlyItems = galleryItemsRef.current.filter((g) => !hfIds.has(g.id) && g.base64 && g.status === "done");
3533
+ if (localOnlyItems.length > 0) {
3534
+ hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
3535
+ try {
3536
+ let currentMeta = await hfLoadMetadata(token);
3537
+ const existingIds = new Set((currentMeta || []).map((e) => e.id));
3538
+ for (const gen of localOnlyItems) {
3539
+ if (existingIds.has(gen.id)) continue;
3540
+ const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3541
+ const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3542
+ await hfUploadImage(raw, gen.id, token, mimeType).catch(() => {
3543
+ });
3544
+ const entry = {
3545
+ id: gen.id,
3546
+ prompt: gen.prompt || void 0,
3547
+ seed: gen.seed,
3548
+ model: gen.model,
3549
+ tags: gen.tags || [],
3550
+ timestamp: gen.timestamp,
3551
+ mimeType
3552
+ };
3553
+ currentMeta = [...currentMeta || [], entry];
3554
+ existingIds.add(gen.id);
3555
+ }
3556
+ await hfSaveMetadata(currentMeta, token);
3557
+ setHfMetadata(currentMeta);
3558
+ } catch {
3559
+ }
3560
+ });
3561
+ }
3516
3562
  } finally {
3517
3563
  setIsHfRefreshing(false);
3518
3564
  }
@@ -4350,7 +4396,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4350
4396
  }
4351
4397
 
4352
4398
  // src/index.ts
4353
- var LIB_VERSION = "1.3.12";
4399
+ var LIB_VERSION = "1.3.14";
4354
4400
  export {
4355
4401
  AvatarArchitectApp,
4356
4402
  CollapsibleCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.3.12",
3
+ "version": "1.3.14",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",