@rslsp1/fa-app-tools 1.3.4 → 1.3.6

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.
@@ -131,6 +131,88 @@ async function hfDeleteProject(path, token) {
131
131
  });
132
132
  if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
133
133
  }
134
+ async function hfSaveTags(workspaceTags, token) {
135
+ const content = new Blob([JSON.stringify(workspaceTags, null, 2)], { type: "application/json" });
136
+ const { uploadFile } = await import("@huggingface/hub");
137
+ await uploadFile({
138
+ repo: { type: "dataset", name: HF_REPO },
139
+ credentials: { accessToken: token },
140
+ file: { path: "tags.json", content },
141
+ branch: "main",
142
+ commitTitle: "Auto-sync tags"
143
+ });
144
+ }
145
+ async function hfLoadTags(token) {
146
+ try {
147
+ const res = await fetch(
148
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
149
+ { headers: { Authorization: `Bearer ${token}` } }
150
+ );
151
+ if (!res.ok) return null;
152
+ return res.json();
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+ async function hfSaveMetadata(entries, token) {
158
+ const content = new Blob([JSON.stringify(entries, null, 2)], { type: "application/json" });
159
+ const { uploadFile } = await import("@huggingface/hub");
160
+ await uploadFile({
161
+ repo: { type: "dataset", name: HF_REPO },
162
+ credentials: { accessToken: token },
163
+ file: { path: "metadata.json", content },
164
+ branch: "main",
165
+ commitTitle: "Auto-sync metadata"
166
+ });
167
+ }
168
+ async function hfLoadMetadata(token) {
169
+ try {
170
+ const res = await fetch(
171
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
172
+ { headers: { Authorization: `Bearer ${token}` } }
173
+ );
174
+ if (!res.ok) return [];
175
+ return res.json();
176
+ } catch {
177
+ return [];
178
+ }
179
+ }
180
+ async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
181
+ const ext = mimeType === "image/png" ? "png" : "jpg";
182
+ const binary = atob(base64);
183
+ const bytes = new Uint8Array(binary.length);
184
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
185
+ const content = new Blob([bytes], { type: mimeType });
186
+ const { uploadFile } = await import("@huggingface/hub");
187
+ await uploadFile({
188
+ repo: { type: "dataset", name: HF_REPO },
189
+ credentials: { accessToken: token },
190
+ file: { path: `images/${id}.${ext}`, content },
191
+ branch: "main",
192
+ commitTitle: `Add image ${id}`
193
+ });
194
+ }
195
+ async function hfLoadImageAsBase64(id, token) {
196
+ for (const ext of ["jpg", "png"]) {
197
+ try {
198
+ const res = await fetch(
199
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/images/${id}.${ext}?download=true`,
200
+ { headers: { Authorization: `Bearer ${token}` } }
201
+ );
202
+ if (!res.ok) continue;
203
+ const blob = await res.blob();
204
+ return new Promise((resolve, reject) => {
205
+ const reader = new FileReader();
206
+ reader.onload = () => resolve(reader.result.split(",")[1]);
207
+ reader.onerror = reject;
208
+ reader.readAsDataURL(blob);
209
+ });
210
+ } catch {
211
+ continue;
212
+ }
213
+ }
214
+ return null;
215
+ }
134
216
 
135
217
  export {
136
218
  HF_TOKEN_KEY,
@@ -140,5 +222,11 @@ export {
140
222
  hfDownloadProject,
141
223
  hfUploadProject,
142
224
  hfUploadProjectForm,
143
- hfDeleteProject
225
+ hfDeleteProject,
226
+ hfSaveTags,
227
+ hfLoadTags,
228
+ hfSaveMetadata,
229
+ hfLoadMetadata,
230
+ hfUploadImage,
231
+ hfLoadImageAsBase64
144
232
  };
@@ -4,16 +4,28 @@ import {
4
4
  hfDeleteProject,
5
5
  hfDownloadProject,
6
6
  hfListProjects,
7
+ hfLoadImageAsBase64,
8
+ hfLoadMetadata,
9
+ hfLoadTags,
10
+ hfSaveMetadata,
11
+ hfSaveTags,
12
+ hfUploadImage,
7
13
  hfUploadProject,
8
14
  hfUploadProjectForm,
9
15
  setHFToken
10
- } from "./chunk-JW5O3URT.mjs";
16
+ } from "./chunk-X6S5BP36.mjs";
11
17
  export {
12
18
  HF_TOKEN_KEY,
13
19
  getHFToken,
14
20
  hfDeleteProject,
15
21
  hfDownloadProject,
16
22
  hfListProjects,
23
+ hfLoadImageAsBase64,
24
+ hfLoadMetadata,
25
+ hfLoadTags,
26
+ hfSaveMetadata,
27
+ hfSaveTags,
28
+ hfUploadImage,
17
29
  hfUploadProject,
18
30
  hfUploadProjectForm,
19
31
  setHFToken
package/dist/index.d.mts CHANGED
@@ -97,6 +97,16 @@ interface LabServices {
97
97
  saveResult?: (item: LabItem) => Promise<void>;
98
98
  workspaceTags?: WorkspaceTags;
99
99
  }
100
+ interface HFMetadataEntry {
101
+ id: string;
102
+ prompt?: string;
103
+ seed?: number;
104
+ model?: string;
105
+ tags: string[];
106
+ timestamp: number;
107
+ mimeType: string;
108
+ size?: number;
109
+ }
100
110
 
101
111
  /**
102
112
  * Hook für die Tastaturnavigation im Detailview.
@@ -359,6 +369,7 @@ interface ProjectSyncTabProps {
359
369
  hfToken?: string;
360
370
  onHfLoad?: (file: File) => void;
361
371
  onProjectExportBase64?: () => Promise<string>;
372
+ onHfInitialSync?: (onProgress: (done: number, total: number) => void) => Promise<void>;
362
373
  }
363
374
  declare const ProjectSyncTab: React.FC<ProjectSyncTabProps>;
364
375
 
@@ -448,6 +459,26 @@ declare function buildLoopInstruction(rounds: Array<{
448
459
  declare function autoLabel(index: number): string;
449
460
  declare function buildReferenceImageMediaIds(images: SelectedLabImage[]): string[];
450
461
 
451
- declare const LIB_VERSION = "1.3.4";
462
+ declare function getHFToken(): string | null;
463
+ declare function setHFToken(token: string): void;
464
+ interface HFProjectMeta {
465
+ id: string;
466
+ name: string;
467
+ path: string;
468
+ size: number;
469
+ isLfs: boolean;
470
+ }
471
+ declare function hfListProjects(token: string): Promise<HFProjectMeta[]>;
472
+ declare function hfDownloadProject(path: string, token: string): Promise<File>;
473
+ declare function hfUploadProjectForm(zipBase64: string, name: string, token: string): Promise<HFProjectMeta>;
474
+ declare function hfDeleteProject(path: string, token: string): Promise<void>;
475
+ declare function hfSaveTags(workspaceTags: any, token: string): Promise<void>;
476
+ declare function hfLoadTags(token: string): Promise<any | null>;
477
+ declare function hfSaveMetadata(entries: any[], token: string): Promise<void>;
478
+ declare function hfLoadMetadata(token: string): Promise<any[]>;
479
+ declare function hfUploadImage(base64: string, id: string, token: string, mimeType?: string): Promise<void>;
480
+ declare function hfLoadImageAsBase64(id: string, token: string): Promise<string | null>;
481
+
482
+ declare const LIB_VERSION = "1.3.6";
452
483
 
453
- export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, 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, groupGenerationsToLabItems, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
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
@@ -97,6 +97,16 @@ interface LabServices {
97
97
  saveResult?: (item: LabItem) => Promise<void>;
98
98
  workspaceTags?: WorkspaceTags;
99
99
  }
100
+ interface HFMetadataEntry {
101
+ id: string;
102
+ prompt?: string;
103
+ seed?: number;
104
+ model?: string;
105
+ tags: string[];
106
+ timestamp: number;
107
+ mimeType: string;
108
+ size?: number;
109
+ }
100
110
 
101
111
  /**
102
112
  * Hook für die Tastaturnavigation im Detailview.
@@ -359,6 +369,7 @@ interface ProjectSyncTabProps {
359
369
  hfToken?: string;
360
370
  onHfLoad?: (file: File) => void;
361
371
  onProjectExportBase64?: () => Promise<string>;
372
+ onHfInitialSync?: (onProgress: (done: number, total: number) => void) => Promise<void>;
362
373
  }
363
374
  declare const ProjectSyncTab: React.FC<ProjectSyncTabProps>;
364
375
 
@@ -448,6 +459,26 @@ declare function buildLoopInstruction(rounds: Array<{
448
459
  declare function autoLabel(index: number): string;
449
460
  declare function buildReferenceImageMediaIds(images: SelectedLabImage[]): string[];
450
461
 
451
- declare const LIB_VERSION = "1.3.4";
462
+ declare function getHFToken(): string | null;
463
+ declare function setHFToken(token: string): void;
464
+ interface HFProjectMeta {
465
+ id: string;
466
+ name: string;
467
+ path: string;
468
+ size: number;
469
+ isLfs: boolean;
470
+ }
471
+ declare function hfListProjects(token: string): Promise<HFProjectMeta[]>;
472
+ declare function hfDownloadProject(path: string, token: string): Promise<File>;
473
+ declare function hfUploadProjectForm(zipBase64: string, name: string, token: string): Promise<HFProjectMeta>;
474
+ declare function hfDeleteProject(path: string, token: string): Promise<void>;
475
+ declare function hfSaveTags(workspaceTags: any, token: string): Promise<void>;
476
+ declare function hfLoadTags(token: string): Promise<any | null>;
477
+ declare function hfSaveMetadata(entries: any[], token: string): Promise<void>;
478
+ declare function hfLoadMetadata(token: string): Promise<any[]>;
479
+ declare function hfUploadImage(base64: string, id: string, token: string, mimeType?: string): Promise<void>;
480
+ declare function hfLoadImageAsBase64(id: string, token: string): Promise<string | null>;
481
+
482
+ declare const LIB_VERSION = "1.3.6";
452
483
 
453
- export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, 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, groupGenerationsToLabItems, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
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
@@ -267,6 +267,12 @@ __export(hfStateService_exports, {
267
267
  hfDeleteProject: () => hfDeleteProject,
268
268
  hfDownloadProject: () => hfDownloadProject,
269
269
  hfListProjects: () => hfListProjects,
270
+ hfLoadImageAsBase64: () => hfLoadImageAsBase64,
271
+ hfLoadMetadata: () => hfLoadMetadata,
272
+ hfLoadTags: () => hfLoadTags,
273
+ hfSaveMetadata: () => hfSaveMetadata,
274
+ hfSaveTags: () => hfSaveTags,
275
+ hfUploadImage: () => hfUploadImage,
270
276
  hfUploadProject: () => hfUploadProject,
271
277
  hfUploadProjectForm: () => hfUploadProjectForm,
272
278
  setHFToken: () => setHFToken
@@ -400,6 +406,88 @@ async function hfDeleteProject(path, token) {
400
406
  });
401
407
  if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
402
408
  }
409
+ async function hfSaveTags(workspaceTags, token) {
410
+ const content = new Blob([JSON.stringify(workspaceTags, null, 2)], { type: "application/json" });
411
+ const { uploadFile } = await import("@huggingface/hub");
412
+ await uploadFile({
413
+ repo: { type: "dataset", name: HF_REPO },
414
+ credentials: { accessToken: token },
415
+ file: { path: "tags.json", content },
416
+ branch: "main",
417
+ commitTitle: "Auto-sync tags"
418
+ });
419
+ }
420
+ async function hfLoadTags(token) {
421
+ try {
422
+ const res = await fetch(
423
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
424
+ { headers: { Authorization: `Bearer ${token}` } }
425
+ );
426
+ if (!res.ok) return null;
427
+ return res.json();
428
+ } catch {
429
+ return null;
430
+ }
431
+ }
432
+ async function hfSaveMetadata(entries, token) {
433
+ const content = new Blob([JSON.stringify(entries, null, 2)], { type: "application/json" });
434
+ const { uploadFile } = await import("@huggingface/hub");
435
+ await uploadFile({
436
+ repo: { type: "dataset", name: HF_REPO },
437
+ credentials: { accessToken: token },
438
+ file: { path: "metadata.json", content },
439
+ branch: "main",
440
+ commitTitle: "Auto-sync metadata"
441
+ });
442
+ }
443
+ async function hfLoadMetadata(token) {
444
+ try {
445
+ const res = await fetch(
446
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
447
+ { headers: { Authorization: `Bearer ${token}` } }
448
+ );
449
+ if (!res.ok) return [];
450
+ return res.json();
451
+ } catch {
452
+ return [];
453
+ }
454
+ }
455
+ async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
456
+ const ext = mimeType === "image/png" ? "png" : "jpg";
457
+ const binary = atob(base64);
458
+ const bytes = new Uint8Array(binary.length);
459
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
460
+ const content = new Blob([bytes], { type: mimeType });
461
+ const { uploadFile } = await import("@huggingface/hub");
462
+ await uploadFile({
463
+ repo: { type: "dataset", name: HF_REPO },
464
+ credentials: { accessToken: token },
465
+ file: { path: `images/${id}.${ext}`, content },
466
+ branch: "main",
467
+ commitTitle: `Add image ${id}`
468
+ });
469
+ }
470
+ async function hfLoadImageAsBase64(id, token) {
471
+ for (const ext of ["jpg", "png"]) {
472
+ try {
473
+ const res = await fetch(
474
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/images/${id}.${ext}?download=true`,
475
+ { headers: { Authorization: `Bearer ${token}` } }
476
+ );
477
+ if (!res.ok) continue;
478
+ const blob = await res.blob();
479
+ return new Promise((resolve, reject) => {
480
+ const reader = new FileReader();
481
+ reader.onload = () => resolve(reader.result.split(",")[1]);
482
+ reader.onerror = reject;
483
+ reader.readAsDataURL(blob);
484
+ });
485
+ } catch {
486
+ continue;
487
+ }
488
+ }
489
+ return null;
490
+ }
403
491
  var HF_BASE, HF_REPO, HF_TOKEN_KEY;
404
492
  var init_hfStateService = __esm({
405
493
  "src/lib/hfStateService.ts"() {
@@ -452,12 +540,24 @@ __export(index_exports, {
452
540
  formatTreeToMarkdown: () => formatTreeToMarkdown,
453
541
  frameToGeneration: () => frameToGeneration,
454
542
  getFormattedTimestamp: () => getFormattedTimestamp,
543
+ getHFToken: () => getHFToken,
455
544
  groupGenerationsToLabItems: () => groupGenerationsToLabItems,
545
+ hfDeleteProject: () => hfDeleteProject,
546
+ hfDownloadProject: () => hfDownloadProject,
547
+ hfListProjects: () => hfListProjects,
548
+ hfLoadImageAsBase64: () => hfLoadImageAsBase64,
549
+ hfLoadMetadata: () => hfLoadMetadata,
550
+ hfLoadTags: () => hfLoadTags,
551
+ hfSaveMetadata: () => hfSaveMetadata,
552
+ hfSaveTags: () => hfSaveTags,
553
+ hfUploadImage: () => hfUploadImage,
554
+ hfUploadProjectForm: () => hfUploadProjectForm,
456
555
  importProjectFromZip: () => importProjectFromZip,
457
556
  injectXMPMetadata: () => injectXMPMetadata,
458
557
  interpretSdkError: () => interpretSdkError,
459
558
  parsePromptFile: () => parsePromptFile,
460
559
  parsePromptResponse: () => parsePromptResponse,
560
+ setHFToken: () => setHFToken,
461
561
  useKeyboardNavigation: () => useKeyboardNavigation,
462
562
  useOnClickOutside: () => useOnClickOutside
463
563
  });
@@ -1783,7 +1883,8 @@ var ProjectSyncTab = ({
1783
1883
  onExecuteSync,
1784
1884
  hfToken,
1785
1885
  onHfLoad,
1786
- onProjectExportBase64
1886
+ onProjectExportBase64,
1887
+ onHfInitialSync
1787
1888
  }) => {
1788
1889
  const projectInputRef = (0, import_react13.useRef)(null);
1789
1890
  const workspaceInputRef = (0, import_react13.useRef)(null);
@@ -1843,6 +1944,8 @@ var ProjectSyncTab = ({
1843
1944
  const [hfSaving, setHfSaving] = (0, import_react13.useState)(false);
1844
1945
  const [hfError, setHfError] = (0, import_react13.useState)(null);
1845
1946
  const [hfSaveName, setHfSaveName] = (0, import_react13.useState)("");
1947
+ const [hfSyncProgress, setHfSyncProgress] = (0, import_react13.useState)(null);
1948
+ const [hfSyncing, setHfSyncing] = (0, import_react13.useState)(false);
1846
1949
  const loadHfProjects = async (token) => {
1847
1950
  setHfLoading(true);
1848
1951
  setHfError(null);
@@ -1990,6 +2093,38 @@ var ProjectSyncTab = ({
1990
2093
  }
1991
2094
  )
1992
2095
  ] }),
2096
+ onHfInitialSync && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
2097
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2098
+ PillButton,
2099
+ {
2100
+ variant: "outline",
2101
+ icon: "cloud_sync",
2102
+ loading: hfSyncing,
2103
+ onClick: async () => {
2104
+ setHfSyncing(true);
2105
+ setHfError(null);
2106
+ setHfSyncProgress({ done: 0, total: 0 });
2107
+ try {
2108
+ await onHfInitialSync((done, total) => setHfSyncProgress({ done, total }));
2109
+ setHfSyncProgress(null);
2110
+ } catch (e) {
2111
+ setHfError(e.message);
2112
+ setHfSyncProgress(null);
2113
+ } finally {
2114
+ setHfSyncing(false);
2115
+ }
2116
+ },
2117
+ children: hfSyncing && hfSyncProgress ? `${hfSyncProgress.done} / ${hfSyncProgress.total} Bilder` : "Initial-Sync nach HF"
2118
+ }
2119
+ ),
2120
+ hfSyncing && hfSyncProgress && hfSyncProgress.total > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "w-full h-1 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2121
+ "div",
2122
+ {
2123
+ className: "h-full bg-blue-400 rounded-full transition-all",
2124
+ style: { width: `${Math.round(hfSyncProgress.done / hfSyncProgress.total * 100)}%` }
2125
+ }
2126
+ ) })
2127
+ ] }),
1993
2128
  hfError && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-red-400 font-mono px-1", children: hfError }),
1994
2129
  !hfLoading && hfProjects.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts auf HF gespeichert." }),
1995
2130
  hfProjects.map((p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
@@ -2231,6 +2366,9 @@ function toPromptImages(images) {
2231
2366
  });
2232
2367
  }
2233
2368
 
2369
+ // src/components/AvatarArchitectApp.tsx
2370
+ init_hfStateService();
2371
+
2234
2372
  // src/components/labs/LabsTab.tsx
2235
2373
  var import_react19 = require("react");
2236
2374
 
@@ -3334,6 +3472,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3334
3472
  const [hfToken, setHfToken] = (0, import_react21.useState)("");
3335
3473
  const [hfTokenInput, setHfTokenInput] = (0, import_react21.useState)("");
3336
3474
  const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react21.useState)(false);
3475
+ const [hfMetadata, setHfMetadata] = (0, import_react21.useState)([]);
3476
+ const hfTagSaveTimer = (0, import_react21.useRef)(null);
3337
3477
  const wsInputRef = (0, import_react21.useRef)(null);
3338
3478
  const startApp = (choice) => {
3339
3479
  try {
@@ -3595,6 +3735,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3595
3735
  if (prev.id === genId || !options.silent) return finishedGen;
3596
3736
  return prev;
3597
3737
  });
3738
+ if (hfToken && base64) {
3739
+ const entry = {
3740
+ id: genId,
3741
+ prompt: promptToUse || void 0,
3742
+ seed: seedMode === "fixed" ? seed : activeSeed,
3743
+ model: selectedModel,
3744
+ tags: [],
3745
+ timestamp: Date.now(),
3746
+ mimeType: "image/jpeg"
3747
+ };
3748
+ hfUploadImage(base64, genId, hfToken).catch(() => {
3749
+ });
3750
+ setHfMetadata((prev) => {
3751
+ const next = [...prev, entry];
3752
+ hfSaveMetadata(next, hfToken).catch(() => {
3753
+ });
3754
+ return next;
3755
+ });
3756
+ }
3598
3757
  } catch (err) {
3599
3758
  const errorGen = { ...newGen, status: "error", error: { message: err.message || "Unbekannter Fehler", details: err.toString() } };
3600
3759
  setHistory((prev) => prev.map((g) => g.id === genId ? errorGen : g));
@@ -3800,6 +3959,32 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3800
3959
  await onServerDelete(id);
3801
3960
  await fetchServerProjects();
3802
3961
  };
3962
+ const handleHfInitialSync = async (onProgress) => {
3963
+ if (!hfToken) return;
3964
+ const gens = galleryItems.filter((g) => g.base64 && g.status === "done");
3965
+ const total = gens.length;
3966
+ onProgress(0, total);
3967
+ let done = 0;
3968
+ for (const gen of gens) {
3969
+ const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3970
+ const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3971
+ await hfUploadImage(raw, gen.id, hfToken, mimeType);
3972
+ done++;
3973
+ onProgress(done, total);
3974
+ }
3975
+ const metaEntries = gens.map((g) => ({
3976
+ id: g.id,
3977
+ prompt: g.prompt || void 0,
3978
+ seed: g.seed,
3979
+ model: g.model,
3980
+ tags: g.tags || [],
3981
+ timestamp: g.timestamp,
3982
+ mimeType: g.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg"
3983
+ }));
3984
+ await hfSaveMetadata(metaEntries, hfToken);
3985
+ setHfMetadata(metaEntries);
3986
+ if (workspaceTags) await hfSaveTags(workspaceTags, hfToken);
3987
+ };
3803
3988
  const handleComputeSyncDiff = async () => {
3804
3989
  if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
3805
3990
  const projects = await onFetchServerProjects();
@@ -3839,6 +4024,28 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3839
4024
  (0, import_react21.useEffect)(() => {
3840
4025
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
3841
4026
  }, [activeTab]);
4027
+ (0, import_react21.useEffect)(() => {
4028
+ if (!hfToken) return;
4029
+ hfLoadTags(hfToken).then((tags) => {
4030
+ if (tags?.by_category) setWorkspaceTags(tags);
4031
+ }).catch(() => {
4032
+ });
4033
+ hfLoadMetadata(hfToken).then((entries) => {
4034
+ if (Array.isArray(entries)) setHfMetadata(entries);
4035
+ }).catch(() => {
4036
+ });
4037
+ }, [hfToken]);
4038
+ (0, import_react21.useEffect)(() => {
4039
+ if (!hfToken || !workspaceTags) return;
4040
+ if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
4041
+ hfTagSaveTimer.current = setTimeout(() => {
4042
+ hfSaveTags(workspaceTags, hfToken).catch(() => {
4043
+ });
4044
+ }, 1500);
4045
+ return () => {
4046
+ if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
4047
+ };
4048
+ }, [workspaceTags, hfToken]);
3842
4049
  if (isFullscreen && currentResult?.base64) {
3843
4050
  const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
3844
4051
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
@@ -4290,7 +4497,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4290
4497
  onProjectExportBase64: async () => {
4291
4498
  const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
4292
4499
  return base64;
4293
- }
4500
+ },
4501
+ onHfInitialSync: hfToken ? handleHfInitialSync : void 0
4294
4502
  }
4295
4503
  ),
4296
4504
  activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
@@ -4619,7 +4827,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4619
4827
  onProjectExportBase64: async () => {
4620
4828
  const { base64 } = await Promise.resolve().then(() => (init_project(), project_exports)).then((m) => m.exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id)));
4621
4829
  return base64;
4622
- }
4830
+ },
4831
+ onHfInitialSync: hfToken ? handleHfInitialSync : void 0
4623
4832
  }
4624
4833
  )
4625
4834
  ] })
@@ -4628,7 +4837,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4628
4837
  }
4629
4838
 
4630
4839
  // src/index.ts
4631
- var LIB_VERSION = "1.3.4";
4840
+ init_hfStateService();
4841
+ var LIB_VERSION = "1.3.6";
4632
4842
  // Annotate the CommonJS export names for ESM import in node:
4633
4843
  0 && (module.exports = {
4634
4844
  AvatarArchitectApp,
@@ -4670,12 +4880,24 @@ var LIB_VERSION = "1.3.4";
4670
4880
  formatTreeToMarkdown,
4671
4881
  frameToGeneration,
4672
4882
  getFormattedTimestamp,
4883
+ getHFToken,
4673
4884
  groupGenerationsToLabItems,
4885
+ hfDeleteProject,
4886
+ hfDownloadProject,
4887
+ hfListProjects,
4888
+ hfLoadImageAsBase64,
4889
+ hfLoadMetadata,
4890
+ hfLoadTags,
4891
+ hfSaveMetadata,
4892
+ hfSaveTags,
4893
+ hfUploadImage,
4894
+ hfUploadProjectForm,
4674
4895
  importProjectFromZip,
4675
4896
  injectXMPMetadata,
4676
4897
  interpretSdkError,
4677
4898
  parsePromptFile,
4678
4899
  parsePromptResponse,
4900
+ setHFToken,
4679
4901
  useKeyboardNavigation,
4680
4902
  useOnClickOutside
4681
4903
  });
package/dist/index.mjs CHANGED
@@ -4,10 +4,19 @@ import {
4
4
  injectXMPMetadata
5
5
  } from "./chunk-UFXDXENC.mjs";
6
6
  import {
7
+ getHFToken,
7
8
  hfDeleteProject,
9
+ hfDownloadProject,
8
10
  hfListProjects,
9
- hfUploadProjectForm
10
- } from "./chunk-JW5O3URT.mjs";
11
+ hfLoadImageAsBase64,
12
+ hfLoadMetadata,
13
+ hfLoadTags,
14
+ hfSaveMetadata,
15
+ hfSaveTags,
16
+ hfUploadImage,
17
+ hfUploadProjectForm,
18
+ setHFToken
19
+ } from "./chunk-X6S5BP36.mjs";
11
20
 
12
21
  // src/hooks/useOnClickOutside.ts
13
22
  import { useEffect } from "react";
@@ -1324,7 +1333,8 @@ var ProjectSyncTab = ({
1324
1333
  onExecuteSync,
1325
1334
  hfToken,
1326
1335
  onHfLoad,
1327
- onProjectExportBase64
1336
+ onProjectExportBase64,
1337
+ onHfInitialSync
1328
1338
  }) => {
1329
1339
  const projectInputRef = useRef5(null);
1330
1340
  const workspaceInputRef = useRef5(null);
@@ -1384,6 +1394,8 @@ var ProjectSyncTab = ({
1384
1394
  const [hfSaving, setHfSaving] = useState6(false);
1385
1395
  const [hfError, setHfError] = useState6(null);
1386
1396
  const [hfSaveName, setHfSaveName] = useState6("");
1397
+ const [hfSyncProgress, setHfSyncProgress] = useState6(null);
1398
+ const [hfSyncing, setHfSyncing] = useState6(false);
1387
1399
  const loadHfProjects = async (token) => {
1388
1400
  setHfLoading(true);
1389
1401
  setHfError(null);
@@ -1531,6 +1543,38 @@ var ProjectSyncTab = ({
1531
1543
  }
1532
1544
  )
1533
1545
  ] }),
1546
+ onHfInitialSync && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1547
+ /* @__PURE__ */ jsx12(
1548
+ PillButton,
1549
+ {
1550
+ variant: "outline",
1551
+ icon: "cloud_sync",
1552
+ loading: hfSyncing,
1553
+ onClick: async () => {
1554
+ setHfSyncing(true);
1555
+ setHfError(null);
1556
+ setHfSyncProgress({ done: 0, total: 0 });
1557
+ try {
1558
+ await onHfInitialSync((done, total) => setHfSyncProgress({ done, total }));
1559
+ setHfSyncProgress(null);
1560
+ } catch (e) {
1561
+ setHfError(e.message);
1562
+ setHfSyncProgress(null);
1563
+ } finally {
1564
+ setHfSyncing(false);
1565
+ }
1566
+ },
1567
+ children: hfSyncing && hfSyncProgress ? `${hfSyncProgress.done} / ${hfSyncProgress.total} Bilder` : "Initial-Sync nach HF"
1568
+ }
1569
+ ),
1570
+ hfSyncing && hfSyncProgress && hfSyncProgress.total > 0 && /* @__PURE__ */ jsx12("div", { className: "w-full h-1 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx12(
1571
+ "div",
1572
+ {
1573
+ className: "h-full bg-blue-400 rounded-full transition-all",
1574
+ style: { width: `${Math.round(hfSyncProgress.done / hfSyncProgress.total * 100)}%` }
1575
+ }
1576
+ ) })
1577
+ ] }),
1534
1578
  hfError && /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-red-400 font-mono px-1", children: hfError }),
1535
1579
  !hfLoading && hfProjects.length === 0 && /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts auf HF gespeichert." }),
1536
1580
  hfProjects.map((p) => /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
@@ -1547,8 +1591,8 @@ var ProjectSyncTab = ({
1547
1591
  {
1548
1592
  onClick: async () => {
1549
1593
  try {
1550
- const { hfDownloadProject } = await import("./hfStateService-UUDYKEMD.mjs");
1551
- const file = await hfDownloadProject(p.path, hfToken);
1594
+ const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-B62RV5K3.mjs");
1595
+ const file = await hfDownloadProject2(p.path, hfToken);
1552
1596
  onHfLoad(file);
1553
1597
  } catch (e) {
1554
1598
  setHfError(e.message);
@@ -2871,6 +2915,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2871
2915
  const [hfToken, setHfToken] = useState14("");
2872
2916
  const [hfTokenInput, setHfTokenInput] = useState14("");
2873
2917
  const [isLoadingFromHF, setIsLoadingFromHF] = useState14(false);
2918
+ const [hfMetadata, setHfMetadata] = useState14([]);
2919
+ const hfTagSaveTimer = useRef6(null);
2874
2920
  const wsInputRef = useRef6(null);
2875
2921
  const startApp = (choice) => {
2876
2922
  try {
@@ -3132,6 +3178,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3132
3178
  if (prev.id === genId || !options.silent) return finishedGen;
3133
3179
  return prev;
3134
3180
  });
3181
+ if (hfToken && base64) {
3182
+ const entry = {
3183
+ id: genId,
3184
+ prompt: promptToUse || void 0,
3185
+ seed: seedMode === "fixed" ? seed : activeSeed,
3186
+ model: selectedModel,
3187
+ tags: [],
3188
+ timestamp: Date.now(),
3189
+ mimeType: "image/jpeg"
3190
+ };
3191
+ hfUploadImage(base64, genId, hfToken).catch(() => {
3192
+ });
3193
+ setHfMetadata((prev) => {
3194
+ const next = [...prev, entry];
3195
+ hfSaveMetadata(next, hfToken).catch(() => {
3196
+ });
3197
+ return next;
3198
+ });
3199
+ }
3135
3200
  } catch (err) {
3136
3201
  const errorGen = { ...newGen, status: "error", error: { message: err.message || "Unbekannter Fehler", details: err.toString() } };
3137
3202
  setHistory((prev) => prev.map((g) => g.id === genId ? errorGen : g));
@@ -3337,6 +3402,32 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3337
3402
  await onServerDelete(id);
3338
3403
  await fetchServerProjects();
3339
3404
  };
3405
+ const handleHfInitialSync = async (onProgress) => {
3406
+ if (!hfToken) return;
3407
+ const gens = galleryItems.filter((g) => g.base64 && g.status === "done");
3408
+ const total = gens.length;
3409
+ onProgress(0, total);
3410
+ let done = 0;
3411
+ for (const gen of gens) {
3412
+ const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3413
+ const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3414
+ await hfUploadImage(raw, gen.id, hfToken, mimeType);
3415
+ done++;
3416
+ onProgress(done, total);
3417
+ }
3418
+ const metaEntries = gens.map((g) => ({
3419
+ id: g.id,
3420
+ prompt: g.prompt || void 0,
3421
+ seed: g.seed,
3422
+ model: g.model,
3423
+ tags: g.tags || [],
3424
+ timestamp: g.timestamp,
3425
+ mimeType: g.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg"
3426
+ }));
3427
+ await hfSaveMetadata(metaEntries, hfToken);
3428
+ setHfMetadata(metaEntries);
3429
+ if (workspaceTags) await hfSaveTags(workspaceTags, hfToken);
3430
+ };
3340
3431
  const handleComputeSyncDiff = async () => {
3341
3432
  if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
3342
3433
  const projects = await onFetchServerProjects();
@@ -3376,6 +3467,28 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3376
3467
  useEffect5(() => {
3377
3468
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
3378
3469
  }, [activeTab]);
3470
+ useEffect5(() => {
3471
+ if (!hfToken) return;
3472
+ hfLoadTags(hfToken).then((tags) => {
3473
+ if (tags?.by_category) setWorkspaceTags(tags);
3474
+ }).catch(() => {
3475
+ });
3476
+ hfLoadMetadata(hfToken).then((entries) => {
3477
+ if (Array.isArray(entries)) setHfMetadata(entries);
3478
+ }).catch(() => {
3479
+ });
3480
+ }, [hfToken]);
3481
+ useEffect5(() => {
3482
+ if (!hfToken || !workspaceTags) return;
3483
+ if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
3484
+ hfTagSaveTimer.current = setTimeout(() => {
3485
+ hfSaveTags(workspaceTags, hfToken).catch(() => {
3486
+ });
3487
+ }, 1500);
3488
+ return () => {
3489
+ if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
3490
+ };
3491
+ }, [workspaceTags, hfToken]);
3379
3492
  if (isFullscreen && currentResult?.base64) {
3380
3493
  const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
3381
3494
  return /* @__PURE__ */ jsxs18(
@@ -3507,10 +3620,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3507
3620
  onClick: async () => {
3508
3621
  setIsLoadingFromHF(true);
3509
3622
  try {
3510
- const { hfListProjects: hfListProjects2, hfDownloadProject } = await import("./hfStateService-UUDYKEMD.mjs");
3623
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-B62RV5K3.mjs");
3511
3624
  const projects = await hfListProjects2(hfToken);
3512
3625
  if (projects.length > 0) {
3513
- const file = await hfDownloadProject(projects[0].path, hfToken);
3626
+ const file = await hfDownloadProject2(projects[0].path, hfToken);
3514
3627
  await handleProjectImport(file);
3515
3628
  }
3516
3629
  } catch (e) {
@@ -3827,7 +3940,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3827
3940
  onProjectExportBase64: async () => {
3828
3941
  const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
3829
3942
  return base64;
3830
- }
3943
+ },
3944
+ onHfInitialSync: hfToken ? handleHfInitialSync : void 0
3831
3945
  }
3832
3946
  ),
3833
3947
  activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
@@ -4156,7 +4270,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4156
4270
  onProjectExportBase64: async () => {
4157
4271
  const { base64 } = await import("./project-O4ORKXY5.mjs").then((m) => m.exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id)));
4158
4272
  return base64;
4159
- }
4273
+ },
4274
+ onHfInitialSync: hfToken ? handleHfInitialSync : void 0
4160
4275
  }
4161
4276
  )
4162
4277
  ] })
@@ -4165,7 +4280,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4165
4280
  }
4166
4281
 
4167
4282
  // src/index.ts
4168
- var LIB_VERSION = "1.3.4";
4283
+ var LIB_VERSION = "1.3.6";
4169
4284
  export {
4170
4285
  AvatarArchitectApp,
4171
4286
  CollapsibleCard,
@@ -4206,12 +4321,24 @@ export {
4206
4321
  formatTreeToMarkdown,
4207
4322
  frameToGeneration,
4208
4323
  getFormattedTimestamp,
4324
+ getHFToken,
4209
4325
  groupGenerationsToLabItems,
4326
+ hfDeleteProject,
4327
+ hfDownloadProject,
4328
+ hfListProjects,
4329
+ hfLoadImageAsBase64,
4330
+ hfLoadMetadata,
4331
+ hfLoadTags,
4332
+ hfSaveMetadata,
4333
+ hfSaveTags,
4334
+ hfUploadImage,
4335
+ hfUploadProjectForm,
4210
4336
  importProjectFromZip,
4211
4337
  injectXMPMetadata,
4212
4338
  interpretSdkError,
4213
4339
  parsePromptFile,
4214
4340
  parsePromptResponse,
4341
+ setHFToken,
4215
4342
  useKeyboardNavigation,
4216
4343
  useOnClickOutside
4217
4344
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",