@rslsp1/fa-app-tools 1.3.18 → 2.0.13

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 useState16, 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 writeEvent2 = 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: writeEvent2,
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) => {
@@ -2891,10 +3138,449 @@ function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete,
2891
3138
  ] });
2892
3139
  }
2893
3140
 
3141
+ // src/components/HFTestTab.tsx
3142
+ import { useState as useState15 } from "react";
3143
+ import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
3144
+ var HF_BASE = "https://huggingface.co";
3145
+ var HF_REPO = "RolandSch/fa-app-state";
3146
+ var TEST_DIR = "test";
3147
+ async function doFetch(label, url, init = {}) {
3148
+ const rawHeaders = init.headers || {};
3149
+ const sanitized = Object.fromEntries(
3150
+ Object.entries(rawHeaders).map(
3151
+ ([k, v]) => k.toLowerCase() === "authorization" ? [k, v.replace(/Bearer\s+\S+/, (m) => "Bearer " + m.slice(7, 14) + "***")] : [k, v]
3152
+ )
3153
+ );
3154
+ const s = {
3155
+ label,
3156
+ method: init.method || "GET",
3157
+ url,
3158
+ reqHeaders: sanitized,
3159
+ reqBody: init.body ? typeof init.body === "string" ? init.body.length > 800 ? init.body.slice(0, 800) + "\n\u2026[truncated]" : init.body : "[binary data]" : void 0
3160
+ };
3161
+ const t0 = Date.now();
3162
+ try {
3163
+ const r = await fetch(url, init);
3164
+ s.durationMs = Date.now() - t0;
3165
+ s.resStatus = r.status;
3166
+ s.resStatusText = r.statusText;
3167
+ const body = await r.text().catch(() => "");
3168
+ s.resBody = body.length > 3e3 ? body.slice(0, 3e3) + "\n\u2026[truncated]" : body;
3169
+ s.ok = r.ok;
3170
+ } catch (e) {
3171
+ s.durationMs = Date.now() - t0;
3172
+ s.error = String(e?.message ?? e);
3173
+ s.ok = false;
3174
+ }
3175
+ return s;
3176
+ }
3177
+ function toBase64(bytes) {
3178
+ let bin = "";
3179
+ bytes.forEach((b) => bin += String.fromCharCode(b));
3180
+ return btoa(bin);
3181
+ }
3182
+ async function sha256hex(bytes) {
3183
+ const buf = await crypto.subtle.digest("SHA-256", bytes);
3184
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
3185
+ }
3186
+ function b64ToBytes(b64) {
3187
+ const raw = b64.includes(",") ? b64.split(",")[1] : b64;
3188
+ const bin = atob(raw);
3189
+ const bytes = new Uint8Array(bin.length);
3190
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
3191
+ return bytes;
3192
+ }
3193
+ function rawB64(dataUrl) {
3194
+ return dataUrl.includes(",") ? dataUrl.split(",")[1] : dataUrl;
3195
+ }
3196
+ async function uploadDirect(token, path, b64content) {
3197
+ const ndjson = [
3198
+ JSON.stringify({ key: "header", value: { summary: `HF Test: upload ${path}`, description: "" } }),
3199
+ JSON.stringify({ key: "file", value: { path, encoding: "base64", content: b64content } })
3200
+ ].join("\n");
3201
+ return [await doFetch(
3202
+ "POST commit (direct, kein LFS)",
3203
+ `${HF_BASE}/api/datasets/${HF_REPO}/commit/main`,
3204
+ { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" }, body: ndjson }
3205
+ )];
3206
+ }
3207
+ async function uploadLFS(token, path, bytes) {
3208
+ const oid = await sha256hex(bytes);
3209
+ const size = bytes.length;
3210
+ const sample = toBase64(bytes.slice(0, 512));
3211
+ const steps = [];
3212
+ const pre = await doFetch("Preupload (LFS-URL anfordern)", `${HF_BASE}/api/datasets/${HF_REPO}/preupload/main`, {
3213
+ method: "POST",
3214
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
3215
+ body: JSON.stringify({ files: [{ path, size, sample }] })
3216
+ });
3217
+ steps.push(pre);
3218
+ if (!pre.ok) return steps;
3219
+ let fi;
3220
+ try {
3221
+ fi = JSON.parse(pre.resBody || "{}").files?.[0];
3222
+ } catch {
3223
+ }
3224
+ if (!fi) {
3225
+ steps.push({ label: "Parse", method: "-", url: "-", reqHeaders: {}, resBody: "Kein files[0] in preupload response", ok: false });
3226
+ return steps;
3227
+ }
3228
+ if (fi.uploadMode === "lfs" && fi.uploadUrl) {
3229
+ const up = await doFetch("PUT to LFS upload URL", fi.uploadUrl, {
3230
+ method: "PUT",
3231
+ headers: { "Content-Type": "application/octet-stream", ...fi.header || {} },
3232
+ body: bytes
3233
+ });
3234
+ steps.push(up);
3235
+ if (!up.ok) return steps;
3236
+ if (fi.verifyUrl) {
3237
+ const ver = await doFetch("POST LFS verify", fi.verifyUrl, {
3238
+ method: "POST",
3239
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...fi.verifyHeader || {} },
3240
+ body: JSON.stringify({ oid, size })
3241
+ });
3242
+ steps.push(ver);
3243
+ if (!ver.ok) return steps;
3244
+ }
3245
+ } else {
3246
+ steps.push({ label: "Info", method: "-", url: "-", reqHeaders: {}, resBody: `uploadMode="${fi.uploadMode}" \xB7 uploadUrl: ${fi.uploadUrl ? "ja" : "nein"} \u2192 ${!fi.uploadUrl ? "bereits in LFS (Deduplizierung)" : "kein LFS n\xF6tig"}`, ok: true });
3247
+ }
3248
+ const ndjson = [
3249
+ JSON.stringify({ key: "header", value: { summary: `HF Test: LFS commit ${path}`, description: "" } }),
3250
+ JSON.stringify({ key: "lfsFile", value: { path, algo: "sha256", oid, size } })
3251
+ ].join("\n");
3252
+ steps.push(await doFetch("Commit (lfsFile reference)", `${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
3253
+ method: "POST",
3254
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
3255
+ body: ndjson
3256
+ }));
3257
+ return steps;
3258
+ }
3259
+ async function uploadViaHubLib(token, path, bytes, mimeType) {
3260
+ const s = { label: "uploadFile() via @huggingface/hub", method: "import()+call", url: "@huggingface/hub", reqHeaders: {} };
3261
+ const t0 = Date.now();
3262
+ try {
3263
+ const { uploadFile } = await import(
3264
+ /* @vite-ignore */
3265
+ "@huggingface/hub"
3266
+ );
3267
+ await uploadFile({
3268
+ repo: { type: "dataset", name: HF_REPO },
3269
+ credentials: { accessToken: token },
3270
+ file: { path, content: new Blob([bytes], { type: mimeType }) },
3271
+ branch: "main",
3272
+ commitTitle: `HF Test: hub lib upload ${path}`
3273
+ });
3274
+ s.durationMs = Date.now() - t0;
3275
+ s.resBody = "SUCCESS";
3276
+ s.ok = true;
3277
+ } catch (e) {
3278
+ s.durationMs = Date.now() - t0;
3279
+ s.error = String(e?.message ?? e);
3280
+ s.ok = false;
3281
+ }
3282
+ return [s];
3283
+ }
3284
+ async function uploadViaCdnLib(token, path, bytes, mimeType) {
3285
+ const s = { label: "uploadFile() via esm.sh/@huggingface/hub", method: "import()+call", url: "https://esm.sh/@huggingface/hub", reqHeaders: {} };
3286
+ const t0 = Date.now();
3287
+ try {
3288
+ const { uploadFile } = await import(
3289
+ /* @vite-ignore */
3290
+ "https://esm.sh/@huggingface/hub"
3291
+ );
3292
+ await uploadFile({
3293
+ repo: { type: "dataset", name: HF_REPO },
3294
+ credentials: { accessToken: token },
3295
+ file: { path, content: new Blob([bytes], { type: mimeType }) },
3296
+ branch: "main",
3297
+ commitTitle: `HF Test: CDN hub lib upload ${path}`
3298
+ });
3299
+ s.durationMs = Date.now() - t0;
3300
+ s.resBody = "SUCCESS";
3301
+ s.ok = true;
3302
+ } catch (e) {
3303
+ s.durationMs = Date.now() - t0;
3304
+ s.error = String(e?.message ?? e);
3305
+ s.ok = false;
3306
+ }
3307
+ return [s];
3308
+ }
3309
+ async function writeEvent(token, namespace) {
3310
+ const ns = namespace.endsWith("/") ? namespace : namespace ? namespace + "/" : "";
3311
+ const ts = Date.now();
3312
+ const uuid = crypto.randomUUID().slice(0, 8);
3313
+ const isoTs = new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
3314
+ const eventPath = `${ns}${TEST_DIR}/events/${isoTs}_${uuid}.json`;
3315
+ const event = JSON.stringify({ v: { major: 1, minor: 0 }, type: "probe", ts, prevTs: [], clientId: "hf-test-tab", payload: { test: true } }, null, 2);
3316
+ const b64 = toBase64(new TextEncoder().encode(event));
3317
+ const ndjson = [
3318
+ JSON.stringify({ key: "header", value: { summary: "HF Test: write event", description: "" } }),
3319
+ JSON.stringify({ key: "file", value: { path: eventPath, encoding: "base64", content: b64 } })
3320
+ ].join("\n");
3321
+ return [await doFetch(
3322
+ `Event \u2192 ${eventPath}`,
3323
+ `${HF_BASE}/api/datasets/${HF_REPO}/commit/main`,
3324
+ { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" }, body: ndjson }
3325
+ )];
3326
+ }
3327
+ function tryFmt(s) {
3328
+ try {
3329
+ return JSON.stringify(JSON.parse(s), null, 2);
3330
+ } catch {
3331
+ return s;
3332
+ }
3333
+ }
3334
+ function CopyBtn({ text }) {
3335
+ const [done, setDone] = useState15(false);
3336
+ return /* @__PURE__ */ jsxs18(
3337
+ "button",
3338
+ {
3339
+ onClick: () => {
3340
+ navigator.clipboard?.writeText(text).catch(() => {
3341
+ });
3342
+ setDone(true);
3343
+ setTimeout(() => setDone(false), 1500);
3344
+ },
3345
+ style: { background: "none", border: "1px solid rgba(255,255,255,0.15)", borderRadius: 5, color: done ? "#4ade80" : "rgba(255,255,255,0.45)", fontSize: 10, padding: "3px 8px", cursor: "pointer", fontFamily: "inherit", display: "flex", alignItems: "center", gap: 3 },
3346
+ children: [
3347
+ /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 11 }, children: done ? "check" : "content_copy" }),
3348
+ done ? "Kopiert" : "Copy"
3349
+ ]
3350
+ }
3351
+ );
3352
+ }
3353
+ function StepView({ step }) {
3354
+ const isSpecial = step.method === "-" || step.method === "import()" || step.method === "import()+call";
3355
+ return /* @__PURE__ */ jsxs18("div", { style: { marginBottom: 6, background: "rgba(0,0,0,0.3)", borderRadius: 7, padding: "7px 9px", border: `1px solid ${step.ok === false ? "rgba(248,113,113,0.2)" : "rgba(255,255,255,0.05)"}` }, children: [
3356
+ /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }, children: [
3357
+ /* @__PURE__ */ jsx20("span", { style: { fontSize: 11, fontWeight: 700, color: step.ok === false ? "#f87171" : "#4ade80" }, children: step.ok === false ? "\u2717" : "\u2713" }),
3358
+ /* @__PURE__ */ jsx20("span", { style: { fontSize: 11, fontWeight: 600, color: "rgba(255,255,255,0.7)", flex: 1 }, children: step.label }),
3359
+ step.durationMs !== void 0 && /* @__PURE__ */ jsxs18("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)" }, children: [
3360
+ step.durationMs,
3361
+ "ms"
3362
+ ] }),
3363
+ /* @__PURE__ */ jsx20(CopyBtn, { text: JSON.stringify(step, null, 2) })
3364
+ ] }),
3365
+ !isSpecial && /* @__PURE__ */ jsxs18("div", { style: { marginBottom: 5 }, children: [
3366
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.25)", marginBottom: 2 }, children: [
3367
+ "\u2192 ",
3368
+ step.method,
3369
+ " ",
3370
+ step.url
3371
+ ] }),
3372
+ Object.keys(step.reqHeaders).length > 0 && /* @__PURE__ */ jsx20("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.35)", margin: "2px 0", padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 60, overflow: "auto" }, children: Object.entries(step.reqHeaders).map(([k, v]) => `${k}: ${v}`).join("\n") }),
3373
+ step.reqBody && /* @__PURE__ */ jsx20("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.35)", margin: "2px 0", padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 80, overflow: "auto" }, children: step.reqBody })
3374
+ ] }),
3375
+ step.error && /* @__PURE__ */ jsx20("pre", { style: { fontSize: 11, color: "#f87171", margin: 0, padding: "3px 5px", background: "rgba(248,113,113,0.05)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: step.error }),
3376
+ !step.error && (step.resStatus !== void 0 || step.resBody) && /* @__PURE__ */ jsxs18("div", { children: [
3377
+ step.resStatus !== void 0 && /* @__PURE__ */ jsxs18("div", { style: { fontSize: 11, fontWeight: 700, color: (step.resStatus || 0) < 300 ? "#4ade80" : "#f87171", marginBottom: 3 }, children: [
3378
+ "\u2190 ",
3379
+ step.resStatus,
3380
+ " ",
3381
+ step.resStatusText
3382
+ ] }),
3383
+ step.resBody && /* @__PURE__ */ jsx20("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.55)", margin: 0, padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 180, overflow: "auto" }, children: tryFmt(step.resBody) })
3384
+ ] })
3385
+ ] });
3386
+ }
3387
+ function TestCard({
3388
+ id,
3389
+ label,
3390
+ icon,
3391
+ desc,
3392
+ disabled,
3393
+ state,
3394
+ onRun,
3395
+ expanded,
3396
+ onToggle
3397
+ }) {
3398
+ const hasResult = state && state.status !== "idle";
3399
+ return /* @__PURE__ */ jsxs18("div", { style: { marginBottom: 8, background: "rgba(255,255,255,0.03)", borderRadius: 10, border: `1px solid ${state?.status === "ok" ? "rgba(74,222,128,0.15)" : state?.status === "error" ? "rgba(248,113,113,0.15)" : "rgba(255,255,255,0.07)"}`, overflow: "hidden" }, children: [
3400
+ /* @__PURE__ */ jsxs18("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "9px 10px" }, children: [
3401
+ /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18, color: state?.status === "ok" ? "#4ade80" : state?.status === "error" ? "#f87171" : state?.status === "running" ? "#60a5fa" : "rgba(255,255,255,0.35)", flexShrink: 0 }, children: state?.status === "ok" ? "check_circle" : state?.status === "error" ? "error" : state?.status === "running" ? "hourglass_top" : icon }),
3402
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
3403
+ /* @__PURE__ */ jsx20("div", { style: { fontSize: 13, fontWeight: 700, color: "#fff" }, children: label }),
3404
+ /* @__PURE__ */ jsx20("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: desc })
3405
+ ] }),
3406
+ hasResult && state?.status !== "running" && /* @__PURE__ */ jsx20("button", { onClick: onToggle, style: { background: "none", border: "none", color: "rgba(255,255,255,0.3)", cursor: "pointer", padding: 2, lineHeight: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: expanded ? "expand_less" : "expand_more" }) }),
3407
+ /* @__PURE__ */ jsx20(
3408
+ "button",
3409
+ {
3410
+ onClick: onRun,
3411
+ disabled,
3412
+ style: { background: disabled ? "transparent" : "#0284c7", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 6, color: "#fff", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.05em", padding: "5px 10px", cursor: disabled ? "default" : "pointer", opacity: disabled ? 0.35 : 1, fontFamily: "inherit", flexShrink: 0 },
3413
+ children: state?.status === "running" ? "\u2026" : "Run"
3414
+ }
3415
+ )
3416
+ ] }),
3417
+ hasResult && /* @__PURE__ */ jsxs18("div", { style: { borderTop: "1px solid rgba(255,255,255,0.05)" }, children: [
3418
+ /* @__PURE__ */ jsxs18("div", { style: { padding: "4px 10px 5px", display: "flex", alignItems: "center", gap: 8 }, children: [
3419
+ /* @__PURE__ */ jsx20("span", { style: { fontSize: 11, fontWeight: 700, color: state.status === "ok" ? "#4ade80" : "#f87171" }, children: state.status === "ok" ? "\u2713 OK" : state.status === "running" ? "\u2026" : "\u2717 Fehler" }),
3420
+ /* @__PURE__ */ jsxs18("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)" }, children: [
3421
+ state.totalMs,
3422
+ "ms \xB7 ",
3423
+ state.steps.length,
3424
+ " Step",
3425
+ state.steps.length !== 1 ? "s" : ""
3426
+ ] }),
3427
+ /* @__PURE__ */ jsx20("div", { style: { flex: 1 } }),
3428
+ /* @__PURE__ */ jsx20(CopyBtn, { text: JSON.stringify(state, null, 2) })
3429
+ ] }),
3430
+ expanded && state.steps.map((step, i) => /* @__PURE__ */ jsx20("div", { style: { padding: "0 10px 4px" }, children: /* @__PURE__ */ jsx20(StepView, { step }) }, i))
3431
+ ] })
3432
+ ] });
3433
+ }
3434
+ function HFTestTab({ token, namespace, galleryItems }) {
3435
+ const [selected, setSelected] = useState15(null);
3436
+ const [results, setResults] = useState15({});
3437
+ const [expanded, setExpanded] = useState15({});
3438
+ const withResults = galleryItems.filter((g) => g.base64 && g.status === "done");
3439
+ const setRunning = (id) => setResults((r) => ({ ...r, [id]: { status: "running", steps: [], totalMs: 0 } }));
3440
+ const setDone = (id, steps, t0) => {
3441
+ const ok = steps.length > 0 && steps.every((s) => s.ok !== false);
3442
+ setResults((r) => ({ ...r, [id]: { status: ok ? "ok" : "error", steps, totalMs: Date.now() - t0 } }));
3443
+ setExpanded((e) => ({ ...e, [id]: true }));
3444
+ };
3445
+ const run = async (id) => {
3446
+ const isImgTest = id !== "event";
3447
+ if (isImgTest && !selected?.base64) return;
3448
+ setRunning(id);
3449
+ const t0 = Date.now();
3450
+ let steps = [];
3451
+ try {
3452
+ if (isImgTest) {
3453
+ const bytes = b64ToBytes(selected.base64);
3454
+ const imgPath = `${TEST_DIR}/${selected.id}.jpg`;
3455
+ switch (id) {
3456
+ case "img-direct":
3457
+ steps = await uploadDirect(token, imgPath, rawB64(selected.base64));
3458
+ break;
3459
+ case "img-lfs":
3460
+ steps = await uploadLFS(token, imgPath, bytes);
3461
+ break;
3462
+ case "img-hub":
3463
+ steps = await uploadViaHubLib(token, imgPath, bytes, "image/jpeg");
3464
+ break;
3465
+ case "img-cdn":
3466
+ steps = await uploadViaCdnLib(token, imgPath, bytes, "image/jpeg");
3467
+ break;
3468
+ }
3469
+ } else {
3470
+ steps = await writeEvent(token, namespace);
3471
+ }
3472
+ } catch (e) {
3473
+ steps = [{ label: "Unexpected error", method: "-", url: "-", reqHeaders: {}, error: String(e?.message ?? e), ok: false }];
3474
+ }
3475
+ setDone(id, steps, t0);
3476
+ };
3477
+ const noToken = !token;
3478
+ const noImg = !selected;
3479
+ const imgTests = [
3480
+ { id: "img-direct", label: "Upload Direct", icon: "upload_file", desc: "POST commit + file+base64 (kein LFS)" },
3481
+ { id: "img-lfs", label: "Upload LFS", icon: "cloud_upload", desc: "preupload \u2192 PUT LFS \u2192 verify \u2192 commit" },
3482
+ { id: "img-hub", label: "Upload hub lib", icon: "package_2", desc: "uploadFile() via @huggingface/hub" },
3483
+ { id: "img-cdn", label: "Upload CDN lib", icon: "language", desc: "uploadFile() via esm.sh hub lib" }
3484
+ ];
3485
+ return /* @__PURE__ */ jsxs18("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflowY: "auto", padding: "12px 10px 80px", boxSizing: "border-box", fontFamily: "inherit" }, children: [
3486
+ noToken && /* @__PURE__ */ jsx20("div", { style: { marginBottom: 10, padding: "8px 12px", background: "rgba(248,113,113,0.08)", borderRadius: 8, border: "1px solid rgba(248,113,113,0.2)", fontSize: 12, color: "#f87171" }, children: "Kein HF-Token geladen \u2014 bitte zuerst Token im Sync-Tab eingeben." }),
3487
+ /* @__PURE__ */ jsxs18("div", { style: { marginBottom: 14 }, children: [
3488
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 8 }, children: [
3489
+ "Bild ausw\xE4hlen (",
3490
+ withResults.length,
3491
+ " verf\xFCgbar)"
3492
+ ] }),
3493
+ withResults.length === 0 ? /* @__PURE__ */ jsx20("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.3)", fontStyle: "italic" }, children: "Noch keine Bilder in der Galerie. Generiere zuerst ein Bild oder lade von HF." }) : /* @__PURE__ */ jsx20("div", { style: { display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }, children: withResults.slice(0, 12).map((g) => /* @__PURE__ */ jsx20(
3494
+ "button",
3495
+ {
3496
+ onClick: () => setSelected(g),
3497
+ style: { padding: 0, border: `2px solid ${selected?.id === g.id ? "#0284c7" : "transparent"}`, borderRadius: 6, cursor: "pointer", overflow: "hidden", background: "none", lineHeight: 0 },
3498
+ children: /* @__PURE__ */ jsx20(
3499
+ "img",
3500
+ {
3501
+ src: g.base64,
3502
+ alt: g.prompt || g.id,
3503
+ style: { width: "100%", aspectRatio: "1", objectFit: "cover", display: "block", borderRadius: 4 }
3504
+ }
3505
+ )
3506
+ },
3507
+ g.id
3508
+ )) }),
3509
+ selected && /* @__PURE__ */ jsxs18("div", { style: { marginTop: 10, display: "flex", gap: 10, alignItems: "flex-start" }, children: [
3510
+ /* @__PURE__ */ jsx20(
3511
+ "img",
3512
+ {
3513
+ src: selected.base64,
3514
+ style: { width: 80, height: 80, objectFit: "cover", borderRadius: 8, border: "1px solid rgba(255,255,255,0.1)", flexShrink: 0 }
3515
+ }
3516
+ ),
3517
+ /* @__PURE__ */ jsxs18("div", { style: { flex: 1, minWidth: 0 }, children: [
3518
+ /* @__PURE__ */ jsx20("div", { style: { fontSize: 11, fontWeight: 700, color: "rgba(255,255,255,0.7)", marginBottom: 2 }, children: "Ausgew\xE4hlt" }),
3519
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", wordBreak: "break-all" }, children: [
3520
+ "ID: ",
3521
+ selected.id
3522
+ ] }),
3523
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 2 }, children: [
3524
+ "Ziel: test/",
3525
+ selected.id,
3526
+ ".jpg"
3527
+ ] }),
3528
+ selected.prompt && /* @__PURE__ */ jsx20("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.25)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selected.prompt })
3529
+ ] })
3530
+ ] })
3531
+ ] }),
3532
+ /* @__PURE__ */ jsxs18("div", { style: { marginBottom: 18 }, children: [
3533
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8, borderBottom: "1px solid rgba(255,255,255,0.06)", paddingBottom: 4 }, children: [
3534
+ "Bild hochladen \u2192 test/",
3535
+ "{",
3536
+ "id",
3537
+ "}",
3538
+ ".jpg"
3539
+ ] }),
3540
+ imgTests.map((t) => /* @__PURE__ */ jsx20(
3541
+ TestCard,
3542
+ {
3543
+ id: t.id,
3544
+ label: t.label,
3545
+ icon: t.icon,
3546
+ desc: t.desc,
3547
+ disabled: noToken || noImg || results[t.id]?.status === "running",
3548
+ state: results[t.id],
3549
+ onRun: () => run(t.id),
3550
+ expanded: !!expanded[t.id],
3551
+ onToggle: () => setExpanded((e) => ({ ...e, [t.id]: !e[t.id] }))
3552
+ },
3553
+ t.id
3554
+ ))
3555
+ ] }),
3556
+ /* @__PURE__ */ jsxs18("div", { children: [
3557
+ /* @__PURE__ */ jsxs18("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8, borderBottom: "1px solid rgba(255,255,255,0.06)", paddingBottom: 4 }, children: [
3558
+ "Event schreiben \u2192 ",
3559
+ namespace || "(kein namespace)",
3560
+ "test/events/"
3561
+ ] }),
3562
+ /* @__PURE__ */ jsx20(
3563
+ TestCard,
3564
+ {
3565
+ id: "event",
3566
+ label: "Write Event",
3567
+ icon: "bolt",
3568
+ desc: "Kleines JSON-Event hochladen (direct commit)",
3569
+ disabled: noToken || results["event"]?.status === "running",
3570
+ state: results["event"],
3571
+ onRun: () => run("event"),
3572
+ expanded: !!expanded["event"],
3573
+ onToggle: () => setExpanded((e) => ({ ...e, event: !e.event }))
3574
+ }
3575
+ )
3576
+ ] })
3577
+ ] });
3578
+ }
3579
+
2894
3580
  // src/components/AvatarArchitectApp.tsx
2895
- 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(() => {
3581
+ import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
3582
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
3583
+ useEffect6(() => {
2898
3584
  const id = "flow-styles";
2899
3585
  if (!document.getElementById(id)) {
2900
3586
  const style = document.createElement("style");
@@ -2903,22 +3589,92 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2903
3589
  document.head.appendChild(style);
2904
3590
  }
2905
3591
  }, []);
2906
- const [showStart, setShowStart] = useState14(true);
2907
- const [layoutChoice, setLayoutChoice] = useState14(() => {
3592
+ const [showStart, setShowStart] = useState16(true);
3593
+ const [layoutChoice, setLayoutChoice] = useState16(() => {
2908
3594
  try {
2909
3595
  return localStorage.getItem("aa-layout") || null;
2910
3596
  } catch {
2911
3597
  return null;
2912
3598
  }
2913
3599
  });
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);
3600
+ const [projectLoaded, setProjectLoaded] = useState16(false);
3601
+ const [hfToken, setHfToken] = useState16(initialHfToken || "");
3602
+ const [hfTokenInput, setHfTokenInput] = useState16(initialHfToken || "");
3603
+ const [isLoadingFromHF, setIsLoadingFromHF] = useState16(false);
3604
+ const [hfNamespaceLocal, setHfNamespaceLocal] = useState16(() => {
3605
+ try {
3606
+ const stored = localStorage.getItem("aa-hf-namespace");
3607
+ if (stored !== null) return stored;
3608
+ return allowDevNamespace ? "app.art-by-rolands.de/" : "";
3609
+ } catch {
3610
+ return "";
3611
+ }
3612
+ });
3613
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState16(null);
3614
+ useEffect6(() => {
3615
+ if (hfNamespace !== void 0) return;
3616
+ const backendUrl = typeof window !== "undefined" ? window.BACKEND_URL || window.location.origin : null;
3617
+ if (!backendUrl) return;
3618
+ fetch(`${backendUrl}/api/status`).then((r) => r.json()).then((d) => {
3619
+ if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
3620
+ }).catch(() => {
3621
+ });
3622
+ }, [hfNamespace]);
3623
+ const effectiveNamespace = hfNamespace ?? hfNamespaceFromServer ?? hfNamespaceLocal;
3624
+ const {
3625
+ state: hfState,
3626
+ isLoading: isHfRefreshing,
3627
+ pendingBufferCount,
3628
+ eventCount,
3629
+ writeEvent: hfWriteEvent,
3630
+ refresh: refreshHF,
3631
+ hasStateZip
3632
+ } = useHFState(hfToken, effectiveNamespace);
3633
+ const [bootstrapLog, setBootstrapLog] = useState16([]);
3634
+ const [isBootstrapping, setIsBootstrapping] = useState16(false);
3635
+ const syncTopSlot = /* @__PURE__ */ jsxs19(Fragment9, { children: [
3636
+ pendingBufferCount > 0 && /* @__PURE__ */ jsxs19("div", { style: { background: "linear-gradient(90deg,#f59e0b,#ef4444)", padding: "4px 10px", fontSize: 11, color: "#fff", borderRadius: 4, marginBottom: 4 }, children: [
3637
+ pendingBufferCount,
3638
+ " \xC4nderung",
3639
+ pendingBufferCount > 1 ? "en" : "",
3640
+ " lokal \u2014 bei Flow-Reload verloren wenn nicht synchronisiert"
3641
+ ] }),
3642
+ eventCount > 100 && /* @__PURE__ */ jsxs19("div", { style: { background: "#dc2626", color: "#fff", padding: "5px 10px", borderRadius: 4, marginBottom: 4, fontWeight: 600, fontSize: 11 }, children: [
3643
+ "\u26A0 ",
3644
+ eventCount,
3645
+ " Events nicht konsolidiert \u2014 Konsolidierung dringend empfohlen"
3646
+ ] }),
3647
+ eventCount > 50 && eventCount <= 100 && /* @__PURE__ */ jsxs19("div", { style: { background: "#44403c", color: "#a8a29e", padding: "4px 10px", borderRadius: 4, marginBottom: 4, fontSize: 11 }, children: [
3648
+ eventCount,
3649
+ " Events seit letzter Konsolidierung \u2014 Konsolidierung empfohlen"
3650
+ ] }),
3651
+ hfToken && !hasStateZip && !isHfRefreshing && /* @__PURE__ */ jsxs19("div", { style: { background: "#1c1917", border: "1px solid #44403c", borderRadius: 6, padding: "10px 12px" }, children: [
3652
+ /* @__PURE__ */ jsx21("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" }),
3653
+ /* @__PURE__ */ jsx21(
3654
+ "button",
3655
+ {
3656
+ disabled: isBootstrapping || !effectiveNamespace,
3657
+ onClick: async () => {
3658
+ setIsBootstrapping(true);
3659
+ setBootstrapLog([]);
3660
+ try {
3661
+ await hfBootstrapFromLegacy(effectiveNamespace, hfToken, (msg) => setBootstrapLog((prev) => [...prev, msg]));
3662
+ setBootstrapLog((prev) => [...prev, "\u2713 Fertig"]);
3663
+ setTimeout(() => refreshHF(), 1500);
3664
+ } catch (e) {
3665
+ setBootstrapLog((prev) => [...prev, `Fehler: ${e.message}`]);
3666
+ } finally {
3667
+ setIsBootstrapping(false);
3668
+ }
3669
+ },
3670
+ 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 },
3671
+ children: isBootstrapping ? "Migriere\u2026" : "Legacy-Migration starten"
3672
+ }
3673
+ ),
3674
+ bootstrapLog.length > 0 && /* @__PURE__ */ jsx21("div", { style: { marginTop: 6, fontFamily: "monospace", fontSize: 10, color: "#78716c", lineHeight: 1.6 }, children: bootstrapLog.map((l, i) => /* @__PURE__ */ jsx21("div", { children: l }, i)) })
3675
+ ] })
3676
+ ] });
3677
+ const wsInputRef = useRef7(null);
2922
3678
  const startApp = (choice) => {
2923
3679
  try {
2924
3680
  localStorage.setItem("aa-layout", choice);
@@ -2927,70 +3683,110 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2927
3683
  setLayoutChoice(choice);
2928
3684
  setShowStart(false);
2929
3685
  };
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(() => {
3686
+ const [nodes, setNodes] = useState16([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
3687
+ const [edges, setEdges] = useState16([]);
3688
+ const [history, setHistory] = useState16([]);
3689
+ const [galleryItems, setGalleryItems] = useState16([]);
3690
+ const galleryItemsRef = useRef7([]);
3691
+ useEffect6(() => {
2936
3692
  galleryItemsRef.current = galleryItems;
2937
3693
  }, [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(() => {
3694
+ const hfImageNotFoundRef = useRef7(/* @__PURE__ */ new Set());
3695
+ useEffect6(() => {
3696
+ if (!hfState) return;
3697
+ if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
3698
+ const hfIds = new Set(hfState.metadata.map((m) => m.id));
3699
+ const skeletons = hfState.metadata.map((m) => ({
3700
+ id: m.id,
3701
+ nodeId: m.id,
3702
+ prompt: m.prompt,
3703
+ seed: m.seed,
3704
+ model: m.model,
3705
+ tags: m.tags || [],
3706
+ timestamp: m.timestamp,
3707
+ status: "done"
3708
+ }));
3709
+ setGalleryItems((prev) => {
3710
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3711
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3712
+ return [...localOnly, ...merged];
3713
+ });
3714
+ setHistory((prev) => {
3715
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
3716
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
3717
+ return [...localOnly, ...merged];
3718
+ });
3719
+ for (const entry of hfState.metadata) {
3720
+ if (hfImageNotFoundRef.current.has(entry.id)) continue;
3721
+ hfLoadImageAsBase64(entry.id, hfToken).then((b64) => {
3722
+ if (!b64) {
3723
+ hfImageNotFoundRef.current.add(entry.id);
3724
+ return;
3725
+ }
3726
+ const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
3727
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3728
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
3729
+ }).catch(() => {
3730
+ hfImageNotFoundRef.current.add(entry.id);
3731
+ });
3732
+ }
3733
+ }, [hfState]);
3734
+ const [activePrompt, setActivePrompt] = useState16("");
3735
+ const [isSynthesizing, setIsSynthesizing] = useState16(false);
3736
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState16(0);
3737
+ const [currentResult, setCurrentResult] = useState16(null);
3738
+ const [focusedNodeId, setFocusedNodeId] = useState16(null);
3739
+ const [leftTab, setLeftTab] = useState16("prompt");
3740
+ const [promptFeedback, setPromptFeedback] = useState16(null);
3741
+ const [lastPromptPayload, setLastPromptPayload] = useState16(null);
3742
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState16(false);
3743
+ const [activeTab, setActiveTab] = useState16("history");
3744
+ const [mobileTab, setMobileTab] = useState16("stage");
3745
+ const [middlePanel, setMiddlePanel] = useState16("stage");
3746
+ const [recentLabItems, setRecentLabItems] = useState16([]);
3747
+ const [aspectRatio, setAspectRatio] = useState16("1:1");
3748
+ const [selectedModel, setSelectedModel] = useState16("\u{1F34C} Nano Banana Pro");
3749
+ const [seed, setSeed] = useState16(Math.floor(Math.random() * 1e6));
3750
+ const [seedMode, setSeedMode] = useState16("random");
3751
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState16(false);
3752
+ const [isRightCollapsed, setIsRightCollapsed] = useState16(false);
3753
+ const [leftPanelWidth, setLeftPanelWidth] = useState16(() => {
2958
3754
  try {
2959
3755
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
2960
3756
  } catch {
2961
3757
  return 260;
2962
3758
  }
2963
3759
  });
2964
- const [rightPanelWidth, setRightPanelWidth] = useState14(() => {
3760
+ const [rightPanelWidth, setRightPanelWidth] = useState16(() => {
2965
3761
  try {
2966
3762
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
2967
3763
  } catch {
2968
3764
  return 320;
2969
3765
  }
2970
3766
  });
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(() => {
3767
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState16(false);
3768
+ const [projectActionState, setProjectActionState] = useState16("idle");
3769
+ const syncServerDataRef = useRef7(null);
3770
+ const [workspaceTags, setWorkspaceTags] = useState16(null);
3771
+ const [serverProjects, setServerProjects] = useState16([]);
3772
+ const [isLoadingFromServer, setIsLoadingFromServer] = useState16(false);
3773
+ const [highContrast, setHighContrast] = useState16(() => {
2978
3774
  try {
2979
3775
  return localStorage.getItem("aa-contrast") === "high";
2980
3776
  } catch {
2981
3777
  return false;
2982
3778
  }
2983
3779
  });
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);
3780
+ const [activeReferenceId, setActiveReferenceId] = useState16(null);
3781
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState16(null);
3782
+ const [isScanningImage, setIsScanningImage] = useState16(false);
3783
+ const [touchStartX, setTouchStartX] = useState16(null);
3784
+ const [isFullscreen, setIsFullscreen] = useState16(false);
3785
+ const [zoomScale, setZoomScale] = useState16(1);
3786
+ const [zoomOffset, setZoomOffset] = useState16({ x: 0, y: 0 });
3787
+ const lastPinchDist = useRef7(null);
3788
+ const lastTapTime = useRef7(0);
3789
+ const dragStart = useRef7(null);
2994
3790
  const openFullscreen = () => {
2995
3791
  setIsFullscreen(true);
2996
3792
  setZoomScale(1);
@@ -3134,16 +3930,16 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3134
3930
  }
3135
3931
  };
3136
3932
  const currentIndex = useMemo2(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
3137
- const goToPrev = useCallback(() => {
3933
+ const goToPrev = useCallback2(() => {
3138
3934
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
3139
3935
  }, [currentIndex, history]);
3140
- const goToNext = useCallback(() => {
3936
+ const goToNext = useCallback2(() => {
3141
3937
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
3142
3938
  }, [currentIndex, history]);
3143
3939
  const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
3144
3940
  const isGenerating = activeGenerationsCount > 0;
3145
3941
  useKeyboardNavigation(history, currentResult, setCurrentResult);
3146
- const getSubtreeFormat = useCallback((nodeId, depth = 0) => {
3942
+ const getSubtreeFormat = useCallback2((nodeId, depth = 0) => {
3147
3943
  const node = nodes.find((n) => n.id === nodeId);
3148
3944
  if (!node) return "";
3149
3945
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -3183,7 +3979,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3183
3979
  if (prev.id === genId || !options.silent) return finishedGen;
3184
3980
  return prev;
3185
3981
  });
3186
- if (hfToken && base64) {
3982
+ console.log("[HF] handleGenerateImage \u2014 condition check:", { hfToken: !!hfToken, base64: !!base64, effectiveNamespace });
3983
+ if (hfToken && base64 && effectiveNamespace) {
3984
+ hfUploadImage(base64, genId, hfToken).catch((e) => {
3985
+ console.error("[HF] hfUploadImage failed:", e);
3986
+ });
3187
3987
  const entry = {
3188
3988
  id: genId,
3189
3989
  prompt: promptToUse || void 0,
@@ -3193,20 +3993,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3193
3993
  timestamp: Date.now(),
3194
3994
  mimeType: "image/jpeg"
3195
3995
  };
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
- }
3996
+ console.log("[HF] calling hfWriteEvent, namespace:", effectiveNamespace);
3997
+ hfWriteEvent("image_added", entry).catch((e) => {
3998
+ console.error("[HF] hfWriteEvent outer catch:", e);
3210
3999
  });
3211
4000
  }
3212
4001
  } catch (err) {
@@ -3242,6 +4031,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3242
4031
  all: [...prev.all, { ...newTagOption, category: tag.category }]
3243
4032
  };
3244
4033
  });
4034
+ if (hfToken && effectiveNamespace) {
4035
+ const p = { category: tag.category, label: tag.label, value: tag.value, is_user_created: true };
4036
+ hfWriteEvent("tag_upserted", p).catch(() => {
4037
+ });
4038
+ }
3245
4039
  };
3246
4040
  const handleTagUpdate = (originalLabel, originalCategory, updates) => {
3247
4041
  setWorkspaceTags((prev) => {
@@ -3254,8 +4048,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3254
4048
  );
3255
4049
  return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
3256
4050
  });
4051
+ if (hfToken && effectiveNamespace) {
4052
+ const p = { category: originalCategory, label: updates.label, value: updates.value, is_user_created: true };
4053
+ hfWriteEvent("tag_upserted", p).catch(() => {
4054
+ });
4055
+ }
3257
4056
  };
3258
4057
  const handleTagDelete = (label, category) => {
4058
+ const tagValue = workspaceTags?.by_category[category]?.find((t) => t.label === label)?.value;
3259
4059
  setWorkspaceTags((prev) => {
3260
4060
  if (!prev) return prev;
3261
4061
  const updatedCat = (prev.by_category[category] || []).map(
@@ -3266,6 +4066,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3266
4066
  );
3267
4067
  return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
3268
4068
  });
4069
+ if (hfToken && effectiveNamespace && tagValue) {
4070
+ const p = { category, label, value: tagValue, is_deleted: true };
4071
+ hfWriteEvent("tag_upserted", p).catch(() => {
4072
+ });
4073
+ }
3269
4074
  };
3270
4075
  const handleTagReorder = (category, reorderedTags) => {
3271
4076
  setWorkspaceTags((prev) => {
@@ -3415,38 +4220,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3415
4220
  await fetchServerProjects();
3416
4221
  };
3417
4222
  const handleHfInitialSync = async (onProgress) => {
3418
- if (!hfToken) return;
3419
- const gens = galleryItems.filter((g) => g.base64 && g.status === "done");
4223
+ if (!hfToken || !effectiveNamespace) return;
4224
+ const existingIds = new Set((hfState?.metadata || []).map((m) => m.id));
4225
+ const gens = galleryItems.filter((g) => g.base64 && g.status === "done" && !existingIds.has(g.id));
3420
4226
  const total = gens.length;
3421
4227
  onProgress(0, total);
3422
4228
  let done = 0;
3423
4229
  for (const gen of gens) {
3424
4230
  const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3425
4231
  const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3426
- await hfUploadImage(raw, gen.id, hfToken, mimeType);
4232
+ await hfUploadImage(raw, gen.id, hfToken, mimeType).catch(() => {
4233
+ });
4234
+ const entry = {
4235
+ id: gen.id,
4236
+ prompt: gen.prompt || void 0,
4237
+ seed: gen.seed,
4238
+ model: gen.model,
4239
+ tags: gen.tags || [],
4240
+ timestamp: gen.timestamp,
4241
+ mimeType
4242
+ };
4243
+ await hfWriteEvent("image_added", entry).catch(() => {
4244
+ });
3427
4245
  done++;
3428
4246
  onProgress(done, total);
3429
4247
  }
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
4248
  if (workspaceTags) {
3446
- const remoteTags = await hfLoadTags(hfToken).catch(() => null);
3447
- const mergedTags = mergeWorkspaceTags(workspaceTags, remoteTags);
3448
- await hfSaveTags(mergedTags, hfToken);
3449
- setWorkspaceTags(mergedTags);
4249
+ for (const [category, tags] of Object.entries(workspaceTags.by_category)) {
4250
+ for (const tag of tags) {
4251
+ const p = { category, label: tag.label, value: tag.value, is_user_created: tag.is_user_created, is_deleted: tag.is_deleted };
4252
+ await hfWriteEvent("tag_upserted", p).catch(() => {
4253
+ });
4254
+ }
4255
+ }
3450
4256
  }
3451
4257
  };
3452
4258
  const handleComputeSyncDiff = async () => {
@@ -3485,87 +4291,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3485
4291
  setTimeout(() => setProjectActionState("idle"), 4e3);
3486
4292
  }
3487
4293
  };
3488
- useEffect5(() => {
4294
+ useEffect6(() => {
3489
4295
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
3490
4296
  }, [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
4297
  const mergeWorkspaceTags = (local, remote) => {
3570
4298
  if (!remote?.by_category) return local;
3571
4299
  const merged = {};
@@ -3585,25 +4313,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3585
4313
  const all = Object.entries(merged).flatMap(([cat, tags]) => tags.map((t) => ({ ...t, category: cat })));
3586
4314
  return { by_category: merged, all };
3587
4315
  };
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
4316
  if (isFullscreen && currentResult?.base64) {
3605
4317
  const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
3606
- return /* @__PURE__ */ jsxs18(
4318
+ return /* @__PURE__ */ jsxs19(
3607
4319
  "div",
3608
4320
  {
3609
4321
  className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
@@ -3611,7 +4323,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3611
4323
  onTouchMove: handleFsTouchMove,
3612
4324
  onTouchEnd: handleFsTouchEnd,
3613
4325
  children: [
3614
- /* @__PURE__ */ jsx20(
4326
+ /* @__PURE__ */ jsx21(
3615
4327
  "img",
3616
4328
  {
3617
4329
  src: fsBase64,
@@ -3628,77 +4340,77 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3628
4340
  }
3629
4341
  }
3630
4342
  ),
3631
- /* @__PURE__ */ jsx20("button", { onClick: closeFullscreen, className: "absolute top-4 right-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
3632
- zoomScale > 1 && /* @__PURE__ */ jsx20("button", { onClick: () => {
4343
+ /* @__PURE__ */ jsx21("button", { onClick: closeFullscreen, className: "absolute top-4 right-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
4344
+ zoomScale > 1 && /* @__PURE__ */ jsx21("button", { onClick: () => {
3633
4345
  setZoomScale(1);
3634
4346
  setZoomOffset({ x: 0, y: 0 });
3635
- }, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
3636
- history.length > 1 && /* @__PURE__ */ jsxs18(Fragment9, { children: [
3637
- /* @__PURE__ */ jsx20("button", { onClick: () => {
4347
+ }, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
4348
+ history.length > 1 && /* @__PURE__ */ jsxs19(Fragment9, { children: [
4349
+ /* @__PURE__ */ jsx21("button", { onClick: () => {
3638
4350
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
3639
- }, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
3640
- /* @__PURE__ */ jsx20("button", { onClick: () => {
4351
+ }, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
4352
+ /* @__PURE__ */ jsx21("button", { onClick: () => {
3641
4353
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
3642
- }, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
3643
- /* @__PURE__ */ jsxs18("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
4354
+ }, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
4355
+ /* @__PURE__ */ jsxs19("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
3644
4356
  currentIndex + 1,
3645
4357
  " / ",
3646
4358
  history.length
3647
4359
  ] })
3648
4360
  ] }),
3649
- zoomScale === 1 && /* @__PURE__ */ jsx20("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
4361
+ zoomScale === 1 && /* @__PURE__ */ jsx21("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
3650
4362
  ]
3651
4363
  }
3652
4364
  );
3653
4365
  }
3654
4366
  if (showStart) {
3655
- return /* @__PURE__ */ jsxs18("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
3656
- /* @__PURE__ */ jsx20("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
4367
+ return /* @__PURE__ */ jsxs19("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
4368
+ /* @__PURE__ */ jsx21("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
3657
4369
  const f = e.target.files?.[0];
3658
4370
  if (f) handleProjectImport(f);
3659
4371
  e.target.value = "";
3660
4372
  } }),
3661
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-1", children: [
3662
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
3663
- /* @__PURE__ */ jsx20("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
3664
- /* @__PURE__ */ jsxs18("span", { className: "text-white text-[13px] font-mono", children: [
4373
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-1", children: [
4374
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
4375
+ /* @__PURE__ */ jsx21("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
4376
+ /* @__PURE__ */ jsxs19("span", { className: "text-white text-[13px] font-mono", children: [
3665
4377
  "v",
3666
4378
  LIB_VERSION
3667
4379
  ] })
3668
4380
  ] }),
3669
- /* @__PURE__ */ jsxs18(
4381
+ /* @__PURE__ */ jsxs19(
3670
4382
  "button",
3671
4383
  {
3672
4384
  onClick: toggleContrast,
3673
4385
  className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
3674
4386
  style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
3675
4387
  children: [
3676
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
3677
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-start", children: [
3678
- /* @__PURE__ */ jsx20("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
3679
- /* @__PURE__ */ jsx20("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
4388
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
4389
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-start", children: [
4390
+ /* @__PURE__ */ jsx21("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
4391
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
3680
4392
  ] })
3681
4393
  ]
3682
4394
  }
3683
4395
  ),
3684
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
3685
- /* @__PURE__ */ jsxs18(
4396
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4397
+ /* @__PURE__ */ jsxs19(
3686
4398
  "button",
3687
4399
  {
3688
4400
  onClick: () => wsInputRef.current?.click(),
3689
4401
  className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform",
3690
4402
  style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
3691
4403
  children: [
3692
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
4404
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
3693
4405
  projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
3694
4406
  ]
3695
4407
  }
3696
4408
  ),
3697
- !projectLoaded && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
4409
+ !projectLoaded && /* @__PURE__ */ jsx21("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
3698
4410
  ] }),
3699
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
3700
- !initialHfToken && /* @__PURE__ */ jsxs18("div", { className: "flex gap-2 w-full", children: [
3701
- /* @__PURE__ */ jsx20(
4411
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4412
+ !initialHfToken && /* @__PURE__ */ jsxs19("div", { className: "flex gap-2 w-full", children: [
4413
+ /* @__PURE__ */ jsx21(
3702
4414
  "input",
3703
4415
  {
3704
4416
  type: "password",
@@ -3714,7 +4426,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3714
4426
  style: { height: 44, background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.7)" }
3715
4427
  }
3716
4428
  ),
3717
- hfTokenInput.trim() && /* @__PURE__ */ jsx20(
4429
+ hfTokenInput.trim() && /* @__PURE__ */ jsx21(
3718
4430
  "button",
3719
4431
  {
3720
4432
  type: "button",
@@ -3725,14 +4437,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3725
4437
  }
3726
4438
  )
3727
4439
  ] }),
3728
- hfToken && /* @__PURE__ */ jsxs18(
4440
+ hfToken && /* @__PURE__ */ jsxs19(
3729
4441
  "button",
3730
4442
  {
3731
4443
  disabled: isLoadingFromHF,
3732
4444
  onClick: async () => {
3733
4445
  setIsLoadingFromHF(true);
3734
4446
  try {
3735
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-B62RV5K3.mjs");
4447
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-6YYT6ATO.mjs");
3736
4448
  const projects = await hfListProjects2(hfToken);
3737
4449
  if (projects.length > 0) {
3738
4450
  const file = await hfDownloadProject2(projects[0].path, hfToken);
@@ -3747,15 +4459,15 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3747
4459
  className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform disabled:opacity-50",
3748
4460
  style: { height: 56, background: "#f59e0b" },
3749
4461
  children: [
3750
- /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromHF ? " animate-spin" : ""}`, children: isLoadingFromHF ? "sync" : "cloud_download" }),
4462
+ /* @__PURE__ */ jsx21("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromHF ? " animate-spin" : ""}`, children: isLoadingFromHF ? "sync" : "cloud_download" }),
3751
4463
  isLoadingFromHF ? "Laden\u2026" : "Von HF laden"
3752
4464
  ]
3753
4465
  }
3754
4466
  ),
3755
- hfToken && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand von Hugging Face laden" })
4467
+ hfToken && /* @__PURE__ */ jsx21("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand von Hugging Face laden" })
3756
4468
  ] }),
3757
- onFetchServerProjects && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
3758
- /* @__PURE__ */ jsxs18(
4469
+ onFetchServerProjects && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4470
+ /* @__PURE__ */ jsxs19(
3759
4471
  "button",
3760
4472
  {
3761
4473
  disabled: isLoadingFromServer,
@@ -3776,35 +4488,57 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3776
4488
  className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform disabled:opacity-50",
3777
4489
  style: { height: 56, background: "#7c3aed" },
3778
4490
  children: [
3779
- /* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
4491
+ /* @__PURE__ */ jsx21("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
3780
4492
  isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
3781
4493
  ]
3782
4494
  }
3783
4495
  ),
3784
- /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
4496
+ /* @__PURE__ */ jsx21("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
3785
4497
  ] }),
3786
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
3787
- /* @__PURE__ */ jsx20("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
3788
- /* @__PURE__ */ jsx20("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
4498
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4499
+ /* @__PURE__ */ jsx21("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
4500
+ /* @__PURE__ */ jsx21("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
3789
4501
  { id: "mobile", icon: "smartphone", label: "Mobile" },
3790
4502
  { id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
3791
4503
  { id: "desktop", icon: "desktop_windows", label: "Desktop" },
3792
4504
  { id: "tablet-landscape", icon: "tablet", label: "Landscape" }
3793
- ].map((opt) => /* @__PURE__ */ jsxs18(
4505
+ ].map((opt) => /* @__PURE__ */ jsxs19(
3794
4506
  "button",
3795
4507
  {
3796
4508
  onClick: () => startApp(opt.id),
3797
4509
  className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
3798
4510
  style: { borderColor: layoutChoice === opt.id ? "rgba(255,255,255,0.35)" : "rgba(255,255,255,0.08)", background: layoutChoice === opt.id ? "rgba(255,255,255,0.07)" : "transparent" },
3799
4511
  children: [
3800
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
3801
- /* @__PURE__ */ jsx20("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
4512
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
4513
+ /* @__PURE__ */ jsx21("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
3802
4514
  ]
3803
4515
  },
3804
4516
  opt.id
3805
4517
  )) }),
3806
- layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
3807
- 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" })
4518
+ layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx21("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
4519
+ layoutChoice === "tablet-landscape" && /* @__PURE__ */ jsx21("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
4520
+ ] }),
4521
+ !hfNamespace && !hfNamespaceFromServer && /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-3 w-full max-w-[280px]", children: [
4522
+ /* @__PURE__ */ jsx21("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "State:" }),
4523
+ ["app.art-by-rolands.de/", "dev-app.art-by-rolands.de/"].map((ns, i) => /* @__PURE__ */ jsx21(
4524
+ "button",
4525
+ {
4526
+ onClick: () => {
4527
+ setHfNamespaceLocal(ns);
4528
+ try {
4529
+ localStorage.setItem("aa-hf-namespace", ns);
4530
+ } catch {
4531
+ }
4532
+ },
4533
+ className: "px-3 py-1 rounded-lg text-[11px] font-bold transition-colors",
4534
+ style: {
4535
+ background: hfNamespaceLocal === ns ? "#e7e5e4" : "#44403c",
4536
+ color: hfNamespaceLocal === ns ? "#1c1917" : "#e7e5e4"
4537
+ },
4538
+ children: i === 0 ? "PROD" : "DEV"
4539
+ },
4540
+ ns
4541
+ ))
3808
4542
  ] })
3809
4543
  ] });
3810
4544
  }
@@ -3813,21 +4547,21 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3813
4547
  const mdScale = mdMode ? window.innerWidth / 430 : 1;
3814
4548
  const mdW = mdMode ? 430 : void 0;
3815
4549
  const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
3816
- const mobileRoot = /* @__PURE__ */ jsxs18("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
4550
+ const mobileRoot = /* @__PURE__ */ jsxs19("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
3817
4551
  width: mdMode ? mdW : "100vw",
3818
4552
  height: mdMode ? mdH : "100dvh",
3819
4553
  transform: mdMode ? `scale(${mdScale})` : void 0,
3820
4554
  transformOrigin: mdMode ? "top left" : void 0,
3821
4555
  ...hcStyle || {}
3822
4556
  }, children: [
3823
- mobileTab === "labs" && /* @__PURE__ */ jsx20("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx20(LabsTab, { services: labServices, onResult: (item) => {
4557
+ mobileTab === "labs" && /* @__PURE__ */ jsx21("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx21(LabsTab, { services: labServices, onResult: (item) => {
3824
4558
  const frame = item.frames[0];
3825
4559
  if (frame?.base64) {
3826
4560
  setCurrentResult(frameToGeneration(frame, item));
3827
4561
  setMobileTab("stage");
3828
4562
  }
3829
4563
  } }) }),
3830
- mobileTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx20(
4564
+ mobileTab === "tags" && workspaceTags && /* @__PURE__ */ jsx21("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx21(
3831
4565
  TagManagerPanel,
3832
4566
  {
3833
4567
  workspaceTags,
@@ -3838,21 +4572,21 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3838
4572
  onTagMove: handleTagMove
3839
4573
  }
3840
4574
  ) }),
3841
- mobileTab === "stage" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col flex-1 min-h-0", children: [
3842
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
3843
- /* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
3844
- /* @__PURE__ */ jsx20(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
3845
- /* @__PURE__ */ jsx20("div", { className: "flex-1" }),
3846
- activeReferenceThumbnail ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
3847
- /* @__PURE__ */ jsx20("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
3848
- /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
3849
- /* @__PURE__ */ jsx20("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
3850
- ] }) : /* @__PURE__ */ jsx20("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
3851
- /* @__PURE__ */ jsx20("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
3852
- /* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
4575
+ mobileTab === "stage" && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col flex-1 min-h-0", children: [
4576
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
4577
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4578
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
4579
+ /* @__PURE__ */ jsx21("div", { className: "flex-1" }),
4580
+ activeReferenceThumbnail ? /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
4581
+ /* @__PURE__ */ jsx21("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
4582
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
4583
+ /* @__PURE__ */ jsx21("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4584
+ ] }) : /* @__PURE__ */ jsx21("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
4585
+ /* @__PURE__ */ jsx21("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
4586
+ /* @__PURE__ */ jsx21("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
3853
4587
  ] }),
3854
- /* @__PURE__ */ jsx20("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ jsxs18("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
3855
- /* @__PURE__ */ jsx20(
4588
+ /* @__PURE__ */ jsx21("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ jsxs19("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
4589
+ /* @__PURE__ */ jsx21(
3856
4590
  "textarea",
3857
4591
  {
3858
4592
  value: activePrompt,
@@ -3862,26 +4596,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3862
4596
  placeholder: "Prompt eingeben..."
3863
4597
  }
3864
4598
  ),
3865
- activePrompt && !isSynthesizing && /* @__PURE__ */ jsx20("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-white/20 active:text-white transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
4599
+ activePrompt && !isSynthesizing && /* @__PURE__ */ jsx21("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-white/20 active:text-white transition-colors", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
3866
4600
  ] }) }),
3867
- /* @__PURE__ */ jsx20("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ jsx20(
4601
+ /* @__PURE__ */ jsx21("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ jsx21(
3868
4602
  "button",
3869
4603
  {
3870
4604
  onClick: () => handleGenerateImage(),
3871
4605
  disabled: !activePrompt.trim() || isGenerating,
3872
4606
  className: "w-full flex items-center justify-center gap-2 rounded-xl font-bold text-[14px] uppercase tracking-wide transition-all disabled:opacity-30 active:scale-95",
3873
4607
  style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
3874
- children: isGenerating ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
3875
- /* @__PURE__ */ jsx20("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
3876
- /* @__PURE__ */ jsx20("span", { children: "Generiere..." })
3877
- ] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
3878
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
3879
- /* @__PURE__ */ jsx20("span", { children: "Generieren" })
4608
+ children: isGenerating ? /* @__PURE__ */ jsxs19(Fragment9, { children: [
4609
+ /* @__PURE__ */ jsx21("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
4610
+ /* @__PURE__ */ jsx21("span", { children: "Generiere..." })
4611
+ ] }) : /* @__PURE__ */ jsxs19(Fragment9, { children: [
4612
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
4613
+ /* @__PURE__ */ jsx21("span", { children: "Generieren" })
3880
4614
  ] })
3881
4615
  }
3882
4616
  ) }),
3883
- /* @__PURE__ */ jsxs18("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
3884
- /* @__PURE__ */ jsxs18(
4617
+ /* @__PURE__ */ jsxs19("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
4618
+ /* @__PURE__ */ jsxs19(
3885
4619
  "div",
3886
4620
  {
3887
4621
  className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
@@ -3895,25 +4629,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3895
4629
  setTouchStartX(null);
3896
4630
  },
3897
4631
  children: [
3898
- currentResult?.status === "processing" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-3", children: [
3899
- /* @__PURE__ */ jsx20("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
3900
- /* @__PURE__ */ jsx20("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
4632
+ currentResult?.status === "processing" && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-3", children: [
4633
+ /* @__PURE__ */ jsx21("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
4634
+ /* @__PURE__ */ jsx21("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
3901
4635
  ] }),
3902
- currentResult?.status === "error" && /* @__PURE__ */ jsxs18("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
3903
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
3904
- /* @__PURE__ */ jsx20("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
3905
- /* @__PURE__ */ jsx20("button", { onClick: () => handleGenerateImage(currentResult.prompt), className: "px-4 py-2 rounded-lg border border-white/20 text-[13px] text-white/70 active:bg-white/10", children: "Erneut versuchen" })
4636
+ currentResult?.status === "error" && /* @__PURE__ */ jsxs19("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
4637
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
4638
+ /* @__PURE__ */ jsx21("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
4639
+ /* @__PURE__ */ jsx21("button", { onClick: () => handleGenerateImage(currentResult.prompt), className: "px-4 py-2 rounded-lg border border-white/20 text-[13px] text-white/70 active:bg-white/10", children: "Erneut versuchen" })
3906
4640
  ] }),
3907
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx20("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
3908
- !currentResult && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
3909
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
3910
- /* @__PURE__ */ jsx20("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
4641
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx21("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
4642
+ !currentResult && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
4643
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
4644
+ /* @__PURE__ */ jsx21("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
3911
4645
  ] }),
3912
- currentResult?.status === "done" && /* @__PURE__ */ jsx20("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
3913
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs18(Fragment9, { children: [
3914
- /* @__PURE__ */ jsx20("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
3915
- /* @__PURE__ */ jsx20("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
3916
- /* @__PURE__ */ jsxs18("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
4646
+ currentResult?.status === "done" && /* @__PURE__ */ jsx21("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
4647
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs19(Fragment9, { children: [
4648
+ /* @__PURE__ */ jsx21("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
4649
+ /* @__PURE__ */ jsx21("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
4650
+ /* @__PURE__ */ jsxs19("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
3917
4651
  currentIndex + 1,
3918
4652
  " / ",
3919
4653
  history.length
@@ -3922,33 +4656,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3922
4656
  ]
3923
4657
  }
3924
4658
  ),
3925
- currentResult?.status === "done" && /* @__PURE__ */ jsxs18("div", { className: "flex gap-2 mt-3", children: [
3926
- /* @__PURE__ */ jsxs18("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
3927
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
3928
- /* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/60", children: "Prompt" })
4659
+ currentResult?.status === "done" && /* @__PURE__ */ jsxs19("div", { className: "flex gap-2 mt-3", children: [
4660
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
4661
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
4662
+ /* @__PURE__ */ jsx21("span", { className: "text-[12px] text-white/60", children: "Prompt" })
3929
4663
  ] }),
3930
- /* @__PURE__ */ jsxs18("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl bg-white/10 active:bg-white/15 transition-colors", style: { height: 44 }, children: [
3931
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
3932
- /* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
4664
+ /* @__PURE__ */ jsxs19("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl bg-white/10 active:bg-white/15 transition-colors", style: { height: 44 }, children: [
4665
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
4666
+ /* @__PURE__ */ jsx21("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
3933
4667
  ] }),
3934
- /* @__PURE__ */ jsxs18("button", { onClick: handleDownloadSingle, className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
3935
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
3936
- /* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/60", children: "Laden" })
4668
+ /* @__PURE__ */ jsxs19("button", { onClick: handleDownloadSingle, className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
4669
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
4670
+ /* @__PURE__ */ jsx21("span", { className: "text-[12px] text-white/60", children: "Laden" })
3937
4671
  ] })
3938
4672
  ] })
3939
4673
  ] })
3940
4674
  ] }),
3941
- mobileTab === "browse" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col flex-1 min-h-0", children: [
3942
- /* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
3943
- ["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" }) })
4675
+ mobileTab === "browse" && /* @__PURE__ */ jsxs19("div", { className: "flex flex-col flex-1 min-h-0", children: [
4676
+ /* @__PURE__ */ jsxs19("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
4677
+ ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ jsx21("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__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)),
4678
+ hfToken && /* @__PURE__ */ jsx21("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__ */ jsx21("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
3945
4679
  ] }),
3946
- /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
3947
- activeTab === "history" && /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
4680
+ /* @__PURE__ */ jsxs19("div", { className: "flex-1 overflow-hidden relative", children: [
4681
+ activeTab === "history" && /* @__PURE__ */ jsx21(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
3948
4682
  setCurrentResult(g);
3949
4683
  setMobileTab("stage");
3950
4684
  }, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
3951
- activeTab === "gallery" && /* @__PURE__ */ jsx20(
4685
+ activeTab === "gallery" && /* @__PURE__ */ jsx21(
3952
4686
  MediaLibrary,
3953
4687
  {
3954
4688
  items: galleryItems,
@@ -3968,39 +4702,43 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3968
4702
  }
3969
4703
  }
3970
4704
  ),
3971
- activeTab === "inspect" && /* @__PURE__ */ jsx20(InspectPanel, { currentResult, history, onSelect: (g) => {
4705
+ activeTab === "inspect" && /* @__PURE__ */ jsx21(InspectPanel, { currentResult, history, onSelect: (g) => {
3972
4706
  setCurrentResult(g);
3973
4707
  } })
3974
4708
  ] })
3975
4709
  ] }),
3976
- /* @__PURE__ */ jsxs18("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
3977
- /* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
3978
- workspaceTags && /* @__PURE__ */ jsxs18("button", { onClick: () => {
4710
+ /* @__PURE__ */ jsxs19("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
4711
+ /* @__PURE__ */ jsxs19("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
4712
+ workspaceTags && /* @__PURE__ */ jsxs19("button", { onClick: () => {
3979
4713
  setLeftTab("prompt");
3980
4714
  if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
3981
4715
  }, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
3982
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
4716
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
3983
4717
  "Prompt"
3984
4718
  ] }),
3985
- /* @__PURE__ */ jsxs18("button", { onClick: () => {
4719
+ /* @__PURE__ */ jsxs19("button", { onClick: () => {
3986
4720
  setLeftTab("hierarchy");
3987
4721
  if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
3988
4722
  }, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
3989
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
4723
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
3990
4724
  "Hierarchie"
3991
4725
  ] }),
3992
- /* @__PURE__ */ jsxs18("button", { onClick: () => setActiveTab("setup"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "setup" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
3993
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
4726
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setActiveTab("setup"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "setup" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
4727
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
3994
4728
  "Setup"
3995
4729
  ] }),
3996
- /* @__PURE__ */ jsxs18("button", { onClick: () => setActiveTab("sync"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
3997
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
4730
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setActiveTab("sync"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
4731
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
3998
4732
  "Sync"
3999
4733
  ] }),
4000
- workspaceTags && /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4734
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setActiveTab("hftest"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "hftest" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
4735
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "biotech" }),
4736
+ "HF"
4737
+ ] }),
4738
+ workspaceTags && /* @__PURE__ */ jsx21("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4001
4739
  ] }),
4002
- /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
4003
- leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx20("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx20(
4740
+ /* @__PURE__ */ jsxs19("div", { className: "flex-1 overflow-hidden relative", children: [
4741
+ leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && activeTab !== "hftest" && /* @__PURE__ */ jsx21("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx21(
4004
4742
  ListView,
4005
4743
  {
4006
4744
  nodes,
@@ -4031,14 +4769,15 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4031
4769
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
4032
4770
  }
4033
4771
  ) }),
4034
- workspaceTags && /* @__PURE__ */ jsx20("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ jsx20(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
4772
+ workspaceTags && /* @__PURE__ */ jsx21("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" && activeTab !== "hftest" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ jsx21(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
4035
4773
  handleGenerateImage(prompt);
4036
4774
  setMobileTab("stage");
4037
4775
  }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
4038
- activeTab === "setup" && /* @__PURE__ */ jsx20(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
4039
- activeTab === "sync" && /* @__PURE__ */ jsx20(
4776
+ activeTab === "setup" && /* @__PURE__ */ jsx21(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
4777
+ activeTab === "sync" && /* @__PURE__ */ jsx21(
4040
4778
  ProjectSyncTab,
4041
4779
  {
4780
+ topSlot: syncTopSlot,
4042
4781
  onProjectExport: handleProjectExport,
4043
4782
  onProjectImport: (f) => handleProjectImport(f),
4044
4783
  onWorkspaceImport: handleWorkspaceImport,
@@ -4059,7 +4798,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4059
4798
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
4060
4799
  }
4061
4800
  ),
4062
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
4801
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx21(
4063
4802
  TagManagerPanel,
4064
4803
  {
4065
4804
  workspaceTags,
@@ -4069,22 +4808,23 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4069
4808
  onTagReorder: handleTagReorder,
4070
4809
  onTagMove: handleTagMove
4071
4810
  }
4072
- )
4811
+ ),
4812
+ activeTab === "hftest" && /* @__PURE__ */ jsx21("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx21(HFTestTab, { token: hfToken, namespace: effectiveNamespace, galleryItems }) })
4073
4813
  ] })
4074
4814
  ] }),
4075
- /* @__PURE__ */ jsx20("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
4815
+ /* @__PURE__ */ jsx21("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
4076
4816
  { id: "tools", icon: "auto_fix_high", label: "Prompt" },
4077
4817
  { id: "stage", icon: "palette", label: "Stage" },
4078
4818
  { id: "labs", icon: "science", label: "Labs" },
4079
4819
  ...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
4080
4820
  { id: "browse", icon: "photo_library", label: "Galerie" }
4081
- ].map((tab) => /* @__PURE__ */ jsxs18("button", { onClick: () => setMobileTab(tab.id), className: `flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors ${mobileTab === tab.id ? "text-white" : "text-white/30"}`, children: [
4082
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
4083
- /* @__PURE__ */ jsx20("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
4821
+ ].map((tab) => /* @__PURE__ */ jsxs19("button", { onClick: () => setMobileTab(tab.id), className: `flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors ${mobileTab === tab.id ? "text-white" : "text-white/30"}`, children: [
4822
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
4823
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
4084
4824
  ] }, tab.id)) })
4085
4825
  ] });
4086
4826
  if (mdMode) {
4087
- return /* @__PURE__ */ jsx20("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
4827
+ return /* @__PURE__ */ jsx21("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
4088
4828
  }
4089
4829
  return mobileRoot;
4090
4830
  }
@@ -4092,17 +4832,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4092
4832
  const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
4093
4833
  const tlW = 920;
4094
4834
  const tlH = 520;
4095
- return /* @__PURE__ */ jsx20("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ jsxs18("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
4096
- /* @__PURE__ */ jsxs18("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
4097
- /* @__PURE__ */ jsxs18("div", { style: { height: 52, borderBottom: "1px solid rgba(255,255,255,0.05)", display: "flex", alignItems: "center", gap: 8, padding: "0 12px", flexShrink: 0 }, children: [
4098
- /* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4099
- /* @__PURE__ */ jsx20(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
4100
- /* @__PURE__ */ jsx20("div", { style: { flex: 1 } }),
4101
- /* @__PURE__ */ jsx20("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
4102
- /* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
4835
+ return /* @__PURE__ */ jsx21("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ jsxs19("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
4836
+ /* @__PURE__ */ jsxs19("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
4837
+ /* @__PURE__ */ jsxs19("div", { style: { height: 52, borderBottom: "1px solid rgba(255,255,255,0.05)", display: "flex", alignItems: "center", gap: 8, padding: "0 12px", flexShrink: 0 }, children: [
4838
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4839
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
4840
+ /* @__PURE__ */ jsx21("div", { style: { flex: 1 } }),
4841
+ /* @__PURE__ */ jsx21("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
4842
+ /* @__PURE__ */ jsx21("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
4103
4843
  ] }),
4104
- /* @__PURE__ */ jsx20("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs18("div", { style: { position: "relative", borderRadius: 12, border: `1px solid ${isSynthesizing ? "rgba(255,255,255,0.2)" : "rgba(255,255,255,0.1)"}`, background: "rgba(255,255,255,0.05)" }, children: [
4105
- /* @__PURE__ */ jsx20(
4844
+ /* @__PURE__ */ jsx21("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs19("div", { style: { position: "relative", borderRadius: 12, border: `1px solid ${isSynthesizing ? "rgba(255,255,255,0.2)" : "rgba(255,255,255,0.1)"}`, background: "rgba(255,255,255,0.05)" }, children: [
4845
+ /* @__PURE__ */ jsx21(
4106
4846
  "textarea",
4107
4847
  {
4108
4848
  value: activePrompt,
@@ -4111,27 +4851,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4111
4851
  placeholder: "Prompt eingeben..."
4112
4852
  }
4113
4853
  ),
4114
- activePrompt && /* @__PURE__ */ jsx20("button", { onClick: () => setActivePrompt(""), style: { position: "absolute", top: 6, right: 6, width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
4854
+ activePrompt && /* @__PURE__ */ jsx21("button", { onClick: () => setActivePrompt(""), style: { position: "absolute", top: 6, right: 6, width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 0 }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
4115
4855
  ] }) }),
4116
- /* @__PURE__ */ jsx20("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ jsx20(
4856
+ /* @__PURE__ */ jsx21("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ jsx21(
4117
4857
  "button",
4118
4858
  {
4119
4859
  onClick: () => handleGenerateImage(),
4120
4860
  disabled: !activePrompt.trim() || isGenerating,
4121
4861
  style: { width: "100%", height: 42, display: "flex", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 10, fontWeight: "bold", fontSize: 13, textTransform: "uppercase", letterSpacing: "0.05em", border: "1px solid rgba(255,255,255,0.1)", background: activePrompt.trim() && !isGenerating ? "#0284c7" : "transparent", color: "#fff", cursor: activePrompt.trim() && !isGenerating ? "pointer" : "default", opacity: !activePrompt.trim() || isGenerating ? 0.3 : 1, fontFamily: "inherit", transition: "background 0.2s" },
4122
- children: isGenerating ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
4123
- /* @__PURE__ */ jsx20("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4124
- /* @__PURE__ */ jsx20("span", { children: "Generiere..." })
4125
- ] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
4126
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
4127
- /* @__PURE__ */ jsx20("span", { children: "Generieren" })
4862
+ children: isGenerating ? /* @__PURE__ */ jsxs19(Fragment9, { children: [
4863
+ /* @__PURE__ */ jsx21("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4864
+ /* @__PURE__ */ jsx21("span", { children: "Generiere..." })
4865
+ ] }) : /* @__PURE__ */ jsxs19(Fragment9, { children: [
4866
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
4867
+ /* @__PURE__ */ jsx21("span", { children: "Generieren" })
4128
4868
  ] })
4129
4869
  }
4130
4870
  ) }),
4131
- /* @__PURE__ */ jsx20("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
4871
+ /* @__PURE__ */ jsx21("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx21(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
4132
4872
  ] }),
4133
- /* @__PURE__ */ jsxs18("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
4134
- /* @__PURE__ */ jsx20(
4873
+ /* @__PURE__ */ jsxs19("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
4874
+ /* @__PURE__ */ jsx21(
4135
4875
  "div",
4136
4876
  {
4137
4877
  style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
@@ -4143,26 +4883,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4143
4883
  else if (dx > 50) goToPrev();
4144
4884
  setTouchStartX(null);
4145
4885
  },
4146
- children: /* @__PURE__ */ jsxs18("div", { style: { height: "100%", width: "100%", borderRadius: 20, border: "1px solid rgba(255,255,255,0.05)", background: "rgba(0,0,0,0.4)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
4147
- currentResult?.status === "processing" && /* @__PURE__ */ jsxs18("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4148
- /* @__PURE__ */ jsx20("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4149
- /* @__PURE__ */ jsx20("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
4886
+ children: /* @__PURE__ */ jsxs19("div", { style: { height: "100%", width: "100%", borderRadius: 20, border: "1px solid rgba(255,255,255,0.05)", background: "rgba(0,0,0,0.4)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
4887
+ currentResult?.status === "processing" && /* @__PURE__ */ jsxs19("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4888
+ /* @__PURE__ */ jsx21("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4889
+ /* @__PURE__ */ jsx21("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
4150
4890
  ] }),
4151
- currentResult?.status === "error" && /* @__PURE__ */ jsxs18("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4152
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
4153
- /* @__PURE__ */ jsx20("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
4154
- /* @__PURE__ */ jsx20("button", { onClick: () => handleGenerateImage(currentResult.prompt), style: { padding: "8px 16px", borderRadius: 8, border: "1px solid rgba(255,255,255,0.2)", fontSize: 11, color: "rgba(255,255,255,0.7)", background: "none", cursor: "pointer", fontFamily: "inherit" }, children: "Erneut versuchen" })
4891
+ currentResult?.status === "error" && /* @__PURE__ */ jsxs19("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4892
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
4893
+ /* @__PURE__ */ jsx21("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
4894
+ /* @__PURE__ */ jsx21("button", { onClick: () => handleGenerateImage(currentResult.prompt), style: { padding: "8px 16px", borderRadius: 8, border: "1px solid rgba(255,255,255,0.2)", fontSize: 11, color: "rgba(255,255,255,0.7)", background: "none", cursor: "pointer", fontFamily: "inherit" }, children: "Erneut versuchen" })
4155
4895
  ] }),
4156
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx20("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
4157
- !currentResult && /* @__PURE__ */ jsxs18("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
4158
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
4159
- /* @__PURE__ */ jsx20("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
4896
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx21("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
4897
+ !currentResult && /* @__PURE__ */ jsxs19("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
4898
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
4899
+ /* @__PURE__ */ jsx21("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
4160
4900
  ] }),
4161
- currentResult?.status === "done" && /* @__PURE__ */ jsx20("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
4162
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs18(Fragment9, { children: [
4163
- /* @__PURE__ */ jsx20("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
4164
- /* @__PURE__ */ jsx20("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
4165
- /* @__PURE__ */ jsxs18("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
4901
+ currentResult?.status === "done" && /* @__PURE__ */ jsx21("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
4902
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs19(Fragment9, { children: [
4903
+ /* @__PURE__ */ jsx21("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
4904
+ /* @__PURE__ */ jsx21("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
4905
+ /* @__PURE__ */ jsxs19("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
4166
4906
  currentIndex + 1,
4167
4907
  " / ",
4168
4908
  history.length
@@ -4171,42 +4911,42 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4171
4911
  ] })
4172
4912
  }
4173
4913
  ),
4174
- currentResult?.status === "done" && /* @__PURE__ */ jsxs18("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
4175
- /* @__PURE__ */ jsxs18("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4176
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
4177
- /* @__PURE__ */ jsx20("span", { children: "Prompt" })
4914
+ currentResult?.status === "done" && /* @__PURE__ */ jsxs19("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
4915
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4916
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
4917
+ /* @__PURE__ */ jsx21("span", { children: "Prompt" })
4178
4918
  ] }),
4179
- /* @__PURE__ */ jsxs18("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "none", background: "rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.8)", fontSize: 11, fontWeight: "bold", cursor: "pointer", fontFamily: "inherit" }, children: [
4180
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
4181
- /* @__PURE__ */ jsx20("span", { children: "Referenz" })
4919
+ /* @__PURE__ */ jsxs19("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "none", background: "rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.8)", fontSize: 11, fontWeight: "bold", cursor: "pointer", fontFamily: "inherit" }, children: [
4920
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
4921
+ /* @__PURE__ */ jsx21("span", { children: "Referenz" })
4182
4922
  ] }),
4183
- /* @__PURE__ */ jsxs18("button", { onClick: handleDownloadSingle, style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4184
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
4185
- /* @__PURE__ */ jsx20("span", { children: "Laden" })
4923
+ /* @__PURE__ */ jsxs19("button", { onClick: handleDownloadSingle, style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4924
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
4925
+ /* @__PURE__ */ jsx21("span", { children: "Laden" })
4186
4926
  ] })
4187
4927
  ] })
4188
4928
  ] })
4189
4929
  ] }) });
4190
4930
  }
4191
- return /* @__PURE__ */ jsxs18("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
4192
- /* @__PURE__ */ jsx20("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
4193
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
4194
- /* @__PURE__ */ jsxs18("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
4195
- !isLeftCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex flex-1 gap-1", children: [
4196
- workspaceTags && /* @__PURE__ */ jsxs18("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4197
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
4931
+ return /* @__PURE__ */ jsxs19("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
4932
+ /* @__PURE__ */ jsx21("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ jsx21("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
4933
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
4934
+ /* @__PURE__ */ jsxs19("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
4935
+ !isLeftCollapsed && /* @__PURE__ */ jsxs19("div", { className: "flex flex-1 gap-1", children: [
4936
+ workspaceTags && /* @__PURE__ */ jsxs19("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4937
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
4198
4938
  "Prompt"
4199
4939
  ] }),
4200
- /* @__PURE__ */ jsxs18("button", { onClick: () => setLeftTab("hierarchy"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4201
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
4940
+ /* @__PURE__ */ jsxs19("button", { onClick: () => setLeftTab("hierarchy"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4941
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
4202
4942
  "Hierarchie"
4203
4943
  ] }),
4204
- workspaceTags && /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4944
+ workspaceTags && /* @__PURE__ */ jsx21("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4205
4945
  ] }),
4206
- /* @__PURE__ */ jsx20("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
4946
+ /* @__PURE__ */ jsx21("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
4207
4947
  ] }),
4208
- !isLeftCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
4209
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
4948
+ !isLeftCollapsed && /* @__PURE__ */ jsxs19("div", { className: "flex-1 overflow-hidden relative", children: [
4949
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx21(
4210
4950
  TagManagerPanel,
4211
4951
  {
4212
4952
  workspaceTags,
@@ -4217,11 +4957,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4217
4957
  onTagMove: handleTagMove
4218
4958
  }
4219
4959
  ),
4220
- activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs18("div", { children: [
4221
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
4222
- /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
4960
+ activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx21("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs19("div", { children: [
4961
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
4962
+ /* @__PURE__ */ jsx21("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
4223
4963
  ] }) }),
4224
- leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ jsx20("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx20(
4964
+ leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ jsx21("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx21(
4225
4965
  ListView,
4226
4966
  {
4227
4967
  nodes,
@@ -4246,18 +4986,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4246
4986
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
4247
4987
  }
4248
4988
  ) }),
4249
- leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ jsx20(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
4989
+ leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ jsx21(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
4250
4990
  ] })
4251
4991
  ] }),
4252
- !isLeftCollapsed && /* @__PURE__ */ jsx20("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
4253
- /* @__PURE__ */ jsxs18("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
4254
- /* @__PURE__ */ jsxs18("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
4255
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5", children: [
4256
- /* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4257
- /* @__PURE__ */ jsx20(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
4992
+ !isLeftCollapsed && /* @__PURE__ */ jsx21("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
4993
+ /* @__PURE__ */ jsxs19("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
4994
+ /* @__PURE__ */ jsxs19("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
4995
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1.5", children: [
4996
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4997
+ /* @__PURE__ */ jsx21(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
4258
4998
  ] }),
4259
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 mx-auto", children: [
4260
- /* @__PURE__ */ jsx20(
4999
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 mx-auto", children: [
5000
+ /* @__PURE__ */ jsx21(
4261
5001
  "button",
4262
5002
  {
4263
5003
  onClick: () => setMiddlePanel("stage"),
@@ -4265,7 +5005,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4265
5005
  children: "Stage"
4266
5006
  }
4267
5007
  ),
4268
- /* @__PURE__ */ jsx20(
5008
+ /* @__PURE__ */ jsx21(
4269
5009
  "button",
4270
5010
  {
4271
5011
  onClick: () => setMiddlePanel("labs"),
@@ -4274,68 +5014,68 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4274
5014
  }
4275
5015
  )
4276
5016
  ] }),
4277
- /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
4278
- activeReferenceThumbnail ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
4279
- /* @__PURE__ */ jsx20("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
4280
- /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
4281
- /* @__PURE__ */ jsx20("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4282
- ] }) : /* @__PURE__ */ jsxs18("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
4283
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
4284
- /* @__PURE__ */ jsx20("span", { children: "Ref" })
5017
+ /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-2", children: [
5018
+ activeReferenceThumbnail ? /* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
5019
+ /* @__PURE__ */ jsx21("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
5020
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
5021
+ /* @__PURE__ */ jsx21("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
5022
+ ] }) : /* @__PURE__ */ jsxs19("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
5023
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
5024
+ /* @__PURE__ */ jsx21("span", { children: "Ref" })
4285
5025
  ] }),
4286
- /* @__PURE__ */ jsx20("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
4287
- /* @__PURE__ */ jsx20(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
5026
+ /* @__PURE__ */ jsx21("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
5027
+ /* @__PURE__ */ jsx21(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
4288
5028
  ] })
4289
5029
  ] }),
4290
- /* @__PURE__ */ jsxs18("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
4291
- !isPromptCollapsed && /* @__PURE__ */ jsx20("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxs18("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
4292
- /* @__PURE__ */ jsx20("textarea", { value: activePrompt, onChange: (e) => setActivePrompt(e.target.value), className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar", placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..." }),
4293
- activePrompt && !isSynthesizing && /* @__PURE__ */ jsx20("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
5030
+ /* @__PURE__ */ jsxs19("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
5031
+ !isPromptCollapsed && /* @__PURE__ */ jsx21("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxs19("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
5032
+ /* @__PURE__ */ jsx21("textarea", { value: activePrompt, onChange: (e) => setActivePrompt(e.target.value), className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar", placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..." }),
5033
+ activePrompt && !isSynthesizing && /* @__PURE__ */ jsx21("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4294
5034
  ] }) }),
4295
- middlePanel === "labs" ? /* @__PURE__ */ jsx20("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx20(LabsTab, { services: labServices, onResult: (item) => {
5035
+ middlePanel === "labs" ? /* @__PURE__ */ jsx21("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx21(LabsTab, { services: labServices, onResult: (item) => {
4296
5036
  const frame = item.frames[0];
4297
5037
  if (frame?.base64) setCurrentResult(frameToGeneration(frame, item));
4298
- } }) }) : /* @__PURE__ */ jsx20("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs18("div", { className: "h-full w-full max-w-4xl aspect-square rounded-3xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center group shadow-2xl", children: [
4299
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs18("div", { className: "absolute top-6 right-6 z-30 bg-black/60 backdrop-blur-md px-4 py-2 rounded-full border border-white/10 flex items-center gap-3", children: [
4300
- /* @__PURE__ */ jsx20("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
4301
- /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
5038
+ } }) }) : /* @__PURE__ */ jsx21("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs19("div", { className: "h-full w-full max-w-4xl aspect-square rounded-3xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center group shadow-2xl", children: [
5039
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs19("div", { className: "absolute top-6 right-6 z-30 bg-black/60 backdrop-blur-md px-4 py-2 rounded-full border border-white/10 flex items-center gap-3", children: [
5040
+ /* @__PURE__ */ jsx21("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
5041
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
4302
5042
  ] }),
4303
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-4", children: [
4304
- /* @__PURE__ */ jsx20("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
4305
- /* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
4306
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs18("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
4307
- /* @__PURE__ */ jsx20("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
4308
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-2", children: [
4309
- /* @__PURE__ */ jsx20("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
4310
- /* @__PURE__ */ jsx20("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
5043
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-4", children: [
5044
+ /* @__PURE__ */ jsx21("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5045
+ /* @__PURE__ */ jsx21("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5046
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs19("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
5047
+ /* @__PURE__ */ jsx21("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
5048
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col gap-2", children: [
5049
+ /* @__PURE__ */ jsx21("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
5050
+ /* @__PURE__ */ jsx21("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
4311
5051
  ] }),
4312
- /* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
4313
- ] }) : /* @__PURE__ */ jsxs18("div", { className: "h-full w-full relative flex items-center justify-center", children: [
4314
- /* @__PURE__ */ jsx20("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
4315
- /* @__PURE__ */ jsxs18("div", { className: "absolute bottom-6 flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-4 group-hover:translate-y-0 z-20", children: [
4316
- /* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
4317
- /* @__PURE__ */ jsx20(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
4318
- /* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
5052
+ /* @__PURE__ */ jsx21(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
5053
+ ] }) : /* @__PURE__ */ jsxs19("div", { className: "h-full w-full relative flex items-center justify-center", children: [
5054
+ /* @__PURE__ */ jsx21("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
5055
+ /* @__PURE__ */ jsxs19("div", { className: "absolute bottom-6 flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-4 group-hover:translate-y-0 z-20", children: [
5056
+ /* @__PURE__ */ jsx21(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
5057
+ /* @__PURE__ */ jsx21(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
5058
+ /* @__PURE__ */ jsx21(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
4319
5059
  ] })
4320
- ] }) : /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
4321
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
4322
- /* @__PURE__ */ jsx20("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5060
+ ] }) : /* @__PURE__ */ jsxs19("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5061
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
5062
+ /* @__PURE__ */ jsx21("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
4323
5063
  ] })
4324
5064
  ] }) })
4325
5065
  ] })
4326
5066
  ] }),
4327
- !isRightCollapsed && /* @__PURE__ */ jsx20("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
4328
- /* @__PURE__ */ jsxs18("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
4329
- /* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
4330
- /* @__PURE__ */ jsx20("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ jsx20("button", { onClick: () => {
5067
+ !isRightCollapsed && /* @__PURE__ */ jsx21("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
5068
+ /* @__PURE__ */ jsxs19("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
5069
+ /* @__PURE__ */ jsxs19("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
5070
+ /* @__PURE__ */ jsx21("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ jsx21("button", { onClick: () => {
4331
5071
  setActiveTab(tab);
4332
5072
  setIsRightCollapsed(false);
4333
- }, 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" }) }),
4335
- /* @__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" }) })
5073
+ }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx21("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)) }),
5074
+ hfToken && /* @__PURE__ */ jsx21("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__ */ jsx21("span", { className: `material-symbols-outlined text-[18px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) }),
5075
+ /* @__PURE__ */ jsx21("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
4336
5076
  ] }),
4337
- !isRightCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
4338
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
5077
+ !isRightCollapsed && /* @__PURE__ */ jsxs19("div", { className: "flex-1 overflow-hidden relative", children: [
5078
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx21(
4339
5079
  TagManagerPanel,
4340
5080
  {
4341
5081
  workspaceTags,
@@ -4346,12 +5086,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4346
5086
  onTagMove: handleTagMove
4347
5087
  }
4348
5088
  ),
4349
- activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs18("div", { children: [
4350
- /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
4351
- /* @__PURE__ */ jsx20("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
5089
+ activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx21("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs19("div", { children: [
5090
+ /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
5091
+ /* @__PURE__ */ jsx21("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
4352
5092
  ] }) }),
4353
- activeTab === "history" && /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
4354
- activeTab === "gallery" && /* @__PURE__ */ jsx20(
5093
+ activeTab === "history" && /* @__PURE__ */ jsx21(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5094
+ activeTab === "gallery" && /* @__PURE__ */ jsx21(
4355
5095
  MediaLibrary,
4356
5096
  {
4357
5097
  items: galleryItems,
@@ -4365,11 +5105,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4365
5105
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
4366
5106
  }
4367
5107
  ),
4368
- activeTab === "inspect" && /* @__PURE__ */ jsx20(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
4369
- activeTab === "setup" && /* @__PURE__ */ jsx20(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
4370
- activeTab === "sync" && /* @__PURE__ */ jsx20(
5108
+ activeTab === "inspect" && /* @__PURE__ */ jsx21(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
5109
+ activeTab === "setup" && /* @__PURE__ */ jsx21(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
5110
+ activeTab === "sync" && /* @__PURE__ */ jsx21(
4371
5111
  ProjectSyncTab,
4372
5112
  {
5113
+ topSlot: syncTopSlot,
4373
5114
  onProjectExport: handleProjectExport,
4374
5115
  onProjectImport: (f) => handleProjectImport(f),
4375
5116
  onWorkspaceImport: handleWorkspaceImport,
@@ -4396,7 +5137,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4396
5137
  }
4397
5138
 
4398
5139
  // src/components/FaApp.tsx
4399
- import { jsx as jsx21 } from "react/jsx-runtime";
5140
+ import { useState as useState17, useEffect as useEffect7 } from "react";
5141
+ import { jsx as jsx22 } from "react/jsx-runtime";
4400
5142
  function FaApp({
4401
5143
  onGenerateImage,
4402
5144
  onGeneratePrompt,
@@ -4407,17 +5149,27 @@ function FaApp({
4407
5149
  onFlowUpload: _onFlowUpload,
4408
5150
  onFlowMediaUpload: _onFlowMediaUpload,
4409
5151
  libToken,
5152
+ allowDevNamespace,
5153
+ serverBaseUrl,
4410
5154
  onFetchServerProjects,
4411
5155
  onServerSave,
4412
5156
  onServerLoad,
4413
5157
  onServerDelete,
4414
5158
  buildInfo
4415
5159
  }) {
5160
+ const [hfNamespace, setHfNamespace] = useState17(void 0);
5161
+ useEffect7(() => {
5162
+ if (!serverBaseUrl) return;
5163
+ fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
5164
+ if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
5165
+ }).catch(() => {
5166
+ });
5167
+ }, [serverBaseUrl]);
4416
5168
  const wrappedPrompt = async (text, options) => {
4417
5169
  const result = await onGeneratePrompt(text, options);
4418
5170
  return result.text;
4419
5171
  };
4420
- return /* @__PURE__ */ jsx21(
5172
+ return /* @__PURE__ */ jsx22(
4421
5173
  AvatarArchitectApp,
4422
5174
  {
4423
5175
  onGenerateImage,
@@ -4425,6 +5177,8 @@ function FaApp({
4425
5177
  onDownload,
4426
5178
  onSelectMedia,
4427
5179
  initialHfToken: libToken ? libToken.startsWith("hf_") ? libToken : `hf_${libToken}` : void 0,
5180
+ hfNamespace,
5181
+ allowDevNamespace: !hfNamespace && allowDevNamespace,
4428
5182
  onFetchServerProjects,
4429
5183
  onServerSave,
4430
5184
  onServerLoad,
@@ -4435,7 +5189,7 @@ function FaApp({
4435
5189
  }
4436
5190
 
4437
5191
  // src/index.ts
4438
- var LIB_VERSION = "1.3.18";
5192
+ var LIB_VERSION = "2.0.13";
4439
5193
  export {
4440
5194
  AvatarArchitectApp,
4441
5195
  CollapsibleCard,
@@ -4460,9 +5214,12 @@ export {
4460
5214
  SectionLabel,
4461
5215
  SetupPanel,
4462
5216
  TagManagerPanel,
5217
+ applyEvent,
5218
+ applyEvents,
4463
5219
  autoLabel,
4464
5220
  buildBlendInstruction,
4465
5221
  buildCompareInstruction,
5222
+ buildDag,
4466
5223
  buildFallbackPrompt,
4467
5224
  buildGenerationPrompt,
4468
5225
  buildImageGenerationOptions,
@@ -4474,27 +5231,36 @@ export {
4474
5231
  cleanAiResponse,
4475
5232
  createFlowServices,
4476
5233
  exportProjectToZip,
5234
+ findForks,
5235
+ findTips,
4477
5236
  formatTreeToMarkdown,
4478
5237
  frameToGeneration,
4479
5238
  getFormattedTimestamp,
4480
5239
  getHFToken,
5240
+ getSessionClientId,
4481
5241
  groupGenerationsToLabItems,
5242
+ hfBatchArchive,
5243
+ hfBootstrapFromLegacy,
4482
5244
  hfDeleteProject,
4483
5245
  hfDownloadProject,
5246
+ hfListDir,
4484
5247
  hfListProjects,
4485
5248
  hfLoadImageAsBase64,
4486
- hfLoadMetadata,
4487
- hfLoadTags,
4488
- hfSaveMetadata,
4489
- hfSaveTags,
4490
5249
  hfUploadImage,
4491
5250
  hfUploadProjectForm,
5251
+ hfUploadSmallFile,
4492
5252
  importProjectFromZip,
4493
5253
  injectXMPMetadata,
4494
5254
  interpretSdkError,
5255
+ loadHFState,
5256
+ loadPendingEvents,
4495
5257
  parsePromptFile,
4496
5258
  parsePromptResponse,
4497
5259
  setHFToken,
5260
+ topoSort,
5261
+ tsFromEventPath,
5262
+ useHFState,
4498
5263
  useKeyboardNavigation,
4499
- useOnClickOutside
5264
+ useOnClickOutside,
5265
+ writeHFEvent
4500
5266
  };