@rslsp1/fa-app-tools 2.0.30 → 2.0.48

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
@@ -21,7 +21,7 @@ import {
21
21
  setHFToken,
22
22
  tsFromEventPath,
23
23
  writeHFEvent
24
- } from "./chunk-UCEQOGPT.mjs";
24
+ } from "./chunk-TTJTQP43.mjs";
25
25
 
26
26
  // src/hooks/useOnClickOutside.ts
27
27
  import { useEffect } from "react";
@@ -353,8 +353,10 @@ var CompactDropdown = ({
353
353
  };
354
354
 
355
355
  // src/components/HistoryPanel.tsx
356
+ import { useState as useState2 } from "react";
356
357
  import { motion } from "motion/react";
357
358
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
359
+ var PAGE_SIZE = 20;
358
360
  var formatFriendlyTimestamp = (timestamp) => {
359
361
  const date = new Date(timestamp);
360
362
  const now = /* @__PURE__ */ new Date();
@@ -367,38 +369,55 @@ var formatFriendlyTimestamp = (timestamp) => {
367
369
  return `${date.toLocaleDateString([], { day: "2-digit", month: "2-digit" })}, ${timeStr}`;
368
370
  };
369
371
  var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
372
+ const [visibleCount, setVisibleCount] = useState2(PAGE_SIZE);
373
+ const visibleHistory = history.slice(0, visibleCount);
370
374
  if (history.length === 0) {
371
375
  return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
372
376
  /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
373
377
  /* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Keine Historie" })
374
378
  ] });
375
379
  }
376
- return /* @__PURE__ */ jsx5(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-4 flex flex-col gap-3 overflow-y-auto dark-scrollbar", children: history.map((gen) => /* @__PURE__ */ jsxs3(
377
- "div",
378
- {
379
- className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${currentResultId === gen.id ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
380
- onClick: () => onSelect(gen),
381
- children: [
382
- /* @__PURE__ */ jsx5("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5", children: /* @__PURE__ */ jsx5("img", { src: gen.base64 ? gen.base64.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
383
- /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
384
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
385
- /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
386
- gen.model && /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: gen.model })
380
+ return /* @__PURE__ */ jsxs3(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-4 flex flex-col gap-3 overflow-y-auto dark-scrollbar", children: [
381
+ visibleHistory.map((gen) => /* @__PURE__ */ jsxs3(
382
+ "div",
383
+ {
384
+ className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${currentResultId === gen.id ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
385
+ onClick: () => onSelect(gen),
386
+ children: [
387
+ /* @__PURE__ */ jsx5("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 flex items-center justify-center", children: gen.status === "processing" ? /* @__PURE__ */ jsx5("div", { className: "w-5 h-5 border-t-2 border-white/40 rounded-full animate-spin" }) : gen.status === "error" ? /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-red-400 text-[20px]", children: "warning" }) : /* @__PURE__ */ jsx5("img", { src: gen.base64 ? gen.base64.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }) }),
388
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
389
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
390
+ /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(gen.timestamp) }),
391
+ gen.model && /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: gen.model })
392
+ ] }),
393
+ /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
394
+ '"',
395
+ gen.prompt,
396
+ '"'
397
+ ] }),
398
+ gen.status === "processing" && /* @__PURE__ */ jsx5("p", { className: "text-[8px] text-white/30 mt-0.5", children: "Generiert\u2026" }),
399
+ gen.status === "error" && /* @__PURE__ */ jsx5("p", { className: "text-[8px] text-red-400/70 mt-0.5 truncate", children: gen.error?.message })
387
400
  ] }),
388
- /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
389
- '"',
390
- gen.prompt,
391
- '"'
392
- ] })
393
- ] }),
394
- /* @__PURE__ */ jsx5("button", { onClick: (e) => {
395
- e.stopPropagation();
396
- onDelete(gen.id);
397
- }, className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 hover:text-white rounded-md transition-all", children: /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
398
- ]
399
- },
400
- gen.id
401
- )) });
401
+ /* @__PURE__ */ jsx5(
402
+ "button",
403
+ {
404
+ onClick: (e) => {
405
+ e.stopPropagation();
406
+ onDelete(gen.id);
407
+ },
408
+ className: "absolute top-2 right-2 opacity-0 group-hover:opacity-100 w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 hover:text-white rounded-md transition-all",
409
+ children: /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[14px]", children: "delete" })
410
+ }
411
+ )
412
+ ]
413
+ },
414
+ gen.id
415
+ )),
416
+ visibleCount < history.length && /* @__PURE__ */ jsxs3("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE), className: "w-full py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-xl text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
417
+ history.length - visibleCount,
418
+ " weitere laden"
419
+ ] })
420
+ ] });
402
421
  };
403
422
 
404
423
  // src/components/InspectPanel.tsx
@@ -467,7 +486,7 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
467
486
  };
468
487
 
469
488
  // src/components/SetupPanel.tsx
470
- import { useRef as useRef2, useState as useState2 } from "react";
489
+ import { useRef as useRef2, useState as useState3 } from "react";
471
490
  import { motion as motion3 } from "motion/react";
472
491
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
473
492
  var PRESET_URLS = [
@@ -477,11 +496,11 @@ var PRESET_URLS = [
477
496
  ];
478
497
  var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
479
498
  const workspaceInputRef = useRef2(null);
480
- const [urlInput, setUrlInput] = useState2("");
481
- const [tokenInput, setTokenInput] = useState2("");
482
- const [testStatus, setTestStatus] = useState2("idle");
483
- const [result, setResult] = useState2(null);
484
- const [fetchError, setFetchError] = useState2(null);
499
+ const [urlInput, setUrlInput] = useState3("");
500
+ const [tokenInput, setTokenInput] = useState3("");
501
+ const [testStatus, setTestStatus] = useState3("idle");
502
+ const [result, setResult] = useState3(null);
503
+ const [fetchError, setFetchError] = useState3(null);
485
504
  const runTest = async (url) => {
486
505
  if (!url.trim()) return;
487
506
  setTestStatus("loading");
@@ -644,18 +663,48 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
644
663
  };
645
664
 
646
665
  // src/components/MediaLibrary.tsx
666
+ import { useState as useState4 } from "react";
647
667
  import { motion as motion4 } from "motion/react";
668
+
669
+ // src/lib/grouping.ts
670
+ function groupByPrompt(items) {
671
+ const map = /* @__PURE__ */ new Map();
672
+ for (const item of items) {
673
+ const key = item.prompt ?? "";
674
+ if (!map.has(key)) map.set(key, []);
675
+ map.get(key).push(item);
676
+ }
677
+ const groups = [];
678
+ for (const [prompt, groupItems] of map) {
679
+ const titled = groupItems.filter((i) => i.titleTs);
680
+ const representative = titled.length > 0 ? titled.reduce((a, b) => a.titleTs > b.titleTs ? a : b) : groupItems.reduce((a, b) => a.timestamp > b.timestamp ? a : b);
681
+ groups.push({ prompt, items: groupItems, representative });
682
+ }
683
+ return groups.sort((a, b) => {
684
+ const aMax = Math.max(...a.items.map((i) => i.timestamp));
685
+ const bMax = Math.max(...b.items.map((i) => i.timestamp));
686
+ return bMax - aMax;
687
+ });
688
+ }
689
+
690
+ // src/components/MediaLibrary.tsx
648
691
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
692
+ var PAGE_SIZE2 = 20;
649
693
  var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
694
+ const [visibleCount, setVisibleCount] = useState4(PAGE_SIZE2);
650
695
  const selectedCount = items.filter((i) => i.selectedForExport).length;
696
+ const groups = groupByPrompt(items);
697
+ const visibleGroups = groups.slice(0, visibleCount);
651
698
  return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col h-full overflow-hidden", children: [
652
699
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col p-4 border-b border-white/5 gap-3 bg-black/20", children: [
653
700
  /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
654
701
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
655
702
  /* @__PURE__ */ jsx8("span", { className: "text-[10px] font-bold text-white uppercase tracking-widest", children: "Projekt Galerie" }),
656
703
  /* @__PURE__ */ jsxs6("span", { className: "text-[9px] text-white/30 uppercase tracking-tighter", children: [
704
+ visibleCount < groups.length ? `${visibleCount} / ${groups.length}` : groups.length,
705
+ " Gruppen \xB7 ",
657
706
  items.length,
658
- " Assets geladen"
707
+ " Assets"
659
708
  ] })
660
709
  ] }),
661
710
  /* @__PURE__ */ jsxs6("button", { type: "button", onClick: () => onImport?.(), className: "flex items-center gap-1.5 px-3 py-1.5 bg-blue-500 hover:bg-blue-400 text-white rounded-lg text-[10px] font-bold uppercase transition-all shadow-[0_0_15px_rgba(59,130,246,0.3)]", children: [
@@ -674,41 +723,57 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
674
723
  ] })
675
724
  ] })
676
725
  ] }),
677
- /* @__PURE__ */ jsx8("div", { className: "flex-1 overflow-y-auto p-4 dark-scrollbar", children: items.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
726
+ /* @__PURE__ */ jsx8("div", { className: "flex-1 overflow-y-auto p-4 dark-scrollbar", children: groups.length === 0 ? /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
678
727
  /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[64px]", children: "photo_library" }),
679
728
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
680
729
  /* @__PURE__ */ jsx8("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
681
730
  /* @__PURE__ */ jsx8("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
682
731
  ] })
683
- ] }) : /* @__PURE__ */ jsx8("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ jsxs6(motion4.div, { initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, className: `relative aspect-square group/item rounded-xl overflow-hidden border transition-all bg-white/5 cursor-pointer shadow-lg ${item.selectedForExport ? "border-blue-500 shadow-[0_0_15px_rgba(59,130,246,0.2)]" : "border-white/10 opacity-70 hover:opacity-100"}`, onClick: () => onSelect?.(item), children: [
684
- item.status === "processing" ? /* @__PURE__ */ jsxs6("div", { className: "w-full h-full flex flex-col items-center justify-center gap-2 bg-black/40", children: [
685
- /* @__PURE__ */ jsx8("div", { className: "w-6 h-6 border-t-2 border-white rounded-full animate-spin" }),
686
- /* @__PURE__ */ jsx8("span", { className: "text-[8px] text-white/40 uppercase font-bold tracking-widest", children: "Generiere\u2026" })
687
- ] }) : /* @__PURE__ */ jsx8("img", { src: item.base64 ? item.base64.startsWith("data:") ? item.base64 : `data:image/png;base64,${item.base64}` : "", className: "w-full h-full object-cover transition-transform duration-500 group-hover/item:scale-110", alt: item.prompt }),
688
- /* @__PURE__ */ jsx8("button", { type: "button", className: `absolute top-2 left-2 w-5 h-5 rounded-md border flex items-center justify-center transition-all z-30 pointer-events-auto ${item.selectedForExport ? "bg-blue-500 border-blue-400" : "bg-black/60 border-white/20"}`, onClick: (e) => {
689
- e.stopPropagation();
690
- onToggleSelection?.(item.id);
691
- }, children: item.selectedForExport && /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px] text-white", children: "check" }) }),
692
- /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover/item:opacity-100 transition-opacity flex flex-col justify-end p-2 gap-2 pointer-events-none", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
693
- /* @__PURE__ */ jsx8("span", { className: "text-[8px] font-bold text-white/80 truncate max-w-[80px] uppercase tracking-tighter", children: item.prompt || "Importiert" }),
694
- /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
695
- onGenerateReference && /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
696
- e.stopPropagation();
697
- onGenerateReference(item);
698
- }, className: "w-6 h-6 flex items-center justify-center bg-blue-500/20 text-blue-400 hover:bg-blue-500 rounded-md transition-all hover:text-white pointer-events-auto", children: /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }) }),
699
- /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
700
- e.stopPropagation();
701
- onDelete?.(item.id);
702
- }, className: "w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 rounded-md transition-all hover:text-white pointer-events-auto", children: /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
703
- ] })
704
- ] }) }),
705
- /* @__PURE__ */ jsx8("div", { className: "absolute top-1 right-1 px-1.5 py-0.5 bg-black/60 backdrop-blur-md rounded text-[7px] font-bold text-white/60 uppercase tracking-tight", children: item.type === "import" ? "Library" : "Gen" })
706
- ] }, item.id)) }) })
732
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "grid grid-cols-2 gap-3 pb-10", children: [
733
+ visibleGroups.map((group) => {
734
+ const rep = group.representative;
735
+ return /* @__PURE__ */ jsxs6(
736
+ motion4.div,
737
+ {
738
+ initial: { opacity: 0, scale: 0.9 },
739
+ animate: { opacity: 1, scale: 1 },
740
+ className: "relative aspect-square group/item rounded-xl overflow-hidden border border-white/10 opacity-70 hover:opacity-100 transition-all bg-white/5 cursor-pointer shadow-lg",
741
+ onClick: () => onSelect(rep),
742
+ children: [
743
+ /* @__PURE__ */ jsx8("img", { src: rep.base64 ? rep.base64.startsWith("data:") ? rep.base64 : `data:image/png;base64,${rep.base64}` : "", className: "w-full h-full object-cover transition-transform duration-500 group-hover/item:scale-110", alt: rep.prompt }),
744
+ group.items.length > 1 && /* @__PURE__ */ jsxs6("div", { className: "absolute top-1.5 left-1.5 px-1.5 py-0.5 bg-black/70 backdrop-blur-md rounded text-[8px] font-bold text-white/80 flex items-center gap-1", children: [
745
+ /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[10px]", children: "photo_library" }),
746
+ group.items.length
747
+ ] }),
748
+ /* @__PURE__ */ jsx8("div", { className: "absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover/item:opacity-100 transition-opacity flex flex-col justify-end p-2 gap-2 pointer-events-none", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
749
+ /* @__PURE__ */ jsx8("span", { className: "text-[8px] font-bold text-white/80 truncate max-w-[80px] uppercase tracking-tighter", children: rep.prompt || "Importiert" }),
750
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
751
+ onGenerateReference && /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
752
+ e.stopPropagation();
753
+ onGenerateReference(rep);
754
+ }, className: "w-6 h-6 flex items-center justify-center bg-blue-500/20 text-blue-400 hover:bg-blue-500 rounded-md transition-all hover:text-white pointer-events-auto", children: /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }) }),
755
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
756
+ e.stopPropagation();
757
+ onDelete(rep.id);
758
+ }, className: "w-6 h-6 flex items-center justify-center bg-red-500/20 text-red-400 hover:bg-red-500 rounded-md transition-all hover:text-white pointer-events-auto", children: /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[14px]", children: "delete" }) })
759
+ ] })
760
+ ] }) }),
761
+ /* @__PURE__ */ jsx8("div", { className: "absolute top-1 right-1 px-1.5 py-0.5 bg-black/60 backdrop-blur-md rounded text-[7px] font-bold text-white/60 uppercase tracking-tight", children: rep.type === "import" ? "Library" : "Gen" })
762
+ ]
763
+ },
764
+ rep.id
765
+ );
766
+ }),
767
+ visibleCount < groups.length && /* @__PURE__ */ jsx8("div", { className: "col-span-2 flex justify-center pt-2 pb-4", children: /* @__PURE__ */ jsxs6("button", { type: "button", onClick: () => setVisibleCount((c) => c + PAGE_SIZE2), className: "px-4 py-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded-lg text-[10px] font-bold uppercase text-white/60 hover:text-white transition-all", children: [
768
+ groups.length - visibleCount,
769
+ " weitere laden"
770
+ ] }) })
771
+ ] }) })
707
772
  ] });
708
773
  };
709
774
 
710
775
  // src/components/ListView.tsx
711
- import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
776
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
712
777
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
713
778
  var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
714
779
  const inputRef = useRef3(null);
@@ -773,7 +838,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
773
838
  ] });
774
839
  };
775
840
  function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
776
- const [collapsed, setCollapsed] = useState3(/* @__PURE__ */ new Set());
841
+ const [collapsed, setCollapsed] = useState5(/* @__PURE__ */ new Set());
777
842
  const toggleCollapse = (id) => {
778
843
  setCollapsed((prev) => {
779
844
  const next = new Set(prev);
@@ -801,13 +866,13 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
801
866
  }
802
867
 
803
868
  // src/components/AvatarArchitectApp.tsx
804
- import { useState as useState18, useCallback as useCallback3, useMemo as useMemo2, useEffect as useEffect7, useRef as useRef8 } from "react";
869
+ import { useState as useState20, useCallback as useCallback3, useMemo as useMemo2, useEffect as useEffect7, useRef as useRef8 } from "react";
805
870
 
806
871
  // src/components/PromptTab.tsx
807
- import { useRef as useRef4, useState as useState5 } from "react";
872
+ import { useRef as useRef4, useState as useState7 } from "react";
808
873
 
809
874
  // src/components/CollapsibleCard.tsx
810
- import { useState as useState4 } from "react";
875
+ import { useState as useState6 } from "react";
811
876
  import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
812
877
  var CollapsibleCard = ({
813
878
  title,
@@ -818,7 +883,7 @@ var CollapsibleCard = ({
818
883
  collapsible = true,
819
884
  className = ""
820
885
  }) => {
821
- const [isOpen, setIsOpen] = useState4(defaultOpen);
886
+ const [isOpen, setIsOpen] = useState6(defaultOpen);
822
887
  return /* @__PURE__ */ jsxs8("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
823
888
  /* @__PURE__ */ jsxs8(
824
889
  "div",
@@ -865,18 +930,18 @@ var PromptTab = ({
865
930
  onTagUpdate,
866
931
  onTagDelete
867
932
  }) => {
868
- const [selectedLabels, setSelectedLabels] = useState5(/* @__PURE__ */ new Set());
869
- const [instructions, setInstructions] = useState5("");
870
- const [rules, setRules] = useState5("");
871
- const [activeCategory, setActiveCategory] = useState5(null);
872
- const [copied, setCopied] = useState5(false);
933
+ const [selectedLabels, setSelectedLabels] = useState7(/* @__PURE__ */ new Set());
934
+ const [instructions, setInstructions] = useState7("");
935
+ const [rules, setRules] = useState7("");
936
+ const [activeCategory, setActiveCategory] = useState7(null);
937
+ const [copied, setCopied] = useState7(false);
873
938
  const imgInputRef = useRef4(null);
874
- const [addingInCat, setAddingInCat] = useState5(null);
875
- const [newLabel, setNewLabel] = useState5("");
876
- const [newValue, setNewValue] = useState5("");
877
- const [editingTag, setEditingTag] = useState5(null);
878
- const [editLabel, setEditLabel] = useState5("");
879
- const [editValue, setEditValue] = useState5("");
939
+ const [addingInCat, setAddingInCat] = useState7(null);
940
+ const [newLabel, setNewLabel] = useState7("");
941
+ const [newValue, setNewValue] = useState7("");
942
+ const [editingTag, setEditingTag] = useState7(null);
943
+ const [editLabel, setEditLabel] = useState7("");
944
+ const [editValue, setEditValue] = useState7("");
880
945
  const longPressTimer = useRef4(null);
881
946
  const longPressActivated = useRef4(false);
882
947
  const toggleTag = (label) => {
@@ -1325,7 +1390,7 @@ var PromptTab = ({
1325
1390
  };
1326
1391
 
1327
1392
  // src/components/ProjectSyncTab.tsx
1328
- import { useRef as useRef5, useState as useState6, useEffect as useEffect4 } from "react";
1393
+ import { useRef as useRef5, useState as useState8, useEffect as useEffect4 } from "react";
1329
1394
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1330
1395
  var ProjectSyncTab = ({
1331
1396
  topSlot,
@@ -1347,12 +1412,12 @@ var ProjectSyncTab = ({
1347
1412
  }) => {
1348
1413
  const projectInputRef = useRef5(null);
1349
1414
  const workspaceInputRef = useRef5(null);
1350
- const [saveName, setSaveName] = useState6("");
1351
- const [isSaving, setIsSaving] = useState6(false);
1352
- const [isExporting, setIsExporting] = useState6(false);
1353
- const [syncState, setSyncState] = useState6("idle");
1354
- const [syncDiff, setSyncDiff] = useState6(null);
1355
- const [selectedLocalIds, setSelectedLocalIds] = useState6(/* @__PURE__ */ new Set());
1415
+ const [saveName, setSaveName] = useState8("");
1416
+ const [isSaving, setIsSaving] = useState8(false);
1417
+ const [isExporting, setIsExporting] = useState8(false);
1418
+ const [syncState, setSyncState] = useState8("idle");
1419
+ const [syncDiff, setSyncDiff] = useState8(null);
1420
+ const [selectedLocalIds, setSelectedLocalIds] = useState8(/* @__PURE__ */ new Set());
1356
1421
  const handleExport = async () => {
1357
1422
  if (!onProjectExport) return;
1358
1423
  setIsExporting(true);
@@ -1398,13 +1463,13 @@ var ProjectSyncTab = ({
1398
1463
  });
1399
1464
  };
1400
1465
  const isWorking = projectActionState === "working" || projectActionState === "working-full";
1401
- const [hfProjects, setHfProjects] = useState6([]);
1402
- const [hfLoading, setHfLoading] = useState6(false);
1403
- const [hfSaving, setHfSaving] = useState6(false);
1404
- const [hfError, setHfError] = useState6(null);
1405
- const [hfSaveName, setHfSaveName] = useState6("");
1406
- const [hfSyncProgress, setHfSyncProgress] = useState6(null);
1407
- const [hfSyncing, setHfSyncing] = useState6(false);
1466
+ const [hfProjects, setHfProjects] = useState8([]);
1467
+ const [hfLoading, setHfLoading] = useState8(false);
1468
+ const [hfSaving, setHfSaving] = useState8(false);
1469
+ const [hfError, setHfError] = useState8(null);
1470
+ const [hfSaveName, setHfSaveName] = useState8("");
1471
+ const [hfSyncProgress, setHfSyncProgress] = useState8(null);
1472
+ const [hfSyncing, setHfSyncing] = useState8(false);
1408
1473
  const loadHfProjects = async (token) => {
1409
1474
  setHfLoading(true);
1410
1475
  setHfError(null);
@@ -1601,7 +1666,7 @@ var ProjectSyncTab = ({
1601
1666
  {
1602
1667
  onClick: async () => {
1603
1668
  try {
1604
- const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-TC65WQXK.mjs");
1669
+ const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-5BU5DVQ6.mjs");
1605
1670
  const file = await hfDownloadProject2(p.path, hfToken);
1606
1671
  onHfLoad(file);
1607
1672
  } catch (e) {
@@ -1823,7 +1888,7 @@ function toPromptImages(images) {
1823
1888
  }
1824
1889
 
1825
1890
  // src/hooks/useHFState.ts
1826
- import { useState as useState7, useEffect as useEffect5, useRef as useRef6, useCallback } from "react";
1891
+ import { useState as useState9, useEffect as useEffect5, useRef as useRef6, useCallback } from "react";
1827
1892
 
1828
1893
  // src/lib/hfReducer.ts
1829
1894
  function applyEvent(state, event) {
@@ -1844,6 +1909,13 @@ function applyEvent(state, event) {
1844
1909
  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 }];
1845
1910
  return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
1846
1911
  }
1912
+ case "thumb_added": {
1913
+ const p = event.payload;
1914
+ return {
1915
+ ...state,
1916
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, hasThumb: true } : m)
1917
+ };
1918
+ }
1847
1919
  case "metadata_updated": {
1848
1920
  const p = event.payload;
1849
1921
  return {
@@ -1851,6 +1923,13 @@ function applyEvent(state, event) {
1851
1923
  metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
1852
1924
  };
1853
1925
  }
1926
+ case "title_set": {
1927
+ const p = event.payload;
1928
+ return {
1929
+ ...state,
1930
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, titleTs: event.ts } : m)
1931
+ };
1932
+ }
1854
1933
  default:
1855
1934
  return state;
1856
1935
  }
@@ -1941,15 +2020,15 @@ function writeOfflineBuffer(events) {
1941
2020
  }
1942
2021
  }
1943
2022
  function useHFState(token, namespace) {
1944
- const [state, setState] = useState7(null);
1945
- const [isLoading, setIsLoading] = useState7(false);
1946
- const [error, setError] = useState7(null);
1947
- const [eventCount, setEventCount] = useState7(0);
1948
- const [localOnlyCount, setLocalOnlyCount] = useState7(0);
1949
- const [forks, setForks] = useState7([]);
1950
- const [pendingBufferCount, setPendingBufferCount] = useState7(readOfflineBuffer().length);
1951
- const [lastEventTs, setLastEventTs] = useState7(0);
1952
- const [hasStateZip, setHasStateZip] = useState7(false);
2023
+ const [state, setState] = useState9(null);
2024
+ const [isLoading, setIsLoading] = useState9(false);
2025
+ const [error, setError] = useState9(null);
2026
+ const [eventCount, setEventCount] = useState9(0);
2027
+ const [localOnlyCount, setLocalOnlyCount] = useState9(0);
2028
+ const [forks, setForks] = useState9([]);
2029
+ const [pendingBufferCount, setPendingBufferCount] = useState9(readOfflineBuffer().length);
2030
+ const [lastEventTs, setLastEventTs] = useState9(0);
2031
+ const [hasStateZip, setHasStateZip] = useState9(false);
1953
2032
  const knownEventPaths = useRef6(/* @__PURE__ */ new Set());
1954
2033
  const allEventsRef = useRef6([]);
1955
2034
  const applyNewEvents = useCallback((snapshot, newEvents) => {
@@ -2085,13 +2164,13 @@ function useHFState(token, namespace) {
2085
2164
  }
2086
2165
 
2087
2166
  // src/components/labs/LabsTab.tsx
2088
- import { useState as useState14 } from "react";
2167
+ import { useState as useState16 } from "react";
2089
2168
 
2090
2169
  // src/components/labs/LabRemix.tsx
2091
- import { useState as useState9 } from "react";
2170
+ import { useState as useState11 } from "react";
2092
2171
 
2093
2172
  // src/components/labs/LabImagePicker.tsx
2094
- import { useState as useState8 } from "react";
2173
+ import { useState as useState10 } from "react";
2095
2174
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2096
2175
  var LabImagePicker = ({
2097
2176
  availableItems,
@@ -2100,8 +2179,8 @@ var LabImagePicker = ({
2100
2179
  onClose,
2101
2180
  title = "Bild w\xE4hlen"
2102
2181
  }) => {
2103
- const [search, setSearch] = useState8("");
2104
- const [drillItem, setDrillItem] = useState8(null);
2182
+ const [search, setSearch] = useState10("");
2183
+ const [drillItem, setDrillItem] = useState10(null);
2105
2184
  const filtered = availableItems.filter(
2106
2185
  (item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
2107
2186
  );
@@ -2203,13 +2282,13 @@ var LabImagePicker = ({
2203
2282
  // src/components/labs/LabRemix.tsx
2204
2283
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2205
2284
  var LabRemix = ({ services, onResult }) => {
2206
- const [showPicker, setShowPicker] = useState9(false);
2207
- const [selected, setSelected] = useState9(null);
2208
- const [instruction, setInstruction] = useState9("");
2209
- const [generatedPrompt, setGeneratedPrompt] = useState9("");
2210
- const [resultImage, setResultImage] = useState9(null);
2211
- const [isGeneratingPrompt, setIsGeneratingPrompt] = useState9(false);
2212
- const [isGeneratingImage, setIsGeneratingImage] = useState9(false);
2285
+ const [showPicker, setShowPicker] = useState11(false);
2286
+ const [selected, setSelected] = useState11(null);
2287
+ const [instruction, setInstruction] = useState11("");
2288
+ const [generatedPrompt, setGeneratedPrompt] = useState11("");
2289
+ const [resultImage, setResultImage] = useState11(null);
2290
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState11(false);
2291
+ const [isGeneratingImage, setIsGeneratingImage] = useState11(false);
2213
2292
  const handleSelectImage = (item, frame) => {
2214
2293
  services.onItemUsed(item);
2215
2294
  setSelected({
@@ -2392,16 +2471,16 @@ var LabRemix = ({ services, onResult }) => {
2392
2471
  };
2393
2472
 
2394
2473
  // src/components/labs/LabBlend.tsx
2395
- import { useState as useState10 } from "react";
2474
+ import { useState as useState12 } from "react";
2396
2475
  import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2397
2476
  var LabBlend = ({ services, onResult }) => {
2398
- const [showPickerFor, setShowPickerFor] = useState10(null);
2399
- const [selectedImages, setSelectedImages] = useState10([]);
2400
- const [instruction, setInstruction] = useState10("");
2401
- const [generatedPrompt, setGeneratedPrompt] = useState10("");
2402
- const [resultImage, setResultImage] = useState10(null);
2403
- const [isGeneratingPrompt, setIsGeneratingPrompt] = useState10(false);
2404
- const [isGeneratingImage, setIsGeneratingImage] = useState10(false);
2477
+ const [showPickerFor, setShowPickerFor] = useState12(null);
2478
+ const [selectedImages, setSelectedImages] = useState12([]);
2479
+ const [instruction, setInstruction] = useState12("");
2480
+ const [generatedPrompt, setGeneratedPrompt] = useState12("");
2481
+ const [resultImage, setResultImage] = useState12(null);
2482
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState12(false);
2483
+ const [isGeneratingImage, setIsGeneratingImage] = useState12(false);
2405
2484
  const handleSelectImage = (index, item, frame) => {
2406
2485
  services.onItemUsed(item);
2407
2486
  const newImg = {
@@ -2588,17 +2667,17 @@ var LabBlend = ({ services, onResult }) => {
2588
2667
  };
2589
2668
 
2590
2669
  // src/components/labs/LabCompare.tsx
2591
- import { useState as useState11 } from "react";
2670
+ import { useState as useState13 } from "react";
2592
2671
  import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2593
2672
  var LabCompare = ({ services, onResult }) => {
2594
- const [showPickerFor, setShowPickerFor] = useState11(null);
2595
- const [selectedImages, setSelectedImages] = useState11([]);
2596
- const [instruction, setInstruction] = useState11("");
2597
- const [analysis, setAnalysis] = useState11("");
2598
- const [generatedPrompt, setGeneratedPrompt] = useState11("");
2599
- const [resultImage, setResultImage] = useState11(null);
2600
- const [isAnalyzing, setIsAnalyzing] = useState11(false);
2601
- const [isGeneratingImage, setIsGeneratingImage] = useState11(false);
2673
+ const [showPickerFor, setShowPickerFor] = useState13(null);
2674
+ const [selectedImages, setSelectedImages] = useState13([]);
2675
+ const [instruction, setInstruction] = useState13("");
2676
+ const [analysis, setAnalysis] = useState13("");
2677
+ const [generatedPrompt, setGeneratedPrompt] = useState13("");
2678
+ const [resultImage, setResultImage] = useState13(null);
2679
+ const [isAnalyzing, setIsAnalyzing] = useState13(false);
2680
+ const [isGeneratingImage, setIsGeneratingImage] = useState13(false);
2602
2681
  const handleSelectImage = (index, item, frame) => {
2603
2682
  services.onItemUsed(item);
2604
2683
  const newImg = {
@@ -2761,14 +2840,14 @@ var LabCompare = ({ services, onResult }) => {
2761
2840
  };
2762
2841
 
2763
2842
  // src/components/labs/LabLoop.tsx
2764
- import { useState as useState12 } from "react";
2843
+ import { useState as useState14 } from "react";
2765
2844
  import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2766
2845
  var LabLoop = ({ services, onResult }) => {
2767
- const [rounds, setRounds] = useState12([]);
2768
- const [currentInstruction, setCurrentInstruction] = useState12("");
2769
- const [showPickerForRound, setShowPickerForRound] = useState12(null);
2770
- const [pendingImages, setPendingImages] = useState12([]);
2771
- const [isGenerating, setIsGenerating] = useState12(false);
2846
+ const [rounds, setRounds] = useState14([]);
2847
+ const [currentInstruction, setCurrentInstruction] = useState14("");
2848
+ const [showPickerForRound, setShowPickerForRound] = useState14(null);
2849
+ const [pendingImages, setPendingImages] = useState14([]);
2850
+ const [isGenerating, setIsGenerating] = useState14(false);
2772
2851
  const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
2773
2852
  const handleAddImage = (item, frame) => {
2774
2853
  services.onItemUsed(item);
@@ -2924,7 +3003,7 @@ var LabLoop = ({ services, onResult }) => {
2924
3003
  };
2925
3004
 
2926
3005
  // src/components/labs/LabFrameExtractor.tsx
2927
- import { useRef as useRef7, useState as useState13, useCallback as useCallback2 } from "react";
3006
+ import { useRef as useRef7, useState as useState15, useCallback as useCallback2 } from "react";
2928
3007
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2929
3008
  var formatTime = (s) => {
2930
3009
  const m = Math.floor(s / 60);
@@ -2941,12 +3020,12 @@ var LabFrameExtractor = ({
2941
3020
  const videoRef = useRef7(null);
2942
3021
  const canvasRef = useRef7(null);
2943
3022
  const cancelledRef = useRef7(false);
2944
- const [selectedItem, setSelectedItem] = useState13(null);
2945
- const [videoSrc, setVideoSrc] = useState13(null);
2946
- const [videoReady, setVideoReady] = useState13(false);
2947
- const [frames, setFrames] = useState13([]);
2948
- const [isExtracting, setIsExtracting] = useState13(false);
2949
- const [intervalSec, setIntervalSec] = useState13("1");
3023
+ const [selectedItem, setSelectedItem] = useState15(null);
3024
+ const [videoSrc, setVideoSrc] = useState15(null);
3025
+ const [videoReady, setVideoReady] = useState15(false);
3026
+ const [frames, setFrames] = useState15([]);
3027
+ const [isExtracting, setIsExtracting] = useState15(false);
3028
+ const [intervalSec, setIntervalSec] = useState15("1");
2950
3029
  const handleVideoSelect = (item) => {
2951
3030
  const mediaId = item.frames[0]?.mediaId;
2952
3031
  if (!mediaId) return;
@@ -3183,7 +3262,7 @@ var BASE_TABS = [
3183
3262
  ];
3184
3263
  var FRAMES_TAB = { key: "frames", label: "Frames", icon: "crop_original" };
3185
3264
  var LabsTab = ({ services, onResult, videoItems, resolveVideoUrl }) => {
3186
- const [activeTab, setActiveTab] = useState14("remix");
3265
+ const [activeTab, setActiveTab] = useState16("remix");
3187
3266
  const showFrames = !!(videoItems && resolveVideoUrl);
3188
3267
  const tabs = showFrames ? [...BASE_TABS, FRAMES_TAB] : BASE_TABS;
3189
3268
  return /* @__PURE__ */ jsxs17("div", { className: "flex flex-col h-full overflow-hidden", children: [
@@ -3217,19 +3296,19 @@ var LabsTab = ({ services, onResult, videoItems, resolveVideoUrl }) => {
3217
3296
  };
3218
3297
 
3219
3298
  // src/components/TagManagerPanel.tsx
3220
- import { useState as useState15 } from "react";
3299
+ import { useState as useState17 } from "react";
3221
3300
  import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
3222
3301
  function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
3223
3302
  const categories = Object.keys(workspaceTags.by_category).filter(
3224
3303
  (cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
3225
3304
  );
3226
- const [selectedCategory, setSelectedCategory] = useState15(categories[0] || "");
3305
+ const [selectedCategory, setSelectedCategory] = useState17(categories[0] || "");
3227
3306
  const effectiveCategory = categories.includes(selectedCategory) ? selectedCategory : categories[0] || "";
3228
- const [editingLabel, setEditingLabel] = useState15(null);
3229
- const [editState, setEditState] = useState15({ label: "", value: "" });
3230
- const [newTag, setNewTag] = useState15({ label: "", value: "" });
3231
- const [movingLabel, setMovingLabel] = useState15(null);
3232
- const [moveTarget, setMoveTarget] = useState15("");
3307
+ const [editingLabel, setEditingLabel] = useState17(null);
3308
+ const [editState, setEditState] = useState17({ label: "", value: "" });
3309
+ const [newTag, setNewTag] = useState17({ label: "", value: "" });
3310
+ const [movingLabel, setMovingLabel] = useState17(null);
3311
+ const [moveTarget, setMoveTarget] = useState17("");
3233
3312
  const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
3234
3313
  const otherCategories = categories.filter((c) => c !== effectiveCategory);
3235
3314
  const startEdit = (tag) => {
@@ -3425,7 +3504,7 @@ function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete,
3425
3504
  }
3426
3505
 
3427
3506
  // src/components/HFTestTab.tsx
3428
- import { useState as useState16 } from "react";
3507
+ import { useState as useState18 } from "react";
3429
3508
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
3430
3509
  var HF_BASE = "https://huggingface.co";
3431
3510
  var HF_REPO = "RolandSch/fa-app-state";
@@ -3618,7 +3697,7 @@ function tryFmt(s) {
3618
3697
  }
3619
3698
  }
3620
3699
  function CopyBtn({ text }) {
3621
- const [done, setDone] = useState16(false);
3700
+ const [done, setDone] = useState18(false);
3622
3701
  return /* @__PURE__ */ jsxs19(
3623
3702
  "button",
3624
3703
  {
@@ -3789,10 +3868,10 @@ function EventMonitor({ events, confirmedEventKeys, galleryItems, imageUploadSta
3789
3868
  ] })
3790
3869
  ] });
3791
3870
  }
3792
- function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEventKeys = /* @__PURE__ */ new Set(), imageUploadStatus = /* @__PURE__ */ new Map() }) {
3793
- const [selected, setSelected] = useState16(null);
3794
- const [results, setResults] = useState16({});
3795
- const [expanded, setExpanded] = useState16({});
3871
+ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEventKeys = /* @__PURE__ */ new Set(), imageUploadStatus = /* @__PURE__ */ new Map(), missingImages = [] }) {
3872
+ const [selected, setSelected] = useState18(null);
3873
+ const [results, setResults] = useState18({});
3874
+ const [expanded, setExpanded] = useState18({});
3796
3875
  const withResults = galleryItems.filter((g) => g.base64 && g.status === "done");
3797
3876
  const setRunning = (id) => setResults((r) => ({ ...r, [id]: { status: "running", steps: [], totalMs: 0 } }));
3798
3877
  const setDone = (id, steps, t0) => {
@@ -3859,6 +3938,33 @@ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEv
3859
3938
  )
3860
3939
  }
3861
3940
  ) }),
3941
+ missingImages.length > 0 && /* @__PURE__ */ jsx21("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx21(
3942
+ CollapsibleCard,
3943
+ {
3944
+ title: `Fehlende Bilder auf HF (${missingImages.length})`,
3945
+ icon: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "broken_image" }),
3946
+ defaultOpen: false,
3947
+ children: /* @__PURE__ */ jsxs19("div", { style: { padding: "8px 10px 4px" }, children: [
3948
+ /* @__PURE__ */ jsx21("div", { style: { fontSize: 11, color: "rgba(255,255,255,0.4)", marginBottom: 8 }, children: "Metadata-Eintr\xE4ge ohne Bild auf HuggingFace \u2014 Orphaned entries." }),
3949
+ /* @__PURE__ */ jsxs19("div", { style: { display: "flex", gap: 8, marginBottom: 8, fontSize: 11, color: "rgba(255,255,255,0.5)" }, children: [
3950
+ /* @__PURE__ */ jsxs19("span", { children: [
3951
+ "UUID (Flow-App): ",
3952
+ missingImages.filter((e) => !e.filename).length
3953
+ ] }),
3954
+ /* @__PURE__ */ jsx21("span", { children: "\xB7" }),
3955
+ /* @__PURE__ */ jsxs19("span", { children: [
3956
+ "Filename (Server-Upload): ",
3957
+ missingImages.filter((e) => !!e.filename).length
3958
+ ] })
3959
+ ] }),
3960
+ /* @__PURE__ */ jsx21("div", { style: { maxHeight: 300, overflowY: "auto", fontFamily: "monospace", fontSize: 10 }, children: missingImages.map((e) => /* @__PURE__ */ jsxs19("div", { style: { padding: "3px 0", borderBottom: "1px solid rgba(255,255,255,0.04)", color: "rgba(255,255,255,0.5)", display: "flex", gap: 8 }, children: [
3961
+ /* @__PURE__ */ jsx21("span", { style: { color: e.filename ? "#fb923c" : "#60a5fa", minWidth: 60 }, children: e.filename ? "filename" : "uuid" }),
3962
+ /* @__PURE__ */ jsx21("span", { style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: e.filename ?? e.id }),
3963
+ /* @__PURE__ */ jsx21("span", { style: { color: "rgba(255,255,255,0.3)" }, children: new Date(e.timestamp).toLocaleDateString("de") })
3964
+ ] }, e.id)) })
3965
+ ] })
3966
+ }
3967
+ ) }),
3862
3968
  /* @__PURE__ */ jsx21(
3863
3969
  CollapsibleCard,
3864
3970
  {
@@ -3963,75 +4069,44 @@ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEv
3963
4069
  }
3964
4070
 
3965
4071
  // src/components/ServerTab.tsx
3966
- import { useState as useState17, useEffect as useEffect6 } from "react";
3967
-
3968
- // src/lib/faHfServerService.ts
3969
- var FA_APP_SPACE = "https://rolandsch-fa-app.hf.space";
3970
- async function request(method, path, env = "prod", body, tokenOverride) {
3971
- const token = tokenOverride || getHFToken();
3972
- if (!token) throw new Error("fa-app gateway: kein HF Token gesetzt");
3973
- const res = await fetch(`${FA_APP_SPACE}/${path.replace(/^\//, "")}`, {
3974
- method,
3975
- headers: {
3976
- "Authorization": `Bearer ${token}`,
3977
- "X-Env": env,
3978
- ...body !== void 0 ? { "Content-Type": "application/json" } : {}
3979
- },
3980
- ...body !== void 0 ? { body: JSON.stringify(body) } : {}
3981
- });
3982
- if (!res.ok) {
3983
- const text = await res.text().catch(() => "");
3984
- throw new Error(`fa-app gateway ${method} /${path} [${env}] \u2192 ${res.status}: ${text.slice(0, 200)}`);
3985
- }
3986
- return res.json();
3987
- }
3988
- function faServerGet(path, env = "prod", token) {
3989
- return request("GET", path, env, void 0, token);
3990
- }
3991
- function faServerPost(path, body, env = "prod", token) {
3992
- return request("POST", path, env, body, token);
3993
- }
3994
- function faServerPut(path, body, env = "prod", token) {
3995
- return request("PUT", path, env, body, token);
3996
- }
3997
- function faServerDelete(path, env = "prod", token) {
3998
- return request("DELETE", path, env, void 0, token);
3999
- }
4000
-
4001
- // src/components/ServerTab.tsx
4072
+ import { useState as useState19, useEffect as useEffect6 } from "react";
4002
4073
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
4003
4074
  function StarRating({ rating = 0 }) {
4004
4075
  return /* @__PURE__ */ jsx22("div", { className: "flex gap-[2px]", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx22("span", { className: `material-symbols-outlined text-[12px] ${i <= rating ? "text-yellow-400" : "text-white/15"}`, children: "star" }, i)) });
4005
4076
  }
4006
- function ServerTab({ hfToken }) {
4007
- const token = hfToken || getHFToken() || "";
4008
- const hasToken = !!token;
4009
- const [env, setEnv] = useState17("prod");
4010
- const [step, setStep] = useState17("user");
4011
- const [users, setUsers] = useState17([]);
4012
- const [usersLoading, setUsersLoading] = useState17(false);
4013
- const [usersError, setUsersError] = useState17(null);
4014
- const [selectedUser, setSelectedUser] = useState17(null);
4015
- const [contexts, setContexts] = useState17([]);
4016
- const [contextsLoading, setContextsLoading] = useState17(false);
4017
- const [selectedContext, setSelectedContext] = useState17(null);
4018
- const [tags, setTags] = useState17([]);
4019
- const [items, setItems] = useState17([]);
4020
- const [libLoading, setLibLoading] = useState17(false);
4021
- const [libError, setLibError] = useState17(null);
4022
- const [activeTag, setActiveTag] = useState17(null);
4023
- const [preview, setPreview] = useState17(null);
4077
+ async function serverGet(baseUrl, path) {
4078
+ const url = `${baseUrl.replace(/\/$/, "")}${path}`;
4079
+ const res = await fetch(url, { credentials: "include" });
4080
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
4081
+ const json = await res.json();
4082
+ return json && typeof json === "object" && "data" in json ? json.data : json;
4083
+ }
4084
+ function ServerTab({ serverBaseUrl }) {
4085
+ const [step, setStep] = useState19("user");
4086
+ const [users, setUsers] = useState19([]);
4087
+ const [usersLoading, setUsersLoading] = useState19(false);
4088
+ const [usersError, setUsersError] = useState19(null);
4089
+ const [selectedUser, setSelectedUser] = useState19(null);
4090
+ const [contexts, setContexts] = useState19([]);
4091
+ const [contextsLoading, setContextsLoading] = useState19(false);
4092
+ const [selectedContext, setSelectedContext] = useState19(null);
4093
+ const [tags, setTags] = useState19([]);
4094
+ const [items, setItems] = useState19([]);
4095
+ const [libLoading, setLibLoading] = useState19(false);
4096
+ const [libError, setLibError] = useState19(null);
4097
+ const [activeTag, setActiveTag] = useState19(null);
4098
+ const [preview, setPreview] = useState19(null);
4024
4099
  useEffect6(() => {
4025
- if (!hasToken) return;
4100
+ if (!serverBaseUrl) return;
4026
4101
  setUsersLoading(true);
4027
4102
  setUsersError(null);
4028
- faServerGet("/api/v2/users", env, token).then(setUsers).catch((e) => setUsersError(String(e))).finally(() => setUsersLoading(false));
4029
- }, [env, hasToken]);
4103
+ serverGet(serverBaseUrl, "/api/v2/users").then(setUsers).catch((e) => setUsersError(String(e))).finally(() => setUsersLoading(false));
4104
+ }, [serverBaseUrl]);
4030
4105
  const selectUser = async (user) => {
4031
4106
  setSelectedUser(user);
4032
4107
  setContextsLoading(true);
4033
4108
  try {
4034
- const data = await faServerGet(`/api2/contexts?user_id=${user.id}`, env, token);
4109
+ const data = await serverGet(serverBaseUrl, `/api2/contexts?user_id=${user.id}`);
4035
4110
  if (data.length === 1) {
4036
4111
  await loadLibrary(user, data[0]);
4037
4112
  } else {
@@ -4051,12 +4126,11 @@ function ServerTab({ hfToken }) {
4051
4126
  setLibError(null);
4052
4127
  try {
4053
4128
  const [tagsRes, libRes] = await Promise.all([
4054
- faServerGet(`/api2/tags?user_id=${user.id}`, env, token),
4055
- faServerGet(`/api2/user-library?user_id=${user.id}&context_id=${ctx.id}&limit=100`, env, token)
4129
+ serverGet(serverBaseUrl, `/api2/tags?user_id=${user.id}`),
4130
+ serverGet(serverBaseUrl, `/api/storage/${user.id}/library`)
4056
4131
  ]);
4057
4132
  setTags(Array.isArray(tagsRes) ? tagsRes : []);
4058
- const raw = Array.isArray(libRes) ? libRes : libRes.data ?? [];
4059
- setItems(raw);
4133
+ setItems(Array.isArray(libRes) ? libRes : []);
4060
4134
  } catch (e) {
4061
4135
  setLibError(String(e));
4062
4136
  } finally {
@@ -4075,13 +4149,7 @@ function ServerTab({ hfToken }) {
4075
4149
  setLibError(null);
4076
4150
  };
4077
4151
  const filteredItems = activeTag ? items.filter((item) => item.tags?.some((t) => t.l === activeTag)) : items;
4078
- if (!hasToken) {
4079
- return /* @__PURE__ */ jsx22("div", { className: "flex items-center justify-center h-full p-6", children: /* @__PURE__ */ jsxs20("p", { className: "text-white/30 text-[12px] text-center", children: [
4080
- "Kein HF Token gesetzt.",
4081
- /* @__PURE__ */ jsx22("br", {}),
4082
- "Bitte zuerst im Setup-Tab einrichten."
4083
- ] }) });
4084
- }
4152
+ if (!serverBaseUrl) return null;
4085
4153
  return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col h-full min-h-0", children: [
4086
4154
  /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-white/8", children: [
4087
4155
  step !== "user" && /* @__PURE__ */ jsx22("button", { onClick: reset, className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx22("span", { className: "material-symbols-outlined text-[18px]", children: "arrow_back" }) }),
@@ -4089,16 +4157,7 @@ function ServerTab({ hfToken }) {
4089
4157
  step === "user" && "Server Browser",
4090
4158
  step === "context" && `${selectedUser?.username} \u2014 Kontext w\xE4hlen`,
4091
4159
  step === "library" && `${selectedUser?.username} / ${selectedContext?.label || selectedContext?.name || selectedContext?.id}`
4092
- ] }),
4093
- step === "user" && /* @__PURE__ */ jsx22("div", { className: "flex gap-1", children: ["prod", "dev"].map((e) => /* @__PURE__ */ jsx22(
4094
- "button",
4095
- {
4096
- onClick: () => setEnv(e),
4097
- className: `text-[10px] font-bold px-2 py-0.5 rounded-lg transition-colors ${env === e ? "bg-white/15 text-white" : "text-white/30 hover:text-white/60"}`,
4098
- children: e
4099
- },
4100
- e
4101
- )) })
4160
+ ] })
4102
4161
  ] }),
4103
4162
  step === "user" && /* @__PURE__ */ jsxs20("div", { className: "flex flex-col flex-1 min-h-0 overflow-y-auto p-3 gap-2", children: [
4104
4163
  usersLoading && /* @__PURE__ */ jsx22("p", { className: "text-white/30 text-[11px] text-center py-4", children: "Lade User\u2026" }),
@@ -4189,7 +4248,7 @@ function ServerTab({ hfToken }) {
4189
4248
 
4190
4249
  // src/components/AvatarArchitectApp.tsx
4191
4250
  import { Fragment as Fragment9, jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
4192
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
4251
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
4193
4252
  useEffect7(() => {
4194
4253
  const id = "flow-styles";
4195
4254
  if (!document.getElementById(id)) {
@@ -4199,28 +4258,28 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4199
4258
  document.head.appendChild(style);
4200
4259
  }
4201
4260
  }, []);
4202
- const [showStart, setShowStart] = useState18(true);
4203
- const [layoutChoice, setLayoutChoice] = useState18(() => {
4261
+ const [showStart, setShowStart] = useState20(true);
4262
+ const [layoutChoice, setLayoutChoice] = useState20(() => {
4204
4263
  try {
4205
4264
  return localStorage.getItem("aa-layout") || null;
4206
4265
  } catch {
4207
4266
  return null;
4208
4267
  }
4209
4268
  });
4210
- const [projectLoaded, setProjectLoaded] = useState18(false);
4211
- const [hfToken, setHfToken] = useState18(initialHfToken || "");
4212
- const [hfTokenInput, setHfTokenInput] = useState18(initialHfToken || "");
4213
- const [isLoadingFromHF, setIsLoadingFromHF] = useState18(false);
4214
- const [hfNamespaceLocal, setHfNamespaceLocal] = useState18(() => {
4269
+ const [projectLoaded, setProjectLoaded] = useState20(false);
4270
+ const [hfToken, setHfToken] = useState20(initialHfToken || "");
4271
+ const [hfTokenInput, setHfTokenInput] = useState20(initialHfToken || "");
4272
+ const [isLoadingFromHF, setIsLoadingFromHF] = useState20(false);
4273
+ const [hfNamespaceLocal, setHfNamespaceLocal] = useState20(() => {
4215
4274
  try {
4216
4275
  const stored = localStorage.getItem("aa-hf-namespace");
4217
4276
  if (stored !== null) return stored;
4218
- return allowDevNamespace ? "app.art-by-rolands.de/" : "";
4277
+ return "app.art-by-rolands.de/";
4219
4278
  } catch {
4220
4279
  return "";
4221
4280
  }
4222
4281
  });
4223
- const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState18(null);
4282
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState20(null);
4224
4283
  useEffect7(() => {
4225
4284
  if (hfNamespace !== void 0) return;
4226
4285
  const backendUrl = typeof window !== "undefined" ? window.BACKEND_URL || window.location.origin : null;
@@ -4243,9 +4302,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4243
4302
  refresh: refreshHF,
4244
4303
  hasStateZip
4245
4304
  } = useHFState(hfToken, effectiveNamespace);
4246
- const [imageUploadStatus, setImageUploadStatus] = useState18(/* @__PURE__ */ new Map());
4247
- const [bootstrapLog, setBootstrapLog] = useState18([]);
4248
- const [isBootstrapping, setIsBootstrapping] = useState18(false);
4305
+ const [imageUploadStatus, setImageUploadStatus] = useState20(/* @__PURE__ */ new Map());
4306
+ const [bootstrapLog, setBootstrapLog] = useState20([]);
4307
+ const [isBootstrapping, setIsBootstrapping] = useState20(false);
4308
+ const [hfMissingImages, setHfMissingImages] = useState20([]);
4249
4309
  const syncTopSlot = /* @__PURE__ */ jsxs21(Fragment9, { children: [
4250
4310
  localOnlyCount > 0 && /* @__PURE__ */ jsxs21("div", { style: { background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", padding: "4px 10px", fontSize: 11, color: "#fbbf24", borderRadius: 4, marginBottom: 4 }, children: [
4251
4311
  "\u26A0 ",
@@ -4304,10 +4364,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4304
4364
  setLayoutChoice(choice);
4305
4365
  setShowStart(false);
4306
4366
  };
4307
- const [nodes, setNodes] = useState18([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
4308
- const [edges, setEdges] = useState18([]);
4309
- const [history, setHistory] = useState18([]);
4310
- const [galleryItems, setGalleryItems] = useState18([]);
4367
+ const [nodes, setNodes] = useState20([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
4368
+ const [edges, setEdges] = useState20([]);
4369
+ const [history, setHistory] = useState20([]);
4370
+ const [galleryItems, setGalleryItems] = useState20([]);
4311
4371
  const galleryItemsRef = useRef8([]);
4312
4372
  useEffect7(() => {
4313
4373
  galleryItemsRef.current = galleryItems;
@@ -4325,7 +4385,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4325
4385
  model: m.model,
4326
4386
  tags: m.tags || [],
4327
4387
  timestamp: m.timestamp,
4328
- status: "done"
4388
+ status: "done",
4389
+ filename: m.filename,
4390
+ hasThumb: m.hasThumb
4329
4391
  }));
4330
4392
  setGalleryItems((prev) => {
4331
4393
  const localOnly = prev.filter((g) => !hfIds.has(g.id));
@@ -4337,77 +4399,83 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4337
4399
  const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4338
4400
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
4339
4401
  });
4340
- const COOLDOWN_MS = 5 * 60 * 1e3;
4341
- for (const entry of hfState.metadata) {
4342
- if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
4343
- const failedAt = hfImageNotFoundRef.current.get(entry.id);
4344
- if (failedAt && Date.now() - failedAt < COOLDOWN_MS) continue;
4345
- hfLoadImageAsBase64(entry.id, hfToken).then((b64) => {
4346
- if (!b64) {
4402
+ const sortedEntries = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
4403
+ (async () => {
4404
+ for (const entry of sortedEntries) {
4405
+ if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
4406
+ if (hfImageNotFoundRef.current.has(entry.id)) continue;
4407
+ try {
4408
+ const b64 = await hfLoadImageAsBase64(entry.id, hfToken, effectiveNamespace, entry.filename, void 0, entry.mimeType, entry.hasThumb);
4409
+ if (!b64) {
4410
+ hfImageNotFoundRef.current.set(entry.id, Date.now());
4411
+ setHfMissingImages((prev) => {
4412
+ if (prev.find((e) => e.id === entry.id)) return prev;
4413
+ return [...prev, { id: entry.id, filename: entry.filename, mimeType: entry.mimeType, timestamp: entry.timestamp }];
4414
+ });
4415
+ continue;
4416
+ }
4417
+ const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4418
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4419
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4420
+ } catch {
4347
4421
  hfImageNotFoundRef.current.set(entry.id, Date.now());
4348
- return;
4349
4422
  }
4350
- const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4351
- setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4352
- setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4353
- }).catch(() => {
4354
- hfImageNotFoundRef.current.set(entry.id, Date.now());
4355
- });
4356
- }
4423
+ }
4424
+ })();
4357
4425
  }, [hfState]);
4358
- const [activePrompt, setActivePrompt] = useState18("");
4359
- const [isSynthesizing, setIsSynthesizing] = useState18(false);
4360
- const [activeGenerationsCount, setActiveGenerationsCount] = useState18(0);
4361
- const [currentResult, setCurrentResult] = useState18(null);
4362
- const [focusedNodeId, setFocusedNodeId] = useState18(null);
4363
- const [leftTab, setLeftTab] = useState18("prompt");
4364
- const [promptFeedback, setPromptFeedback] = useState18(null);
4365
- const [lastPromptPayload, setLastPromptPayload] = useState18(null);
4366
- const [isPromptTabGenerating, setIsPromptTabGenerating] = useState18(false);
4367
- const [activeTab, setActiveTab] = useState18("history");
4368
- const [mobileTab, setMobileTab] = useState18("stage");
4369
- const [middlePanel, setMiddlePanel] = useState18("stage");
4370
- const [recentLabItems, setRecentLabItems] = useState18([]);
4371
- const [aspectRatio, setAspectRatio] = useState18("1:1");
4372
- const [selectedModel, setSelectedModel] = useState18("\u{1F34C} Nano Banana Pro");
4373
- const [seed, setSeed] = useState18(Math.floor(Math.random() * 1e6));
4374
- const [seedMode, setSeedMode] = useState18("random");
4375
- const [isLeftCollapsed, setIsLeftCollapsed] = useState18(false);
4376
- const [isRightCollapsed, setIsRightCollapsed] = useState18(false);
4377
- const [leftPanelWidth, setLeftPanelWidth] = useState18(() => {
4426
+ const [activePrompt, setActivePrompt] = useState20("");
4427
+ const [isSynthesizing, setIsSynthesizing] = useState20(false);
4428
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState20(0);
4429
+ const [currentResult, setCurrentResult] = useState20(null);
4430
+ const [focusedNodeId, setFocusedNodeId] = useState20(null);
4431
+ const [leftTab, setLeftTab] = useState20("prompt");
4432
+ const [promptFeedback, setPromptFeedback] = useState20(null);
4433
+ const [lastPromptPayload, setLastPromptPayload] = useState20(null);
4434
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState20(false);
4435
+ const [activeTab, setActiveTab] = useState20("history");
4436
+ const [mobileTab, setMobileTab] = useState20("stage");
4437
+ const [middlePanel, setMiddlePanel] = useState20("stage");
4438
+ const [recentLabItems, setRecentLabItems] = useState20([]);
4439
+ const [aspectRatio, setAspectRatio] = useState20("1:1");
4440
+ const [selectedModel, setSelectedModel] = useState20("\u{1F34C} Nano Banana Pro");
4441
+ const [seed, setSeed] = useState20(Math.floor(Math.random() * 1e6));
4442
+ const [seedMode, setSeedMode] = useState20("random");
4443
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState20(false);
4444
+ const [isRightCollapsed, setIsRightCollapsed] = useState20(false);
4445
+ const [leftPanelWidth, setLeftPanelWidth] = useState20(() => {
4378
4446
  try {
4379
4447
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
4380
4448
  } catch {
4381
4449
  return 260;
4382
4450
  }
4383
4451
  });
4384
- const [rightPanelWidth, setRightPanelWidth] = useState18(() => {
4452
+ const [rightPanelWidth, setRightPanelWidth] = useState20(() => {
4385
4453
  try {
4386
4454
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
4387
4455
  } catch {
4388
4456
  return 320;
4389
4457
  }
4390
4458
  });
4391
- const [isPromptCollapsed, setIsPromptCollapsed] = useState18(false);
4392
- const [projectActionState, setProjectActionState] = useState18("idle");
4459
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState20(false);
4460
+ const [projectActionState, setProjectActionState] = useState20("idle");
4393
4461
  const syncServerDataRef = useRef8(null);
4394
- const [workspaceTags, setWorkspaceTags] = useState18(null);
4395
- const [serverProjects, setServerProjects] = useState18([]);
4396
- const [isLoadingFromServer, setIsLoadingFromServer] = useState18(false);
4397
- const [highContrast, setHighContrast] = useState18(() => {
4462
+ const [workspaceTags, setWorkspaceTags] = useState20(null);
4463
+ const [serverProjects, setServerProjects] = useState20([]);
4464
+ const [isLoadingFromServer, setIsLoadingFromServer] = useState20(false);
4465
+ const [highContrast, setHighContrast] = useState20(() => {
4398
4466
  try {
4399
4467
  return localStorage.getItem("aa-contrast") === "high";
4400
4468
  } catch {
4401
4469
  return false;
4402
4470
  }
4403
4471
  });
4404
- const [activeReferenceId, setActiveReferenceId] = useState18(null);
4405
- const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState18(null);
4406
- const [isScanningImage, setIsScanningImage] = useState18(false);
4407
- const [touchStartX, setTouchStartX] = useState18(null);
4408
- const [isFullscreen, setIsFullscreen] = useState18(false);
4409
- const [zoomScale, setZoomScale] = useState18(1);
4410
- const [zoomOffset, setZoomOffset] = useState18({ x: 0, y: 0 });
4472
+ const [activeReferenceId, setActiveReferenceId] = useState20(null);
4473
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState20(null);
4474
+ const [isScanningImage, setIsScanningImage] = useState20(false);
4475
+ const [touchStartX, setTouchStartX] = useState20(null);
4476
+ const [isFullscreen, setIsFullscreen] = useState20(false);
4477
+ const [zoomScale, setZoomScale] = useState20(1);
4478
+ const [zoomOffset, setZoomOffset] = useState20({ x: 0, y: 0 });
4411
4479
  const lastPinchDist = useRef8(null);
4412
4480
  const lastTapTime = useRef8(0);
4413
4481
  const dragStart = useRef8(null);
@@ -4560,6 +4628,34 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4560
4628
  const goToNext = useCallback3(() => {
4561
4629
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
4562
4630
  }, [currentIndex, history]);
4631
+ const handleGallerySelect = useCallback3((g) => {
4632
+ setCurrentResult(g);
4633
+ setMobileTab("stage");
4634
+ if (g.filename && hfToken && !g.fullBase64) {
4635
+ hfLoadImageAsBase64(g.id, hfToken, effectiveNamespace, g.filename, void 0, g.mimeType, false).then((b64) => {
4636
+ if (!b64) return;
4637
+ const full = `data:${g.mimeType || "image/jpeg"};base64,${b64}`;
4638
+ setCurrentResult((prev) => prev?.id === g.id ? { ...prev, fullBase64: full } : prev);
4639
+ setGalleryItems((prev) => prev.map((item) => item.id === g.id ? { ...item, fullBase64: full } : item));
4640
+ }).catch(() => {
4641
+ });
4642
+ }
4643
+ }, [hfToken, effectiveNamespace]);
4644
+ const handleTitleSet = useCallback3((id) => {
4645
+ const ts = Date.now();
4646
+ setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
4647
+ setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
4648
+ if (hfToken && effectiveNamespace) {
4649
+ hfWriteEvent("title_set", { id }).catch(() => {
4650
+ });
4651
+ }
4652
+ }, [hfToken, effectiveNamespace, hfWriteEvent]);
4653
+ const currentGroup = useMemo2(() => {
4654
+ if (!currentResult?.prompt) return [];
4655
+ const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
4656
+ const group = groups.find((gr) => gr.prompt === currentResult.prompt);
4657
+ return group ? group.items : [];
4658
+ }, [galleryItems, currentResult?.prompt]);
4563
4659
  const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
4564
4660
  const isGenerating = activeGenerationsCount > 0;
4565
4661
  useKeyboardNavigation(history, currentResult, setCurrentResult);
@@ -4741,8 +4837,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4741
4837
  const handleDownloadSingle = async () => {
4742
4838
  if (!currentResult?.base64) return;
4743
4839
  try {
4744
- const base64Data = currentResult.base64.split(",")[1];
4745
- const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
4840
+ const downloadSrc = currentResult.fullBase64 || currentResult.base64;
4841
+ const base64Data = downloadSrc.split(",")[1];
4842
+ const mimeType = downloadSrc.split(";")[0].split(":")[1] || "image/png";
4746
4843
  let finalBase64 = base64Data;
4747
4844
  if (mimeType === "image/png") finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
4748
4845
  await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
@@ -4942,7 +5039,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4942
5039
  return { by_category: merged, all };
4943
5040
  };
4944
5041
  if (isFullscreen && currentResult?.base64) {
4945
- const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
5042
+ const fsRaw = currentResult.fullBase64 || currentResult.base64;
5043
+ const fsBase64 = fsRaw.startsWith("data:") ? fsRaw : `data:image/png;base64,${fsRaw}`;
4946
5044
  return /* @__PURE__ */ jsxs21(
4947
5045
  "div",
4948
5046
  {
@@ -5094,7 +5192,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5094
5192
  onClick: async () => {
5095
5193
  setIsLoadingFromHF(true);
5096
5194
  try {
5097
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-TC65WQXK.mjs");
5195
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-5BU5DVQ6.mjs");
5098
5196
  const projects = await hfListProjects2(hfToken);
5099
5197
  if (projects.length > 0) {
5100
5198
  const file = await hfDownloadProject2(projects[0].path, hfToken);
@@ -5189,7 +5287,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5189
5287
  setMobileTab("stage");
5190
5288
  }
5191
5289
  } }) }),
5192
- mobileTab === "server" && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0 relative", children: /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 }) }),
5290
+ mobileTab === "server" && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0 relative", children: /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl }) }),
5193
5291
  mobileTab === "tags" && workspaceTags && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx23(
5194
5292
  TagManagerPanel,
5195
5293
  {
@@ -5267,7 +5365,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5267
5365
  /* @__PURE__ */ jsx23("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
5268
5366
  /* @__PURE__ */ jsx23("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" })
5269
5367
  ] }),
5270
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
5368
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, className: "w-full h-full object-contain" }),
5271
5369
  !currentResult && /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5272
5370
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
5273
5371
  /* @__PURE__ */ jsx23("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
@@ -5285,6 +5383,49 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5285
5383
  ]
5286
5384
  }
5287
5385
  ),
5386
+ currentResult?.status === "done" && currentGroup.length > 1 && /* @__PURE__ */ jsx23("div", { className: "mt-2 flex gap-1.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: currentGroup.map((item) => /* @__PURE__ */ jsxs21("div", { style: { position: "relative", flexShrink: 0 }, children: [
5387
+ /* @__PURE__ */ jsx23(
5388
+ "div",
5389
+ {
5390
+ onClick: () => handleGallerySelect(item),
5391
+ style: {
5392
+ width: 44,
5393
+ height: 44,
5394
+ borderRadius: 8,
5395
+ overflow: "hidden",
5396
+ cursor: "pointer",
5397
+ border: item.id === currentResult.id ? "2px solid rgba(255,255,255,0.8)" : "2px solid rgba(255,255,255,0.15)",
5398
+ opacity: item.id === currentResult.id ? 1 : 0.55
5399
+ },
5400
+ children: /* @__PURE__ */ jsx23("img", { src: item.base64, style: { width: "100%", height: "100%", objectFit: "cover" }, alt: "" })
5401
+ }
5402
+ ),
5403
+ /* @__PURE__ */ jsx23(
5404
+ "button",
5405
+ {
5406
+ onClick: (e) => {
5407
+ e.stopPropagation();
5408
+ handleTitleSet(item.id);
5409
+ },
5410
+ style: {
5411
+ position: "absolute",
5412
+ bottom: 2,
5413
+ right: 2,
5414
+ width: 14,
5415
+ height: 14,
5416
+ background: item.titleTs ? "#f59e0b" : "rgba(0,0,0,0.75)",
5417
+ border: "1px solid rgba(255,255,255,0.25)",
5418
+ borderRadius: 3,
5419
+ cursor: "pointer",
5420
+ padding: 0,
5421
+ display: "flex",
5422
+ alignItems: "center",
5423
+ justifyContent: "center"
5424
+ },
5425
+ children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined", style: { fontSize: 9, color: item.titleTs ? "#fff" : "rgba(255,255,255,0.5)", lineHeight: 1 }, children: "star" })
5426
+ }
5427
+ )
5428
+ ] }, item.id)) }),
5288
5429
  currentResult?.status === "done" && /* @__PURE__ */ jsxs21("div", { className: "flex gap-2 mt-3", children: [
5289
5430
  /* @__PURE__ */ jsxs21("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: [
5290
5431
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
@@ -5307,10 +5448,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5307
5448
  hfToken && /* @__PURE__ */ jsx23("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__ */ jsx23("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
5308
5449
  ] }),
5309
5450
  /* @__PURE__ */ jsxs21("div", { className: "flex-1 overflow-hidden relative", children: [
5310
- activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
5311
- setCurrentResult(g);
5312
- setMobileTab("stage");
5313
- }, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5451
+ activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: handleGallerySelect, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5314
5452
  activeTab === "gallery" && /* @__PURE__ */ jsx23(
5315
5453
  MediaLibrary,
5316
5454
  {
@@ -5321,10 +5459,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5321
5459
  setGalleryItems((prev) => [...media.map((m) => ({ id: crypto.randomUUID(), nodeId: "1", base64: `data:${m.mimeType};base64,${m.base64}`, mediaId: m.mediaId, prompt: m.name || "Importiert", timestamp: Date.now(), status: "done", tags: [], type: "import" })), ...prev]);
5322
5460
  },
5323
5461
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
5324
- onSelect: (g) => {
5325
- setCurrentResult(g);
5326
- setMobileTab("stage");
5327
- },
5462
+ onSelect: handleGallerySelect,
5328
5463
  onGenerateReference: (item) => {
5329
5464
  handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true });
5330
5465
  setMobileTab("stage");
@@ -5364,7 +5499,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5364
5499
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "biotech" }),
5365
5500
  "HF"
5366
5501
  ] }),
5367
- /* @__PURE__ */ jsx23("button", { onClick: () => setActiveTab("server"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "server" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "storage" }) }),
5502
+ serverBaseUrl && /* @__PURE__ */ jsx23("button", { onClick: () => setActiveTab("server"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "server" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "storage" }) }),
5368
5503
  workspaceTags && /* @__PURE__ */ jsx23("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__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
5369
5504
  ] }),
5370
5505
  /* @__PURE__ */ jsxs21("div", { className: "flex-1 overflow-hidden relative", children: [
@@ -5447,10 +5582,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5447
5582
  galleryItems,
5448
5583
  allEvents: hfAllEvents,
5449
5584
  confirmedEventKeys: hfConfirmedKeys,
5450
- imageUploadStatus
5585
+ imageUploadStatus,
5586
+ missingImages: hfMissingImages
5451
5587
  }
5452
5588
  ) }),
5453
- activeTab === "server" && /* @__PURE__ */ jsx23("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 }) })
5589
+ activeTab === "server" && /* @__PURE__ */ jsx23("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl }) })
5454
5590
  ] })
5455
5591
  ] }),
5456
5592
  /* @__PURE__ */ jsx23("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
@@ -5459,7 +5595,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5459
5595
  { id: "labs", icon: "science", label: "Labs" },
5460
5596
  ...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
5461
5597
  { id: "browse", icon: "photo_library", label: "Galerie" },
5462
- { id: "server", icon: "storage", label: "Server" }
5598
+ ...serverBaseUrl ? [{ id: "server", icon: "storage", label: "Server" }] : []
5463
5599
  ].map((tab) => /* @__PURE__ */ jsxs21("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: [
5464
5600
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
5465
5601
  /* @__PURE__ */ jsx23("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
@@ -5510,7 +5646,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5510
5646
  ] })
5511
5647
  }
5512
5648
  ) }),
5513
- /* @__PURE__ */ jsx23("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
5649
+ /* @__PURE__ */ jsx23("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: handleGallerySelect, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
5514
5650
  ] }),
5515
5651
  /* @__PURE__ */ jsxs21("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
5516
5652
  /* @__PURE__ */ jsx23(
@@ -5535,7 +5671,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5535
5671
  /* @__PURE__ */ jsx23("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
5536
5672
  /* @__PURE__ */ jsx23("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" })
5537
5673
  ] }),
5538
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
5674
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
5539
5675
  !currentResult && /* @__PURE__ */ jsxs21("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
5540
5676
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
5541
5677
  /* @__PURE__ */ jsx23("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
@@ -5677,33 +5813,81 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5677
5813
  middlePanel === "labs" ? /* @__PURE__ */ jsx23("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx23(LabsTab, { services: labServices, onResult: (item) => {
5678
5814
  const frame = item.frames[0];
5679
5815
  if (frame?.base64) setCurrentResult(frameToGeneration(frame, item));
5680
- } }) }) : /* @__PURE__ */ jsx23("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs21("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: [
5681
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs21("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: [
5682
- /* @__PURE__ */ jsx23("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
5683
- /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
5684
- ] }),
5685
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-4", children: [
5686
- /* @__PURE__ */ jsx23("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5687
- /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5688
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs21("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
5689
- /* @__PURE__ */ jsx23("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
5690
- /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", children: [
5691
- /* @__PURE__ */ jsx23("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
5692
- /* @__PURE__ */ jsx23("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
5816
+ } }) }) : /* @__PURE__ */ jsxs21("div", { className: "flex-1 overflow-hidden flex flex-col", children: [
5817
+ /* @__PURE__ */ jsx23("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs21("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: [
5818
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs21("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: [
5819
+ /* @__PURE__ */ jsx23("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
5820
+ /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
5693
5821
  ] }),
5694
- /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
5695
- ] }) : /* @__PURE__ */ jsxs21("div", { className: "h-full w-full relative flex items-center justify-center", children: [
5696
- /* @__PURE__ */ jsx23("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
5697
- /* @__PURE__ */ jsxs21("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: [
5698
- /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
5699
- /* @__PURE__ */ jsx23(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
5700
- /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
5822
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-4", children: [
5823
+ /* @__PURE__ */ jsx23("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5824
+ /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5825
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs21("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
5826
+ /* @__PURE__ */ jsx23("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
5827
+ /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", children: [
5828
+ /* @__PURE__ */ jsx23("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
5829
+ /* @__PURE__ */ jsx23("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
5830
+ ] }),
5831
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
5832
+ ] }) : /* @__PURE__ */ jsxs21("div", { className: "h-full w-full relative flex items-center justify-center", children: [
5833
+ /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
5834
+ /* @__PURE__ */ jsxs21("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: [
5835
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
5836
+ /* @__PURE__ */ jsx23(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
5837
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
5838
+ ] })
5839
+ ] }) : /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5840
+ /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
5841
+ /* @__PURE__ */ jsx23("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5701
5842
  ] })
5702
- ] }) : /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5703
- /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
5704
- /* @__PURE__ */ jsx23("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5705
- ] })
5706
- ] }) })
5843
+ ] }) }),
5844
+ currentResult?.status === "done" && currentGroup.length > 1 && /* @__PURE__ */ jsx23("div", { className: "px-6 pb-4 shrink-0 flex gap-2 overflow-x-auto", style: { scrollbarWidth: "none" }, children: currentGroup.map((item) => /* @__PURE__ */ jsxs21("div", { style: { position: "relative", flexShrink: 0 }, children: [
5845
+ /* @__PURE__ */ jsx23(
5846
+ "div",
5847
+ {
5848
+ onClick: () => handleGallerySelect(item),
5849
+ style: {
5850
+ width: 52,
5851
+ height: 52,
5852
+ borderRadius: 10,
5853
+ overflow: "hidden",
5854
+ cursor: "pointer",
5855
+ border: item.id === currentResult.id ? "2px solid rgba(255,255,255,0.8)" : "2px solid rgba(255,255,255,0.12)",
5856
+ opacity: item.id === currentResult.id ? 1 : 0.5,
5857
+ transition: "opacity 0.15s, border-color 0.15s"
5858
+ },
5859
+ className: "hover:opacity-80",
5860
+ children: /* @__PURE__ */ jsx23("img", { src: item.base64, style: { width: "100%", height: "100%", objectFit: "cover" }, alt: "" })
5861
+ }
5862
+ ),
5863
+ /* @__PURE__ */ jsx23(
5864
+ "button",
5865
+ {
5866
+ onClick: (e) => {
5867
+ e.stopPropagation();
5868
+ handleTitleSet(item.id);
5869
+ },
5870
+ title: "Als Titel markieren",
5871
+ style: {
5872
+ position: "absolute",
5873
+ bottom: 3,
5874
+ right: 3,
5875
+ width: 16,
5876
+ height: 16,
5877
+ background: item.titleTs ? "#f59e0b" : "rgba(0,0,0,0.75)",
5878
+ border: "1px solid rgba(255,255,255,0.25)",
5879
+ borderRadius: 4,
5880
+ cursor: "pointer",
5881
+ padding: 0,
5882
+ display: "flex",
5883
+ alignItems: "center",
5884
+ justifyContent: "center"
5885
+ },
5886
+ children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined", style: { fontSize: 10, color: item.titleTs ? "#fff" : "rgba(255,255,255,0.5)", lineHeight: 1 }, children: "star" })
5887
+ }
5888
+ )
5889
+ ] }, item.id)) })
5890
+ ] })
5707
5891
  ] })
5708
5892
  ] }),
5709
5893
  !isRightCollapsed && /* @__PURE__ */ jsx23("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
@@ -5714,7 +5898,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5714
5898
  setActiveTab(tab);
5715
5899
  setIsRightCollapsed(false);
5716
5900
  }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx23("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)),
5717
- /* @__PURE__ */ jsx23("button", { onClick: () => {
5901
+ serverBaseUrl && /* @__PURE__ */ jsx23("button", { onClick: () => {
5718
5902
  setActiveTab("server");
5719
5903
  setIsRightCollapsed(false);
5720
5904
  }, className: `w-10 flex items-center justify-center relative transition-colors ${activeTab === "server" ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "storage" }) })
@@ -5738,7 +5922,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5738
5922
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
5739
5923
  /* @__PURE__ */ jsx23("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
5740
5924
  ] }) }),
5741
- activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5925
+ activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: handleGallerySelect, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5742
5926
  activeTab === "gallery" && /* @__PURE__ */ jsx23(
5743
5927
  MediaLibrary,
5744
5928
  {
@@ -5749,7 +5933,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5749
5933
  setGalleryItems((prev) => [...media.map((m) => ({ id: crypto.randomUUID(), nodeId: "1", base64: `data:${m.mimeType};base64,${m.base64}`, mediaId: m.mediaId, prompt: m.name || "Importiert", timestamp: Date.now(), status: "done", tags: [], type: "import" })), ...prev]);
5750
5934
  },
5751
5935
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
5752
- onSelect: setCurrentResult,
5936
+ onSelect: handleGallerySelect,
5753
5937
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
5754
5938
  }
5755
5939
  ),
@@ -5779,14 +5963,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5779
5963
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
5780
5964
  }
5781
5965
  ),
5782
- activeTab === "server" && /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 })
5966
+ activeTab === "server" && /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl })
5783
5967
  ] })
5784
5968
  ] })
5785
5969
  ] });
5786
5970
  }
5787
5971
 
5788
5972
  // src/components/FaApp.tsx
5789
- import { useState as useState19, useEffect as useEffect8 } from "react";
5973
+ import { useState as useState21, useEffect as useEffect8 } from "react";
5790
5974
  import { jsx as jsx24 } from "react/jsx-runtime";
5791
5975
  function FaApp({
5792
5976
  onGenerateImage,
@@ -5806,7 +5990,7 @@ function FaApp({
5806
5990
  onServerDelete,
5807
5991
  buildInfo
5808
5992
  }) {
5809
- const [hfNamespace, setHfNamespace] = useState19(void 0);
5993
+ const [hfNamespace, setHfNamespace] = useState21(void 0);
5810
5994
  useEffect8(() => {
5811
5995
  if (!serverBaseUrl) return;
5812
5996
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
@@ -5828,6 +6012,7 @@ function FaApp({
5828
6012
  initialHfToken: libToken ? libToken.startsWith("hf_") ? libToken : `hf_${libToken}` : void 0,
5829
6013
  hfNamespace,
5830
6014
  allowDevNamespace: !hfNamespace && allowDevNamespace,
6015
+ serverBaseUrl,
5831
6016
  onFetchServerProjects,
5832
6017
  onServerSave,
5833
6018
  onServerLoad,
@@ -5838,7 +6023,7 @@ function FaApp({
5838
6023
  }
5839
6024
 
5840
6025
  // src/index.ts
5841
- var LIB_VERSION = "2.0.30";
6026
+ var LIB_VERSION = "2.0.48";
5842
6027
  export {
5843
6028
  AvatarArchitectApp,
5844
6029
  CollapsibleCard,
@@ -5882,10 +6067,6 @@ export {
5882
6067
  cleanAiResponse,
5883
6068
  createFlowServices,
5884
6069
  exportProjectToZip,
5885
- faServerDelete,
5886
- faServerGet,
5887
- faServerPost,
5888
- faServerPut,
5889
6070
  findForks,
5890
6071
  findTips,
5891
6072
  formatTreeToMarkdown,