@rslsp1/fa-app-tools 1.3.18 → 2.0.12

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
@@ -5,18 +5,23 @@ import {
5
5
  } from "./chunk-UFXDXENC.mjs";
6
6
  import {
7
7
  getHFToken,
8
+ getSessionClientId,
9
+ hfBatchArchive,
10
+ hfBootstrapFromLegacy,
8
11
  hfDeleteProject,
9
12
  hfDownloadProject,
13
+ hfListDir,
10
14
  hfListProjects,
11
15
  hfLoadImageAsBase64,
12
- hfLoadMetadata,
13
- hfLoadTags,
14
- hfSaveMetadata,
15
- hfSaveTags,
16
16
  hfUploadImage,
17
17
  hfUploadProjectForm,
18
- setHFToken
19
- } from "./chunk-X6S5BP36.mjs";
18
+ hfUploadSmallFile,
19
+ loadHFState,
20
+ loadPendingEvents,
21
+ setHFToken,
22
+ tsFromEventPath,
23
+ writeHFEvent
24
+ } from "./chunk-WCFXXLKN.mjs";
20
25
 
21
26
  // src/hooks/useOnClickOutside.ts
22
27
  import { useEffect } from "react";
@@ -793,7 +798,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
793
798
  }
794
799
 
795
800
  // src/components/AvatarArchitectApp.tsx
796
- import { useState as useState14, useCallback, useMemo as useMemo2, useEffect as useEffect5, useRef as useRef6 } from "react";
801
+ import { useState as useState15, useCallback as useCallback2, useMemo as useMemo2, useEffect as useEffect6, useRef as useRef7 } from "react";
797
802
 
798
803
  // src/components/PromptTab.tsx
799
804
  import { useRef as useRef4, useState as useState5 } from "react";
@@ -1320,6 +1325,7 @@ var PromptTab = ({
1320
1325
  import { useRef as useRef5, useState as useState6, useEffect as useEffect4 } from "react";
1321
1326
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1322
1327
  var ProjectSyncTab = ({
1328
+ topSlot,
1323
1329
  onProjectExport,
1324
1330
  onProjectImport,
1325
1331
  onWorkspaceImport,
@@ -1411,6 +1417,7 @@ var ProjectSyncTab = ({
1411
1417
  if (hfToken) loadHfProjects(hfToken);
1412
1418
  }, [hfToken]);
1413
1419
  return /* @__PURE__ */ jsx12("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar", children: /* @__PURE__ */ jsxs10("div", { className: "p-6 flex flex-col gap-8", children: [
1420
+ topSlot && /* @__PURE__ */ jsx12("div", { children: topSlot }),
1414
1421
  (onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-4", children: [
1415
1422
  /* @__PURE__ */ jsx12(SectionLabel, { children: "Projekt-ZIP" }),
1416
1423
  /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
@@ -1591,7 +1598,7 @@ var ProjectSyncTab = ({
1591
1598
  {
1592
1599
  onClick: async () => {
1593
1600
  try {
1594
- const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-B62RV5K3.mjs");
1601
+ const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-6YYT6ATO.mjs");
1595
1602
  const file = await hfDownloadProject2(p.path, hfToken);
1596
1603
  onHfLoad(file);
1597
1604
  } catch (e) {
@@ -1812,14 +1819,254 @@ function toPromptImages(images) {
1812
1819
  });
1813
1820
  }
1814
1821
 
1822
+ // src/hooks/useHFState.ts
1823
+ import { useState as useState7, useEffect as useEffect5, useRef as useRef6, useCallback } from "react";
1824
+
1825
+ // src/lib/hfReducer.ts
1826
+ function applyEvent(state, event) {
1827
+ if (event.v.major > 1) return state;
1828
+ switch (event.type) {
1829
+ case "image_added": {
1830
+ const p = event.payload;
1831
+ if (state.metadata.some((m) => m.id === p.id)) return state;
1832
+ return { ...state, metadata: [...state.metadata, p] };
1833
+ }
1834
+ case "tag_upserted": {
1835
+ const p = event.payload;
1836
+ const tags = state.tags;
1837
+ const cat = tags.by_category[p.category] || [];
1838
+ const existing = cat.find((t) => t.value === p.value);
1839
+ const updated = { label: p.label, value: p.value, is_user_created: p.is_user_created, is_deleted: p.is_deleted };
1840
+ const newCat = existing ? cat.map((t) => t.value === p.value ? { ...t, ...updated } : t) : [...cat, updated];
1841
+ const newAll = tags.all.some((t) => t.value === p.value && t.category === p.category) ? tags.all.map((t) => t.value === p.value && t.category === p.category ? { ...t, ...updated, category: p.category } : t) : [...tags.all, { ...updated, category: p.category }];
1842
+ return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
1843
+ }
1844
+ case "metadata_updated": {
1845
+ const p = event.payload;
1846
+ return {
1847
+ ...state,
1848
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
1849
+ };
1850
+ }
1851
+ default:
1852
+ return state;
1853
+ }
1854
+ }
1855
+ function applyEvents(state, events) {
1856
+ return events.reduce(applyEvent, state);
1857
+ }
1858
+
1859
+ // src/lib/hfDag.ts
1860
+ function buildDag(events) {
1861
+ const dag = /* @__PURE__ */ new Map();
1862
+ for (const event of events) {
1863
+ dag.set(event.ts, { event, children: [] });
1864
+ }
1865
+ for (const event of events) {
1866
+ for (const parent of event.prevTs) {
1867
+ const node = dag.get(parent);
1868
+ if (node && !node.children.includes(event.ts)) {
1869
+ node.children.push(event.ts);
1870
+ }
1871
+ }
1872
+ }
1873
+ return dag;
1874
+ }
1875
+ function findTips(dag) {
1876
+ return [...dag.values()].filter((n) => n.children.length === 0).map((n) => n.event.ts);
1877
+ }
1878
+ function findForks(dag) {
1879
+ const forks = [];
1880
+ for (const [ts, node] of dag) {
1881
+ if (node.children.length > 1) {
1882
+ forks.push({ parentTs: ts, childTs: node.children });
1883
+ }
1884
+ }
1885
+ return forks;
1886
+ }
1887
+ function topoSort(events) {
1888
+ if (!events.length) return [];
1889
+ const inDegree = /* @__PURE__ */ new Map();
1890
+ const children = /* @__PURE__ */ new Map();
1891
+ const tsSet = new Set(events.map((e) => e.ts));
1892
+ for (const e of events) {
1893
+ inDegree.set(e.ts, 0);
1894
+ children.set(e.ts, []);
1895
+ }
1896
+ for (const e of events) {
1897
+ for (const p of e.prevTs) {
1898
+ if (tsSet.has(p)) {
1899
+ children.get(p).push(e.ts);
1900
+ inDegree.set(e.ts, (inDegree.get(e.ts) || 0) + 1);
1901
+ }
1902
+ }
1903
+ }
1904
+ const queue = events.filter((e) => (inDegree.get(e.ts) || 0) === 0).sort((a, b) => a.ts - b.ts);
1905
+ const result = [];
1906
+ const byTs = new Map(events.map((e) => [e.ts, e]));
1907
+ while (queue.length) {
1908
+ const node = queue.shift();
1909
+ result.push(node);
1910
+ for (const childTs of children.get(node.ts) || []) {
1911
+ const newDeg = (inDegree.get(childTs) || 0) - 1;
1912
+ inDegree.set(childTs, newDeg);
1913
+ if (newDeg === 0) {
1914
+ const child = byTs.get(childTs);
1915
+ const insertAt = queue.findIndex((q) => q.ts > child.ts);
1916
+ if (insertAt === -1) queue.push(child);
1917
+ else queue.splice(insertAt, 0, child);
1918
+ }
1919
+ }
1920
+ }
1921
+ return result;
1922
+ }
1923
+
1924
+ // src/hooks/useHFState.ts
1925
+ var OFFLINE_BUFFER_KEY = "hf-offline-buffer";
1926
+ var POLL_INTERVAL_MS = 3e4;
1927
+ function readOfflineBuffer() {
1928
+ try {
1929
+ return JSON.parse(localStorage.getItem(OFFLINE_BUFFER_KEY) || "[]");
1930
+ } catch {
1931
+ return [];
1932
+ }
1933
+ }
1934
+ function writeOfflineBuffer(events) {
1935
+ try {
1936
+ localStorage.setItem(OFFLINE_BUFFER_KEY, JSON.stringify(events));
1937
+ } catch {
1938
+ }
1939
+ }
1940
+ function useHFState(token, namespace) {
1941
+ const [state, setState] = useState7(null);
1942
+ const [isLoading, setIsLoading] = useState7(false);
1943
+ const [error, setError] = useState7(null);
1944
+ const [eventCount, setEventCount] = useState7(0);
1945
+ const [forks, setForks] = useState7([]);
1946
+ const [pendingBufferCount, setPendingBufferCount] = useState7(readOfflineBuffer().length);
1947
+ const [lastEventTs, setLastEventTs] = useState7(0);
1948
+ const [hasStateZip, setHasStateZip] = useState7(false);
1949
+ const knownEventPaths = useRef6(/* @__PURE__ */ new Set());
1950
+ const allEventsRef = useRef6([]);
1951
+ const applyNewEvents = useCallback((snapshot, newEvents) => {
1952
+ if (!newEvents.length && allEventsRef.current.length === 0) {
1953
+ setEventCount(0);
1954
+ return snapshot;
1955
+ }
1956
+ const sorted = topoSort([...allEventsRef.current, ...newEvents]);
1957
+ allEventsRef.current = sorted;
1958
+ const afterConsolidation = sorted.filter((e) => e.ts > snapshot.meta.consolidatedAt);
1959
+ const dag = buildDag(afterConsolidation);
1960
+ setForks(findForks(dag));
1961
+ setEventCount(afterConsolidation.length);
1962
+ if (afterConsolidation.length) setLastEventTs(Math.max(...afterConsolidation.map((e) => e.ts)));
1963
+ return applyEvents(snapshot, afterConsolidation);
1964
+ }, []);
1965
+ const loadFull = useCallback(async () => {
1966
+ if (!token || !namespace) return;
1967
+ setIsLoading(true);
1968
+ setError(null);
1969
+ try {
1970
+ const snapshot = await loadHFState(namespace, token);
1971
+ setHasStateZip(snapshot !== null);
1972
+ const base = snapshot ?? {
1973
+ metadata: [],
1974
+ tags: { by_category: {}, all: [] },
1975
+ meta: { consolidatedAt: 0, version: 1 }
1976
+ };
1977
+ const events = await loadPendingEvents(namespace, token, base.meta.consolidatedAt);
1978
+ events.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
1979
+ allEventsRef.current = [];
1980
+ const finalState = applyNewEvents(base, events);
1981
+ setState(finalState);
1982
+ const buffer = readOfflineBuffer();
1983
+ if (buffer.length) {
1984
+ for (const evt of buffer) {
1985
+ await writeHFEvent(namespace, token, evt.type, evt.payload, evt.prevTs).catch(() => {
1986
+ });
1987
+ }
1988
+ writeOfflineBuffer([]);
1989
+ setPendingBufferCount(0);
1990
+ const freshEvents = await loadPendingEvents(namespace, token, base.meta.consolidatedAt);
1991
+ freshEvents.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
1992
+ setState((prev) => prev ? applyNewEvents(base, freshEvents) : prev);
1993
+ }
1994
+ } catch (e) {
1995
+ setError(e.message);
1996
+ } finally {
1997
+ setIsLoading(false);
1998
+ }
1999
+ }, [token, namespace, applyNewEvents]);
2000
+ const pollNew = useCallback(async () => {
2001
+ if (!token || !namespace || !state) return;
2002
+ try {
2003
+ const events = await loadPendingEvents(namespace, token, state.meta.consolidatedAt);
2004
+ const newEvents = events.filter((e) => !knownEventPaths.current.has(`${e.ts}_${e.clientId}`));
2005
+ if (!newEvents.length) return;
2006
+ newEvents.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
2007
+ setState((prev) => prev ? applyNewEvents(prev, newEvents) : prev);
2008
+ } catch {
2009
+ }
2010
+ }, [token, namespace, state, applyNewEvents]);
2011
+ useEffect5(() => {
2012
+ if (token && namespace) loadFull();
2013
+ }, [token, namespace]);
2014
+ useEffect5(() => {
2015
+ if (!token || !namespace) return;
2016
+ const id = setInterval(pollNew, POLL_INTERVAL_MS);
2017
+ return () => clearInterval(id);
2018
+ }, [token, namespace, pollNew]);
2019
+ const writeEvent = useCallback(async (type, payload) => {
2020
+ const prevTs = lastEventTs ? [lastEventTs] : [state?.meta.consolidatedAt ?? 0];
2021
+ console.log("[HF] writeEvent called:", { type, namespace, tokenOk: !!token, prevTs });
2022
+ await pollNew();
2023
+ try {
2024
+ console.log("[HF] writeHFEvent start, path will be:", `${namespace}events/...`);
2025
+ const event = await writeHFEvent(namespace, token, type, payload, prevTs);
2026
+ console.log("[HF] writeHFEvent success:", event.ts);
2027
+ knownEventPaths.current.add(`${event.ts}_${event.clientId}`);
2028
+ setState((prev) => prev ? applyNewEvents(prev, [event]) : prev);
2029
+ setLastEventTs(event.ts);
2030
+ await pollNew();
2031
+ } catch (e) {
2032
+ console.error("[HF] writeHFEvent FAILED, going to offline buffer:", e);
2033
+ const buffer = readOfflineBuffer();
2034
+ const offline = {
2035
+ v: { major: 1, minor: 0 },
2036
+ type,
2037
+ ts: Date.now(),
2038
+ prevTs,
2039
+ clientId: getSessionClientId(),
2040
+ payload
2041
+ };
2042
+ writeOfflineBuffer([...buffer, offline]);
2043
+ setPendingBufferCount(buffer.length + 1);
2044
+ setState((prev) => prev ? applyNewEvents(prev, [offline]) : prev);
2045
+ }
2046
+ }, [namespace, token, lastEventTs, state, pollNew, applyNewEvents]);
2047
+ return {
2048
+ state,
2049
+ isLoading,
2050
+ error,
2051
+ pendingBufferCount,
2052
+ eventCount,
2053
+ forks,
2054
+ writeEvent,
2055
+ refresh: loadFull,
2056
+ lastEventTs,
2057
+ allEvents: allEventsRef.current,
2058
+ hasStateZip
2059
+ };
2060
+ }
2061
+
1815
2062
  // src/components/labs/LabsTab.tsx
1816
- import { useState as useState12 } from "react";
2063
+ import { useState as useState13 } from "react";
1817
2064
 
1818
2065
  // src/components/labs/LabRemix.tsx
1819
- import { useState as useState8 } from "react";
2066
+ import { useState as useState9 } from "react";
1820
2067
 
1821
2068
  // src/components/labs/LabImagePicker.tsx
1822
- import { useState as useState7 } from "react";
2069
+ import { useState as useState8 } from "react";
1823
2070
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1824
2071
  var LabImagePicker = ({
1825
2072
  availableItems,
@@ -1828,8 +2075,8 @@ var LabImagePicker = ({
1828
2075
  onClose,
1829
2076
  title = "Bild w\xE4hlen"
1830
2077
  }) => {
1831
- const [search, setSearch] = useState7("");
1832
- const [drillItem, setDrillItem] = useState7(null);
2078
+ const [search, setSearch] = useState8("");
2079
+ const [drillItem, setDrillItem] = useState8(null);
1833
2080
  const filtered = availableItems.filter(
1834
2081
  (item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
1835
2082
  );
@@ -1931,13 +2178,13 @@ var LabImagePicker = ({
1931
2178
  // src/components/labs/LabRemix.tsx
1932
2179
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1933
2180
  var LabRemix = ({ services, onResult }) => {
1934
- const [showPicker, setShowPicker] = useState8(false);
1935
- const [selected, setSelected] = useState8(null);
1936
- const [instruction, setInstruction] = useState8("");
1937
- const [generatedPrompt, setGeneratedPrompt] = useState8("");
1938
- const [resultImage, setResultImage] = useState8(null);
1939
- const [isGeneratingPrompt, setIsGeneratingPrompt] = useState8(false);
1940
- const [isGeneratingImage, setIsGeneratingImage] = useState8(false);
2181
+ const [showPicker, setShowPicker] = useState9(false);
2182
+ const [selected, setSelected] = useState9(null);
2183
+ const [instruction, setInstruction] = useState9("");
2184
+ const [generatedPrompt, setGeneratedPrompt] = useState9("");
2185
+ const [resultImage, setResultImage] = useState9(null);
2186
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState9(false);
2187
+ const [isGeneratingImage, setIsGeneratingImage] = useState9(false);
1941
2188
  const handleSelectImage = (item, frame) => {
1942
2189
  services.onItemUsed(item);
1943
2190
  setSelected({
@@ -2120,16 +2367,16 @@ var LabRemix = ({ services, onResult }) => {
2120
2367
  };
2121
2368
 
2122
2369
  // src/components/labs/LabBlend.tsx
2123
- import { useState as useState9 } from "react";
2370
+ import { useState as useState10 } from "react";
2124
2371
  import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2125
2372
  var LabBlend = ({ services, onResult }) => {
2126
- const [showPickerFor, setShowPickerFor] = useState9(null);
2127
- const [selectedImages, setSelectedImages] = useState9([]);
2128
- const [instruction, setInstruction] = useState9("");
2129
- const [generatedPrompt, setGeneratedPrompt] = useState9("");
2130
- const [resultImage, setResultImage] = useState9(null);
2131
- const [isGeneratingPrompt, setIsGeneratingPrompt] = useState9(false);
2132
- const [isGeneratingImage, setIsGeneratingImage] = useState9(false);
2373
+ const [showPickerFor, setShowPickerFor] = useState10(null);
2374
+ const [selectedImages, setSelectedImages] = useState10([]);
2375
+ const [instruction, setInstruction] = useState10("");
2376
+ const [generatedPrompt, setGeneratedPrompt] = useState10("");
2377
+ const [resultImage, setResultImage] = useState10(null);
2378
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState10(false);
2379
+ const [isGeneratingImage, setIsGeneratingImage] = useState10(false);
2133
2380
  const handleSelectImage = (index, item, frame) => {
2134
2381
  services.onItemUsed(item);
2135
2382
  const newImg = {
@@ -2316,17 +2563,17 @@ var LabBlend = ({ services, onResult }) => {
2316
2563
  };
2317
2564
 
2318
2565
  // src/components/labs/LabCompare.tsx
2319
- import { useState as useState10 } from "react";
2566
+ import { useState as useState11 } from "react";
2320
2567
  import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2321
2568
  var LabCompare = ({ services, onResult }) => {
2322
- const [showPickerFor, setShowPickerFor] = useState10(null);
2323
- const [selectedImages, setSelectedImages] = useState10([]);
2324
- const [instruction, setInstruction] = useState10("");
2325
- const [analysis, setAnalysis] = useState10("");
2326
- const [generatedPrompt, setGeneratedPrompt] = useState10("");
2327
- const [resultImage, setResultImage] = useState10(null);
2328
- const [isAnalyzing, setIsAnalyzing] = useState10(false);
2329
- const [isGeneratingImage, setIsGeneratingImage] = useState10(false);
2569
+ const [showPickerFor, setShowPickerFor] = useState11(null);
2570
+ const [selectedImages, setSelectedImages] = useState11([]);
2571
+ const [instruction, setInstruction] = useState11("");
2572
+ const [analysis, setAnalysis] = useState11("");
2573
+ const [generatedPrompt, setGeneratedPrompt] = useState11("");
2574
+ const [resultImage, setResultImage] = useState11(null);
2575
+ const [isAnalyzing, setIsAnalyzing] = useState11(false);
2576
+ const [isGeneratingImage, setIsGeneratingImage] = useState11(false);
2330
2577
  const handleSelectImage = (index, item, frame) => {
2331
2578
  services.onItemUsed(item);
2332
2579
  const newImg = {
@@ -2489,14 +2736,14 @@ var LabCompare = ({ services, onResult }) => {
2489
2736
  };
2490
2737
 
2491
2738
  // src/components/labs/LabLoop.tsx
2492
- import { useState as useState11 } from "react";
2739
+ import { useState as useState12 } from "react";
2493
2740
  import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2494
2741
  var LabLoop = ({ services, onResult }) => {
2495
- const [rounds, setRounds] = useState11([]);
2496
- const [currentInstruction, setCurrentInstruction] = useState11("");
2497
- const [showPickerForRound, setShowPickerForRound] = useState11(null);
2498
- const [pendingImages, setPendingImages] = useState11([]);
2499
- const [isGenerating, setIsGenerating] = useState11(false);
2742
+ const [rounds, setRounds] = useState12([]);
2743
+ const [currentInstruction, setCurrentInstruction] = useState12("");
2744
+ const [showPickerForRound, setShowPickerForRound] = useState12(null);
2745
+ const [pendingImages, setPendingImages] = useState12([]);
2746
+ const [isGenerating, setIsGenerating] = useState12(false);
2500
2747
  const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
2501
2748
  const handleAddImage = (item, frame) => {
2502
2749
  services.onItemUsed(item);
@@ -2660,7 +2907,7 @@ var TABS = [
2660
2907
  { key: "loop", label: "Loop", icon: "loop" }
2661
2908
  ];
2662
2909
  var LabsTab = ({ services, onResult }) => {
2663
- const [activeTab, setActiveTab] = useState12("remix");
2910
+ const [activeTab, setActiveTab] = useState13("remix");
2664
2911
  return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col h-full overflow-hidden", children: [
2665
2912
  /* @__PURE__ */ jsx18("div", { className: "flex border-b border-white/5 shrink-0", children: TABS.map((tab) => /* @__PURE__ */ jsxs16(
2666
2913
  "button",
@@ -2684,19 +2931,19 @@ var LabsTab = ({ services, onResult }) => {
2684
2931
  };
2685
2932
 
2686
2933
  // src/components/TagManagerPanel.tsx
2687
- import { useState as useState13 } from "react";
2934
+ import { useState as useState14 } from "react";
2688
2935
  import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2689
2936
  function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
2690
2937
  const categories = Object.keys(workspaceTags.by_category).filter(
2691
2938
  (cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
2692
2939
  );
2693
- const [selectedCategory, setSelectedCategory] = useState13(categories[0] || "");
2940
+ const [selectedCategory, setSelectedCategory] = useState14(categories[0] || "");
2694
2941
  const effectiveCategory = categories.includes(selectedCategory) ? selectedCategory : categories[0] || "";
2695
- const [editingLabel, setEditingLabel] = useState13(null);
2696
- const [editState, setEditState] = useState13({ label: "", value: "" });
2697
- const [newTag, setNewTag] = useState13({ label: "", value: "" });
2698
- const [movingLabel, setMovingLabel] = useState13(null);
2699
- const [moveTarget, setMoveTarget] = useState13("");
2942
+ const [editingLabel, setEditingLabel] = useState14(null);
2943
+ const [editState, setEditState] = useState14({ label: "", value: "" });
2944
+ const [newTag, setNewTag] = useState14({ label: "", value: "" });
2945
+ const [movingLabel, setMovingLabel] = useState14(null);
2946
+ const [moveTarget, setMoveTarget] = useState14("");
2700
2947
  const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
2701
2948
  const otherCategories = categories.filter((c) => c !== effectiveCategory);
2702
2949
  const startEdit = (tag) => {
@@ -2893,8 +3140,8 @@ function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete,
2893
3140
 
2894
3141
  // src/components/AvatarArchitectApp.tsx
2895
3142
  import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
2896
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
2897
- useEffect5(() => {
3143
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
3144
+ useEffect6(() => {
2898
3145
  const id = "flow-styles";
2899
3146
  if (!document.getElementById(id)) {
2900
3147
  const style = document.createElement("style");
@@ -2903,22 +3150,92 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2903
3150
  document.head.appendChild(style);
2904
3151
  }
2905
3152
  }, []);
2906
- const [showStart, setShowStart] = useState14(true);
2907
- const [layoutChoice, setLayoutChoice] = useState14(() => {
3153
+ const [showStart, setShowStart] = useState15(true);
3154
+ const [layoutChoice, setLayoutChoice] = useState15(() => {
2908
3155
  try {
2909
3156
  return localStorage.getItem("aa-layout") || null;
2910
3157
  } catch {
2911
3158
  return null;
2912
3159
  }
2913
3160
  });
2914
- const [projectLoaded, setProjectLoaded] = useState14(false);
2915
- const [hfToken, setHfToken] = useState14(initialHfToken || "");
2916
- const [hfTokenInput, setHfTokenInput] = useState14(initialHfToken || "");
2917
- const [isLoadingFromHF, setIsLoadingFromHF] = useState14(false);
2918
- const [hfMetadata, setHfMetadata] = useState14([]);
2919
- const hfTagSaveTimer = useRef6(null);
2920
- const hfMetaSaveQueue = useRef6(Promise.resolve());
2921
- const wsInputRef = useRef6(null);
3161
+ const [projectLoaded, setProjectLoaded] = useState15(false);
3162
+ const [hfToken, setHfToken] = useState15(initialHfToken || "");
3163
+ const [hfTokenInput, setHfTokenInput] = useState15(initialHfToken || "");
3164
+ const [isLoadingFromHF, setIsLoadingFromHF] = useState15(false);
3165
+ const [hfNamespaceLocal, setHfNamespaceLocal] = useState15(() => {
3166
+ try {
3167
+ const stored = localStorage.getItem("aa-hf-namespace");
3168
+ if (stored !== null) return stored;
3169
+ return allowDevNamespace ? "app.art-by-rolands.de/" : "";
3170
+ } catch {
3171
+ return "";
3172
+ }
3173
+ });
3174
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState15(null);
3175
+ useEffect6(() => {
3176
+ if (hfNamespace !== void 0) return;
3177
+ const backendUrl = typeof window !== "undefined" ? window.BACKEND_URL || window.location.origin : null;
3178
+ if (!backendUrl) return;
3179
+ fetch(`${backendUrl}/api/status`).then((r) => r.json()).then((d) => {
3180
+ if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
3181
+ }).catch(() => {
3182
+ });
3183
+ }, [hfNamespace]);
3184
+ const effectiveNamespace = hfNamespace ?? hfNamespaceFromServer ?? hfNamespaceLocal;
3185
+ const {
3186
+ state: hfState,
3187
+ isLoading: isHfRefreshing,
3188
+ pendingBufferCount,
3189
+ eventCount,
3190
+ writeEvent: hfWriteEvent,
3191
+ refresh: refreshHF,
3192
+ hasStateZip
3193
+ } = useHFState(hfToken, effectiveNamespace);
3194
+ const [bootstrapLog, setBootstrapLog] = useState15([]);
3195
+ const [isBootstrapping, setIsBootstrapping] = useState15(false);
3196
+ const syncTopSlot = /* @__PURE__ */ jsxs18(Fragment9, { children: [
3197
+ pendingBufferCount > 0 && /* @__PURE__ */ jsxs18("div", { style: { background: "linear-gradient(90deg,#f59e0b,#ef4444)", padding: "4px 10px", fontSize: 11, color: "#fff", borderRadius: 4, marginBottom: 4 }, children: [
3198
+ pendingBufferCount,
3199
+ " \xC4nderung",
3200
+ pendingBufferCount > 1 ? "en" : "",
3201
+ " lokal \u2014 bei Flow-Reload verloren wenn nicht synchronisiert"
3202
+ ] }),
3203
+ eventCount > 100 && /* @__PURE__ */ jsxs18("div", { style: { background: "#dc2626", color: "#fff", padding: "5px 10px", borderRadius: 4, marginBottom: 4, fontWeight: 600, fontSize: 11 }, children: [
3204
+ "\u26A0 ",
3205
+ eventCount,
3206
+ " Events nicht konsolidiert \u2014 Konsolidierung dringend empfohlen"
3207
+ ] }),
3208
+ eventCount > 50 && eventCount <= 100 && /* @__PURE__ */ jsxs18("div", { style: { background: "#44403c", color: "#a8a29e", padding: "4px 10px", borderRadius: 4, marginBottom: 4, fontSize: 11 }, children: [
3209
+ eventCount,
3210
+ " Events seit letzter Konsolidierung \u2014 Konsolidierung empfohlen"
3211
+ ] }),
3212
+ hfToken && !hasStateZip && !isHfRefreshing && /* @__PURE__ */ jsxs18("div", { style: { background: "#1c1917", border: "1px solid #44403c", borderRadius: 6, padding: "10px 12px" }, children: [
3213
+ /* @__PURE__ */ jsx20("div", { style: { fontSize: 12, color: "#a8a29e", marginBottom: 6 }, children: effectiveNamespace ? `Kein State-Snapshot in HF (${effectiveNamespace}) \u2014 aus Legacy-Daten (tags.json + metadata.json) migrieren?` : "Namespace wird geladen\u2026" }),
3214
+ /* @__PURE__ */ jsx20(
3215
+ "button",
3216
+ {
3217
+ disabled: isBootstrapping || !effectiveNamespace,
3218
+ onClick: async () => {
3219
+ setIsBootstrapping(true);
3220
+ setBootstrapLog([]);
3221
+ try {
3222
+ await hfBootstrapFromLegacy(effectiveNamespace, hfToken, (msg) => setBootstrapLog((prev) => [...prev, msg]));
3223
+ setBootstrapLog((prev) => [...prev, "\u2713 Fertig"]);
3224
+ setTimeout(() => refreshHF(), 1500);
3225
+ } catch (e) {
3226
+ setBootstrapLog((prev) => [...prev, `Fehler: ${e.message}`]);
3227
+ } finally {
3228
+ setIsBootstrapping(false);
3229
+ }
3230
+ },
3231
+ style: { padding: "5px 12px", background: "#0ea5e9", color: "#fff", border: "none", borderRadius: 4, cursor: isBootstrapping || !effectiveNamespace ? "not-allowed" : "pointer", fontSize: 11, fontWeight: 600, opacity: isBootstrapping || !effectiveNamespace ? 0.6 : 1 },
3232
+ children: isBootstrapping ? "Migriere\u2026" : "Legacy-Migration starten"
3233
+ }
3234
+ ),
3235
+ bootstrapLog.length > 0 && /* @__PURE__ */ jsx20("div", { style: { marginTop: 6, fontFamily: "monospace", fontSize: 10, color: "#78716c", lineHeight: 1.6 }, children: bootstrapLog.map((l, i) => /* @__PURE__ */ jsx20("div", { children: l }, i)) })
3236
+ ] })
3237
+ ] });
3238
+ const wsInputRef = useRef7(null);
2922
3239
  const startApp = (choice) => {
2923
3240
  try {
2924
3241
  localStorage.setItem("aa-layout", choice);
@@ -2927,70 +3244,110 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2927
3244
  setLayoutChoice(choice);
2928
3245
  setShowStart(false);
2929
3246
  };
2930
- const [nodes, setNodes] = useState14([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
2931
- const [edges, setEdges] = useState14([]);
2932
- const [history, setHistory] = useState14([]);
2933
- const [galleryItems, setGalleryItems] = useState14([]);
2934
- const galleryItemsRef = useRef6([]);
2935
- useEffect5(() => {
3247
+ const [nodes, setNodes] = useState15([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
3248
+ const [edges, setEdges] = useState15([]);
3249
+ const [history, setHistory] = useState15([]);
3250
+ const [galleryItems, setGalleryItems] = useState15([]);
3251
+ const galleryItemsRef = useRef7([]);
3252
+ useEffect6(() => {
2936
3253
  galleryItemsRef.current = galleryItems;
2937
3254
  }, [galleryItems]);
2938
- const [activePrompt, setActivePrompt] = useState14("");
2939
- const [isSynthesizing, setIsSynthesizing] = useState14(false);
2940
- const [activeGenerationsCount, setActiveGenerationsCount] = useState14(0);
2941
- const [currentResult, setCurrentResult] = useState14(null);
2942
- const [focusedNodeId, setFocusedNodeId] = useState14(null);
2943
- const [leftTab, setLeftTab] = useState14("prompt");
2944
- const [promptFeedback, setPromptFeedback] = useState14(null);
2945
- const [lastPromptPayload, setLastPromptPayload] = useState14(null);
2946
- const [isPromptTabGenerating, setIsPromptTabGenerating] = useState14(false);
2947
- const [activeTab, setActiveTab] = useState14("history");
2948
- const [mobileTab, setMobileTab] = useState14("stage");
2949
- const [middlePanel, setMiddlePanel] = useState14("stage");
2950
- const [recentLabItems, setRecentLabItems] = useState14([]);
2951
- const [aspectRatio, setAspectRatio] = useState14("1:1");
2952
- const [selectedModel, setSelectedModel] = useState14("\u{1F34C} Nano Banana Pro");
2953
- const [seed, setSeed] = useState14(Math.floor(Math.random() * 1e6));
2954
- const [seedMode, setSeedMode] = useState14("random");
2955
- const [isLeftCollapsed, setIsLeftCollapsed] = useState14(false);
2956
- const [isRightCollapsed, setIsRightCollapsed] = useState14(false);
2957
- const [leftPanelWidth, setLeftPanelWidth] = useState14(() => {
3255
+ const hfImageNotFoundRef = useRef7(/* @__PURE__ */ new Set());
3256
+ useEffect6(() => {
3257
+ if (!hfState) return;
3258
+ if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
3259
+ const hfIds = new Set(hfState.metadata.map((m) => m.id));
3260
+ const skeletons = hfState.metadata.map((m) => ({
3261
+ id: m.id,
3262
+ nodeId: m.id,
3263
+ prompt: m.prompt,
3264
+ seed: m.seed,
3265
+ model: m.model,
3266
+ tags: m.tags || [],
3267
+ timestamp: m.timestamp,
3268
+ status: "done"
3269
+ }));
3270
+ setGalleryItems((prev) => {
3271
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3272
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3273
+ return [...localOnly, ...merged];
3274
+ });
3275
+ setHistory((prev) => {
3276
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3277
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3278
+ return [...localOnly, ...merged];
3279
+ });
3280
+ for (const entry of hfState.metadata) {
3281
+ if (hfImageNotFoundRef.current.has(entry.id)) continue;
3282
+ hfLoadImageAsBase64(entry.id, hfToken).then((b64) => {
3283
+ if (!b64) {
3284
+ hfImageNotFoundRef.current.add(entry.id);
3285
+ return;
3286
+ }
3287
+ const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
3288
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3289
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3290
+ }).catch(() => {
3291
+ hfImageNotFoundRef.current.add(entry.id);
3292
+ });
3293
+ }
3294
+ }, [hfState]);
3295
+ const [activePrompt, setActivePrompt] = useState15("");
3296
+ const [isSynthesizing, setIsSynthesizing] = useState15(false);
3297
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState15(0);
3298
+ const [currentResult, setCurrentResult] = useState15(null);
3299
+ const [focusedNodeId, setFocusedNodeId] = useState15(null);
3300
+ const [leftTab, setLeftTab] = useState15("prompt");
3301
+ const [promptFeedback, setPromptFeedback] = useState15(null);
3302
+ const [lastPromptPayload, setLastPromptPayload] = useState15(null);
3303
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState15(false);
3304
+ const [activeTab, setActiveTab] = useState15("history");
3305
+ const [mobileTab, setMobileTab] = useState15("stage");
3306
+ const [middlePanel, setMiddlePanel] = useState15("stage");
3307
+ const [recentLabItems, setRecentLabItems] = useState15([]);
3308
+ const [aspectRatio, setAspectRatio] = useState15("1:1");
3309
+ const [selectedModel, setSelectedModel] = useState15("\u{1F34C} Nano Banana Pro");
3310
+ const [seed, setSeed] = useState15(Math.floor(Math.random() * 1e6));
3311
+ const [seedMode, setSeedMode] = useState15("random");
3312
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState15(false);
3313
+ const [isRightCollapsed, setIsRightCollapsed] = useState15(false);
3314
+ const [leftPanelWidth, setLeftPanelWidth] = useState15(() => {
2958
3315
  try {
2959
3316
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
2960
3317
  } catch {
2961
3318
  return 260;
2962
3319
  }
2963
3320
  });
2964
- const [rightPanelWidth, setRightPanelWidth] = useState14(() => {
3321
+ const [rightPanelWidth, setRightPanelWidth] = useState15(() => {
2965
3322
  try {
2966
3323
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
2967
3324
  } catch {
2968
3325
  return 320;
2969
3326
  }
2970
3327
  });
2971
- const [isPromptCollapsed, setIsPromptCollapsed] = useState14(false);
2972
- const [projectActionState, setProjectActionState] = useState14("idle");
2973
- const syncServerDataRef = useRef6(null);
2974
- const [workspaceTags, setWorkspaceTags] = useState14(null);
2975
- const [serverProjects, setServerProjects] = useState14([]);
2976
- const [isLoadingFromServer, setIsLoadingFromServer] = useState14(false);
2977
- const [highContrast, setHighContrast] = useState14(() => {
3328
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState15(false);
3329
+ const [projectActionState, setProjectActionState] = useState15("idle");
3330
+ const syncServerDataRef = useRef7(null);
3331
+ const [workspaceTags, setWorkspaceTags] = useState15(null);
3332
+ const [serverProjects, setServerProjects] = useState15([]);
3333
+ const [isLoadingFromServer, setIsLoadingFromServer] = useState15(false);
3334
+ const [highContrast, setHighContrast] = useState15(() => {
2978
3335
  try {
2979
3336
  return localStorage.getItem("aa-contrast") === "high";
2980
3337
  } catch {
2981
3338
  return false;
2982
3339
  }
2983
3340
  });
2984
- const [activeReferenceId, setActiveReferenceId] = useState14(null);
2985
- const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState14(null);
2986
- const [isScanningImage, setIsScanningImage] = useState14(false);
2987
- const [touchStartX, setTouchStartX] = useState14(null);
2988
- const [isFullscreen, setIsFullscreen] = useState14(false);
2989
- const [zoomScale, setZoomScale] = useState14(1);
2990
- const [zoomOffset, setZoomOffset] = useState14({ x: 0, y: 0 });
2991
- const lastPinchDist = useRef6(null);
2992
- const lastTapTime = useRef6(0);
2993
- const dragStart = useRef6(null);
3341
+ const [activeReferenceId, setActiveReferenceId] = useState15(null);
3342
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState15(null);
3343
+ const [isScanningImage, setIsScanningImage] = useState15(false);
3344
+ const [touchStartX, setTouchStartX] = useState15(null);
3345
+ const [isFullscreen, setIsFullscreen] = useState15(false);
3346
+ const [zoomScale, setZoomScale] = useState15(1);
3347
+ const [zoomOffset, setZoomOffset] = useState15({ x: 0, y: 0 });
3348
+ const lastPinchDist = useRef7(null);
3349
+ const lastTapTime = useRef7(0);
3350
+ const dragStart = useRef7(null);
2994
3351
  const openFullscreen = () => {
2995
3352
  setIsFullscreen(true);
2996
3353
  setZoomScale(1);
@@ -3134,16 +3491,16 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3134
3491
  }
3135
3492
  };
3136
3493
  const currentIndex = useMemo2(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
3137
- const goToPrev = useCallback(() => {
3494
+ const goToPrev = useCallback2(() => {
3138
3495
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
3139
3496
  }, [currentIndex, history]);
3140
- const goToNext = useCallback(() => {
3497
+ const goToNext = useCallback2(() => {
3141
3498
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
3142
3499
  }, [currentIndex, history]);
3143
3500
  const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
3144
3501
  const isGenerating = activeGenerationsCount > 0;
3145
3502
  useKeyboardNavigation(history, currentResult, setCurrentResult);
3146
- const getSubtreeFormat = useCallback((nodeId, depth = 0) => {
3503
+ const getSubtreeFormat = useCallback2((nodeId, depth = 0) => {
3147
3504
  const node = nodes.find((n) => n.id === nodeId);
3148
3505
  if (!node) return "";
3149
3506
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -3183,7 +3540,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3183
3540
  if (prev.id === genId || !options.silent) return finishedGen;
3184
3541
  return prev;
3185
3542
  });
3186
- if (hfToken && base64) {
3543
+ console.log("[HF] handleGenerateImage \u2014 condition check:", { hfToken: !!hfToken, base64: !!base64, effectiveNamespace });
3544
+ if (hfToken && base64 && effectiveNamespace) {
3545
+ hfUploadImage(base64, genId, hfToken).catch((e) => {
3546
+ console.error("[HF] hfUploadImage failed:", e);
3547
+ });
3187
3548
  const entry = {
3188
3549
  id: genId,
3189
3550
  prompt: promptToUse || void 0,
@@ -3193,20 +3554,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3193
3554
  timestamp: Date.now(),
3194
3555
  mimeType: "image/jpeg"
3195
3556
  };
3196
- hfUploadImage(base64, genId, hfToken).catch(() => {
3197
- });
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 {
3209
- }
3557
+ console.log("[HF] calling hfWriteEvent, namespace:", effectiveNamespace);
3558
+ hfWriteEvent("image_added", entry).catch((e) => {
3559
+ console.error("[HF] hfWriteEvent outer catch:", e);
3210
3560
  });
3211
3561
  }
3212
3562
  } catch (err) {
@@ -3242,6 +3592,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3242
3592
  all: [...prev.all, { ...newTagOption, category: tag.category }]
3243
3593
  };
3244
3594
  });
3595
+ if (hfToken && effectiveNamespace) {
3596
+ const p = { category: tag.category, label: tag.label, value: tag.value, is_user_created: true };
3597
+ hfWriteEvent("tag_upserted", p).catch(() => {
3598
+ });
3599
+ }
3245
3600
  };
3246
3601
  const handleTagUpdate = (originalLabel, originalCategory, updates) => {
3247
3602
  setWorkspaceTags((prev) => {
@@ -3254,8 +3609,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3254
3609
  );
3255
3610
  return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
3256
3611
  });
3612
+ if (hfToken && effectiveNamespace) {
3613
+ const p = { category: originalCategory, label: updates.label, value: updates.value, is_user_created: true };
3614
+ hfWriteEvent("tag_upserted", p).catch(() => {
3615
+ });
3616
+ }
3257
3617
  };
3258
3618
  const handleTagDelete = (label, category) => {
3619
+ const tagValue = workspaceTags?.by_category[category]?.find((t) => t.label === label)?.value;
3259
3620
  setWorkspaceTags((prev) => {
3260
3621
  if (!prev) return prev;
3261
3622
  const updatedCat = (prev.by_category[category] || []).map(
@@ -3266,6 +3627,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3266
3627
  );
3267
3628
  return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
3268
3629
  });
3630
+ if (hfToken && effectiveNamespace && tagValue) {
3631
+ const p = { category, label, value: tagValue, is_deleted: true };
3632
+ hfWriteEvent("tag_upserted", p).catch(() => {
3633
+ });
3634
+ }
3269
3635
  };
3270
3636
  const handleTagReorder = (category, reorderedTags) => {
3271
3637
  setWorkspaceTags((prev) => {
@@ -3415,38 +3781,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3415
3781
  await fetchServerProjects();
3416
3782
  };
3417
3783
  const handleHfInitialSync = async (onProgress) => {
3418
- if (!hfToken) return;
3419
- const gens = galleryItems.filter((g) => g.base64 && g.status === "done");
3784
+ if (!hfToken || !effectiveNamespace) return;
3785
+ const existingIds = new Set((hfState?.metadata || []).map((m) => m.id));
3786
+ const gens = galleryItems.filter((g) => g.base64 && g.status === "done" && !existingIds.has(g.id));
3420
3787
  const total = gens.length;
3421
3788
  onProgress(0, total);
3422
3789
  let done = 0;
3423
3790
  for (const gen of gens) {
3424
3791
  const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3425
3792
  const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3426
- await hfUploadImage(raw, gen.id, hfToken, mimeType);
3793
+ await hfUploadImage(raw, gen.id, hfToken, mimeType).catch(() => {
3794
+ });
3795
+ const entry = {
3796
+ id: gen.id,
3797
+ prompt: gen.prompt || void 0,
3798
+ seed: gen.seed,
3799
+ model: gen.model,
3800
+ tags: gen.tags || [],
3801
+ timestamp: gen.timestamp,
3802
+ mimeType
3803
+ };
3804
+ await hfWriteEvent("image_added", entry).catch(() => {
3805
+ });
3427
3806
  done++;
3428
3807
  onProgress(done, total);
3429
3808
  }
3430
- const localEntries = gens.map((g) => ({
3431
- id: g.id,
3432
- prompt: g.prompt || void 0,
3433
- seed: g.seed,
3434
- model: g.model,
3435
- tags: g.tags || [],
3436
- timestamp: g.timestamp,
3437
- mimeType: g.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg"
3438
- }));
3439
- const existingMeta = await hfLoadMetadata(hfToken);
3440
- const existingIds = new Set((existingMeta || []).map((e) => e.id));
3441
- const newEntries = localEntries.filter((e) => !existingIds.has(e.id));
3442
- const mergedMeta = [...existingMeta || [], ...newEntries];
3443
- await hfSaveMetadata(mergedMeta, hfToken);
3444
- setHfMetadata(mergedMeta);
3445
3809
  if (workspaceTags) {
3446
- const remoteTags = await hfLoadTags(hfToken).catch(() => null);
3447
- const mergedTags = mergeWorkspaceTags(workspaceTags, remoteTags);
3448
- await hfSaveTags(mergedTags, hfToken);
3449
- setWorkspaceTags(mergedTags);
3810
+ for (const [category, tags] of Object.entries(workspaceTags.by_category)) {
3811
+ for (const tag of tags) {
3812
+ const p = { category, label: tag.label, value: tag.value, is_user_created: tag.is_user_created, is_deleted: tag.is_deleted };
3813
+ await hfWriteEvent("tag_upserted", p).catch(() => {
3814
+ });
3815
+ }
3816
+ }
3450
3817
  }
3451
3818
  };
3452
3819
  const handleComputeSyncDiff = async () => {
@@ -3485,87 +3852,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3485
3852
  setTimeout(() => setProjectActionState("idle"), 4e3);
3486
3853
  }
3487
3854
  };
3488
- useEffect5(() => {
3855
+ useEffect6(() => {
3489
3856
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
3490
3857
  }, [activeTab]);
3491
- const [isHfRefreshing, setIsHfRefreshing] = useState14(false);
3492
- const loadFromHF = useCallback(async (token) => {
3493
- setIsHfRefreshing(true);
3494
- try {
3495
- hfLoadTags(token).then((tags) => {
3496
- if (tags?.by_category) setWorkspaceTags(tags);
3497
- }).catch(() => {
3498
- });
3499
- const entries = await hfLoadMetadata(token);
3500
- if (!Array.isArray(entries) || entries.length === 0) return;
3501
- setHfMetadata(entries);
3502
- const hfIds = new Set(entries.map((e) => e.id));
3503
- const hfSkeletons = entries.map((e) => ({
3504
- id: e.id,
3505
- nodeId: e.id,
3506
- prompt: e.prompt,
3507
- seed: e.seed,
3508
- model: e.model,
3509
- tags: e.tags || [],
3510
- timestamp: e.timestamp,
3511
- status: "done"
3512
- }));
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
- });
3523
- for (const entry of entries) {
3524
- hfLoadImageAsBase64(entry.id, token).then((b64) => {
3525
- if (!b64) return;
3526
- const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
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));
3529
- }).catch(() => {
3530
- });
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
- }
3562
- } finally {
3563
- setIsHfRefreshing(false);
3564
- }
3565
- }, []);
3566
- useEffect5(() => {
3567
- if (hfToken) loadFromHF(hfToken);
3568
- }, [hfToken]);
3569
3858
  const mergeWorkspaceTags = (local, remote) => {
3570
3859
  if (!remote?.by_category) return local;
3571
3860
  const merged = {};
@@ -3585,22 +3874,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3585
3874
  const all = Object.entries(merged).flatMap(([cat, tags]) => tags.map((t) => ({ ...t, category: cat })));
3586
3875
  return { by_category: merged, all };
3587
3876
  };
3588
- useEffect5(() => {
3589
- if (!hfToken || !workspaceTags) return;
3590
- if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
3591
- hfTagSaveTimer.current = setTimeout(async () => {
3592
- const remote = await hfLoadTags(hfToken).catch(() => null);
3593
- const merged = mergeWorkspaceTags(workspaceTags, remote);
3594
- await hfSaveTags(merged, hfToken).catch(() => {
3595
- });
3596
- if (Object.values(merged.by_category).some(
3597
- (tags, i) => tags.length !== Object.values(workspaceTags.by_category)[i]?.length
3598
- )) setWorkspaceTags(merged);
3599
- }, 1500);
3600
- return () => {
3601
- if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
3602
- };
3603
- }, [workspaceTags, hfToken]);
3604
3877
  if (isFullscreen && currentResult?.base64) {
3605
3878
  const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
3606
3879
  return /* @__PURE__ */ jsxs18(
@@ -3732,7 +4005,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3732
4005
  onClick: async () => {
3733
4006
  setIsLoadingFromHF(true);
3734
4007
  try {
3735
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-B62RV5K3.mjs");
4008
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-6YYT6ATO.mjs");
3736
4009
  const projects = await hfListProjects2(hfToken);
3737
4010
  if (projects.length > 0) {
3738
4011
  const file = await hfDownloadProject2(projects[0].path, hfToken);
@@ -3805,6 +4078,28 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3805
4078
  )) }),
3806
4079
  layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
3807
4080
  layoutChoice === "tablet-landscape" && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
4081
+ ] }),
4082
+ !hfNamespace && !hfNamespaceFromServer && /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-3 w-full max-w-[280px]", children: [
4083
+ /* @__PURE__ */ jsx20("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "State:" }),
4084
+ ["app.art-by-rolands.de/", "dev-app.art-by-rolands.de/"].map((ns, i) => /* @__PURE__ */ jsx20(
4085
+ "button",
4086
+ {
4087
+ onClick: () => {
4088
+ setHfNamespaceLocal(ns);
4089
+ try {
4090
+ localStorage.setItem("aa-hf-namespace", ns);
4091
+ } catch {
4092
+ }
4093
+ },
4094
+ className: "px-3 py-1 rounded-lg text-[11px] font-bold transition-colors",
4095
+ style: {
4096
+ background: hfNamespaceLocal === ns ? "#e7e5e4" : "#44403c",
4097
+ color: hfNamespaceLocal === ns ? "#1c1917" : "#e7e5e4"
4098
+ },
4099
+ children: i === 0 ? "PROD" : "DEV"
4100
+ },
4101
+ ns
4102
+ ))
3808
4103
  ] })
3809
4104
  ] });
3810
4105
  }
@@ -3941,7 +4236,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3941
4236
  mobileTab === "browse" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col flex-1 min-h-0", children: [
3942
4237
  /* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
3943
4238
  ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab(tab), className: `flex-1 flex items-center justify-center gap-1.5 transition-colors text-[11px] font-bold uppercase tracking-wide ${activeTab === tab ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)),
3944
- hfToken && /* @__PURE__ */ jsx20("button", { onClick: () => loadFromHF(hfToken), disabled: isHfRefreshing, className: "w-12 flex items-center justify-center text-white/20 active:text-white transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
4239
+ hfToken && /* @__PURE__ */ jsx20("button", { onClick: () => refreshHF(), disabled: isHfRefreshing, className: "w-12 flex items-center justify-center text-white/20 active:text-white transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
3945
4240
  ] }),
3946
4241
  /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
3947
4242
  activeTab === "history" && /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
@@ -4039,6 +4334,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4039
4334
  activeTab === "sync" && /* @__PURE__ */ jsx20(
4040
4335
  ProjectSyncTab,
4041
4336
  {
4337
+ topSlot: syncTopSlot,
4042
4338
  onProjectExport: handleProjectExport,
4043
4339
  onProjectImport: (f) => handleProjectImport(f),
4044
4340
  onWorkspaceImport: handleWorkspaceImport,
@@ -4331,7 +4627,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4331
4627
  setActiveTab(tab);
4332
4628
  setIsRightCollapsed(false);
4333
4629
  }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : tab === "sync" ? "cloud_sync" : "label" }) }, tab)) }),
4334
- hfToken && /* @__PURE__ */ jsx20("button", { onClick: () => loadFromHF(hfToken), disabled: isHfRefreshing, className: "w-10 flex items-center justify-center text-white/20 hover:text-white/60 transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[18px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) }),
4630
+ hfToken && /* @__PURE__ */ jsx20("button", { onClick: () => refreshHF(), disabled: isHfRefreshing, className: "w-10 flex items-center justify-center text-white/20 hover:text-white/60 transition-colors disabled:opacity-30", children: /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[18px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) }),
4335
4631
  /* @__PURE__ */ jsx20("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
4336
4632
  ] }),
4337
4633
  !isRightCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
@@ -4370,6 +4666,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4370
4666
  activeTab === "sync" && /* @__PURE__ */ jsx20(
4371
4667
  ProjectSyncTab,
4372
4668
  {
4669
+ topSlot: syncTopSlot,
4373
4670
  onProjectExport: handleProjectExport,
4374
4671
  onProjectImport: (f) => handleProjectImport(f),
4375
4672
  onWorkspaceImport: handleWorkspaceImport,
@@ -4396,6 +4693,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4396
4693
  }
4397
4694
 
4398
4695
  // src/components/FaApp.tsx
4696
+ import { useState as useState16, useEffect as useEffect7 } from "react";
4399
4697
  import { jsx as jsx21 } from "react/jsx-runtime";
4400
4698
  function FaApp({
4401
4699
  onGenerateImage,
@@ -4407,12 +4705,22 @@ function FaApp({
4407
4705
  onFlowUpload: _onFlowUpload,
4408
4706
  onFlowMediaUpload: _onFlowMediaUpload,
4409
4707
  libToken,
4708
+ allowDevNamespace,
4709
+ serverBaseUrl,
4410
4710
  onFetchServerProjects,
4411
4711
  onServerSave,
4412
4712
  onServerLoad,
4413
4713
  onServerDelete,
4414
4714
  buildInfo
4415
4715
  }) {
4716
+ const [hfNamespace, setHfNamespace] = useState16(void 0);
4717
+ useEffect7(() => {
4718
+ if (!serverBaseUrl) return;
4719
+ fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
4720
+ if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
4721
+ }).catch(() => {
4722
+ });
4723
+ }, [serverBaseUrl]);
4416
4724
  const wrappedPrompt = async (text, options) => {
4417
4725
  const result = await onGeneratePrompt(text, options);
4418
4726
  return result.text;
@@ -4425,6 +4733,8 @@ function FaApp({
4425
4733
  onDownload,
4426
4734
  onSelectMedia,
4427
4735
  initialHfToken: libToken ? libToken.startsWith("hf_") ? libToken : `hf_${libToken}` : void 0,
4736
+ hfNamespace,
4737
+ allowDevNamespace: !hfNamespace && allowDevNamespace,
4428
4738
  onFetchServerProjects,
4429
4739
  onServerSave,
4430
4740
  onServerLoad,
@@ -4435,7 +4745,7 @@ function FaApp({
4435
4745
  }
4436
4746
 
4437
4747
  // src/index.ts
4438
- var LIB_VERSION = "1.3.18";
4748
+ var LIB_VERSION = "2.0.12";
4439
4749
  export {
4440
4750
  AvatarArchitectApp,
4441
4751
  CollapsibleCard,
@@ -4460,9 +4770,12 @@ export {
4460
4770
  SectionLabel,
4461
4771
  SetupPanel,
4462
4772
  TagManagerPanel,
4773
+ applyEvent,
4774
+ applyEvents,
4463
4775
  autoLabel,
4464
4776
  buildBlendInstruction,
4465
4777
  buildCompareInstruction,
4778
+ buildDag,
4466
4779
  buildFallbackPrompt,
4467
4780
  buildGenerationPrompt,
4468
4781
  buildImageGenerationOptions,
@@ -4474,27 +4787,36 @@ export {
4474
4787
  cleanAiResponse,
4475
4788
  createFlowServices,
4476
4789
  exportProjectToZip,
4790
+ findForks,
4791
+ findTips,
4477
4792
  formatTreeToMarkdown,
4478
4793
  frameToGeneration,
4479
4794
  getFormattedTimestamp,
4480
4795
  getHFToken,
4796
+ getSessionClientId,
4481
4797
  groupGenerationsToLabItems,
4798
+ hfBatchArchive,
4799
+ hfBootstrapFromLegacy,
4482
4800
  hfDeleteProject,
4483
4801
  hfDownloadProject,
4802
+ hfListDir,
4484
4803
  hfListProjects,
4485
4804
  hfLoadImageAsBase64,
4486
- hfLoadMetadata,
4487
- hfLoadTags,
4488
- hfSaveMetadata,
4489
- hfSaveTags,
4490
4805
  hfUploadImage,
4491
4806
  hfUploadProjectForm,
4807
+ hfUploadSmallFile,
4492
4808
  importProjectFromZip,
4493
4809
  injectXMPMetadata,
4494
4810
  interpretSdkError,
4811
+ loadHFState,
4812
+ loadPendingEvents,
4495
4813
  parsePromptFile,
4496
4814
  parsePromptResponse,
4497
4815
  setHFToken,
4816
+ topoSort,
4817
+ tsFromEventPath,
4818
+ useHFState,
4498
4819
  useKeyboardNavigation,
4499
- useOnClickOutside
4820
+ useOnClickOutside,
4821
+ writeHFEvent
4500
4822
  };