@rslsp1/fa-app-tools 2.0.30 → 2.0.46

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,33 @@ 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";
358
+
359
+ // src/lib/grouping.ts
360
+ function groupByPrompt(items) {
361
+ const map = /* @__PURE__ */ new Map();
362
+ for (const item of items) {
363
+ const key = item.prompt ?? "";
364
+ if (!map.has(key)) map.set(key, []);
365
+ map.get(key).push(item);
366
+ }
367
+ const groups = [];
368
+ for (const [prompt, groupItems] of map) {
369
+ const titled = groupItems.filter((i) => i.titleTs);
370
+ 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);
371
+ groups.push({ prompt, items: groupItems, representative });
372
+ }
373
+ return groups.sort((a, b) => {
374
+ const aMax = Math.max(...a.items.map((i) => i.timestamp));
375
+ const bMax = Math.max(...b.items.map((i) => i.timestamp));
376
+ return bMax - aMax;
377
+ });
378
+ }
379
+
380
+ // src/components/HistoryPanel.tsx
357
381
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
382
+ var PAGE_SIZE = 20;
358
383
  var formatFriendlyTimestamp = (timestamp) => {
359
384
  const date = new Date(timestamp);
360
385
  const now = /* @__PURE__ */ new Date();
@@ -367,38 +392,75 @@ var formatFriendlyTimestamp = (timestamp) => {
367
392
  return `${date.toLocaleDateString([], { day: "2-digit", month: "2-digit" })}, ${timeStr}`;
368
393
  };
369
394
  var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
395
+ const [visibleCount, setVisibleCount] = useState2(PAGE_SIZE);
396
+ const doneItems = history.filter((g) => g.status === "done");
397
+ const processingItems = history.filter((g) => g.status !== "done");
398
+ const groups = groupByPrompt(doneItems);
399
+ const visibleGroups = groups.slice(0, visibleCount);
370
400
  if (history.length === 0) {
371
401
  return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
372
402
  /* @__PURE__ */ jsx5("span", { className: "material-symbols-outlined text-[64px]", children: "history" }),
373
403
  /* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest", children: "Keine Historie" })
374
404
  ] });
375
405
  }
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 })
387
- ] }),
388
- /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
389
- '"',
390
- gen.prompt,
391
- '"'
406
+ 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: [
407
+ processingItems.map((gen) => /* @__PURE__ */ jsxs3(
408
+ "div",
409
+ {
410
+ className: "flex gap-3 p-2 bg-white/5 border border-white/5 rounded-2xl cursor-pointer group transition-all relative",
411
+ onClick: () => onSelect(gen),
412
+ children: [
413
+ /* @__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: /* @__PURE__ */ jsx5("div", { className: "w-5 h-5 border-t-2 border-white rounded-full animate-spin" }) }),
414
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
415
+ /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase", children: "Generiere..." }),
416
+ /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/40 line-clamp-2 leading-tight mt-1", children: [
417
+ '"',
418
+ gen.prompt,
419
+ '"'
420
+ ] })
392
421
  ] })
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
- )) });
422
+ ]
423
+ },
424
+ gen.id
425
+ )),
426
+ visibleGroups.map((group) => {
427
+ const rep = group.representative;
428
+ const isActive = group.items.some((i) => i.id === currentResultId);
429
+ return /* @__PURE__ */ jsxs3(
430
+ "div",
431
+ {
432
+ className: `flex gap-3 p-2 bg-white/5 border rounded-2xl cursor-pointer group transition-all relative ${isActive ? "border-white/40 bg-white/10 shadow-lg" : "border-white/5 hover:border-white/20"}`,
433
+ onClick: () => onSelect(rep),
434
+ children: [
435
+ /* @__PURE__ */ jsxs3("div", { className: "w-14 h-14 rounded-xl overflow-hidden bg-black shrink-0 border border-white/5 relative", children: [
436
+ /* @__PURE__ */ jsx5("img", { src: rep.base64 ? rep.base64.startsWith("data:") ? rep.base64 : `data:image/png;base64,${rep.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }),
437
+ group.items.length > 1 && /* @__PURE__ */ jsx5("div", { className: "absolute bottom-0.5 right-0.5 px-1 bg-black/80 rounded text-[7px] font-bold text-white/70", children: group.items.length })
438
+ ] }),
439
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 py-0.5 overflow-hidden", children: [
440
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1", children: [
441
+ /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/20 uppercase whitespace-nowrap", children: formatFriendlyTimestamp(rep.timestamp) }),
442
+ rep.model && /* @__PURE__ */ jsx5("span", { className: "text-[7px] font-bold text-white/40 bg-white/5 px-1 rounded uppercase truncate max-w-[60px]", children: rep.model })
443
+ ] }),
444
+ /* @__PURE__ */ jsxs3("p", { className: "text-[9px] text-white/60 line-clamp-2 leading-tight", children: [
445
+ '"',
446
+ rep.prompt,
447
+ '"'
448
+ ] })
449
+ ] }),
450
+ /* @__PURE__ */ jsx5("button", { onClick: (e) => {
451
+ e.stopPropagation();
452
+ onDelete(rep.id);
453
+ }, 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" }) })
454
+ ]
455
+ },
456
+ rep.id
457
+ );
458
+ }),
459
+ visibleCount < groups.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: [
460
+ groups.length - visibleCount,
461
+ " weitere laden"
462
+ ] })
463
+ ] });
402
464
  };
403
465
 
404
466
  // src/components/InspectPanel.tsx
@@ -467,7 +529,7 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
467
529
  };
468
530
 
469
531
  // src/components/SetupPanel.tsx
470
- import { useRef as useRef2, useState as useState2 } from "react";
532
+ import { useRef as useRef2, useState as useState3 } from "react";
471
533
  import { motion as motion3 } from "motion/react";
472
534
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
473
535
  var PRESET_URLS = [
@@ -477,11 +539,11 @@ var PRESET_URLS = [
477
539
  ];
478
540
  var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
479
541
  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);
542
+ const [urlInput, setUrlInput] = useState3("");
543
+ const [tokenInput, setTokenInput] = useState3("");
544
+ const [testStatus, setTestStatus] = useState3("idle");
545
+ const [result, setResult] = useState3(null);
546
+ const [fetchError, setFetchError] = useState3(null);
485
547
  const runTest = async (url) => {
486
548
  if (!url.trim()) return;
487
549
  setTestStatus("loading");
@@ -644,18 +706,25 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
644
706
  };
645
707
 
646
708
  // src/components/MediaLibrary.tsx
709
+ import { useState as useState4 } from "react";
647
710
  import { motion as motion4 } from "motion/react";
648
711
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
712
+ var PAGE_SIZE2 = 20;
649
713
  var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
714
+ const [visibleCount, setVisibleCount] = useState4(PAGE_SIZE2);
650
715
  const selectedCount = items.filter((i) => i.selectedForExport).length;
716
+ const groups = groupByPrompt(items);
717
+ const visibleGroups = groups.slice(0, visibleCount);
651
718
  return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col h-full overflow-hidden", children: [
652
719
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col p-4 border-b border-white/5 gap-3 bg-black/20", children: [
653
720
  /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
654
721
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col", children: [
655
722
  /* @__PURE__ */ jsx8("span", { className: "text-[10px] font-bold text-white uppercase tracking-widest", children: "Projekt Galerie" }),
656
723
  /* @__PURE__ */ jsxs6("span", { className: "text-[9px] text-white/30 uppercase tracking-tighter", children: [
724
+ visibleCount < groups.length ? `${visibleCount} / ${groups.length}` : groups.length,
725
+ " Gruppen \xB7 ",
657
726
  items.length,
658
- " Assets geladen"
727
+ " Assets"
659
728
  ] })
660
729
  ] }),
661
730
  /* @__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 +743,57 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
674
743
  ] })
675
744
  ] })
676
745
  ] }),
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: [
746
+ /* @__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
747
  /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[64px]", children: "photo_library" }),
679
748
  /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-1", children: [
680
749
  /* @__PURE__ */ jsx8("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
681
750
  /* @__PURE__ */ jsx8("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
682
751
  ] })
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)) }) })
752
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "grid grid-cols-2 gap-3 pb-10", children: [
753
+ visibleGroups.map((group) => {
754
+ const rep = group.representative;
755
+ return /* @__PURE__ */ jsxs6(
756
+ motion4.div,
757
+ {
758
+ initial: { opacity: 0, scale: 0.9 },
759
+ animate: { opacity: 1, scale: 1 },
760
+ 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",
761
+ onClick: () => onSelect(rep),
762
+ children: [
763
+ /* @__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 }),
764
+ 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: [
765
+ /* @__PURE__ */ jsx8("span", { className: "material-symbols-outlined text-[10px]", children: "photo_library" }),
766
+ group.items.length
767
+ ] }),
768
+ /* @__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: [
769
+ /* @__PURE__ */ jsx8("span", { className: "text-[8px] font-bold text-white/80 truncate max-w-[80px] uppercase tracking-tighter", children: rep.prompt || "Importiert" }),
770
+ /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
771
+ onGenerateReference && /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
772
+ e.stopPropagation();
773
+ onGenerateReference(rep);
774
+ }, 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" }) }),
775
+ /* @__PURE__ */ jsx8("button", { type: "button", onClick: (e) => {
776
+ e.stopPropagation();
777
+ onDelete(rep.id);
778
+ }, 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" }) })
779
+ ] })
780
+ ] }) }),
781
+ /* @__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" })
782
+ ]
783
+ },
784
+ rep.id
785
+ );
786
+ }),
787
+ 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: [
788
+ groups.length - visibleCount,
789
+ " weitere laden"
790
+ ] }) })
791
+ ] }) })
707
792
  ] });
708
793
  };
709
794
 
710
795
  // src/components/ListView.tsx
711
- import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
796
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
712
797
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
713
798
  var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
714
799
  const inputRef = useRef3(null);
@@ -773,7 +858,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
773
858
  ] });
774
859
  };
775
860
  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());
861
+ const [collapsed, setCollapsed] = useState5(/* @__PURE__ */ new Set());
777
862
  const toggleCollapse = (id) => {
778
863
  setCollapsed((prev) => {
779
864
  const next = new Set(prev);
@@ -801,13 +886,13 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
801
886
  }
802
887
 
803
888
  // src/components/AvatarArchitectApp.tsx
804
- import { useState as useState18, useCallback as useCallback3, useMemo as useMemo2, useEffect as useEffect7, useRef as useRef8 } from "react";
889
+ import { useState as useState20, useCallback as useCallback3, useMemo as useMemo2, useEffect as useEffect7, useRef as useRef8 } from "react";
805
890
 
806
891
  // src/components/PromptTab.tsx
807
- import { useRef as useRef4, useState as useState5 } from "react";
892
+ import { useRef as useRef4, useState as useState7 } from "react";
808
893
 
809
894
  // src/components/CollapsibleCard.tsx
810
- import { useState as useState4 } from "react";
895
+ import { useState as useState6 } from "react";
811
896
  import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
812
897
  var CollapsibleCard = ({
813
898
  title,
@@ -818,7 +903,7 @@ var CollapsibleCard = ({
818
903
  collapsible = true,
819
904
  className = ""
820
905
  }) => {
821
- const [isOpen, setIsOpen] = useState4(defaultOpen);
906
+ const [isOpen, setIsOpen] = useState6(defaultOpen);
822
907
  return /* @__PURE__ */ jsxs8("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
823
908
  /* @__PURE__ */ jsxs8(
824
909
  "div",
@@ -865,18 +950,18 @@ var PromptTab = ({
865
950
  onTagUpdate,
866
951
  onTagDelete
867
952
  }) => {
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);
953
+ const [selectedLabels, setSelectedLabels] = useState7(/* @__PURE__ */ new Set());
954
+ const [instructions, setInstructions] = useState7("");
955
+ const [rules, setRules] = useState7("");
956
+ const [activeCategory, setActiveCategory] = useState7(null);
957
+ const [copied, setCopied] = useState7(false);
873
958
  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("");
959
+ const [addingInCat, setAddingInCat] = useState7(null);
960
+ const [newLabel, setNewLabel] = useState7("");
961
+ const [newValue, setNewValue] = useState7("");
962
+ const [editingTag, setEditingTag] = useState7(null);
963
+ const [editLabel, setEditLabel] = useState7("");
964
+ const [editValue, setEditValue] = useState7("");
880
965
  const longPressTimer = useRef4(null);
881
966
  const longPressActivated = useRef4(false);
882
967
  const toggleTag = (label) => {
@@ -1325,7 +1410,7 @@ var PromptTab = ({
1325
1410
  };
1326
1411
 
1327
1412
  // src/components/ProjectSyncTab.tsx
1328
- import { useRef as useRef5, useState as useState6, useEffect as useEffect4 } from "react";
1413
+ import { useRef as useRef5, useState as useState8, useEffect as useEffect4 } from "react";
1329
1414
  import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1330
1415
  var ProjectSyncTab = ({
1331
1416
  topSlot,
@@ -1347,12 +1432,12 @@ var ProjectSyncTab = ({
1347
1432
  }) => {
1348
1433
  const projectInputRef = useRef5(null);
1349
1434
  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());
1435
+ const [saveName, setSaveName] = useState8("");
1436
+ const [isSaving, setIsSaving] = useState8(false);
1437
+ const [isExporting, setIsExporting] = useState8(false);
1438
+ const [syncState, setSyncState] = useState8("idle");
1439
+ const [syncDiff, setSyncDiff] = useState8(null);
1440
+ const [selectedLocalIds, setSelectedLocalIds] = useState8(/* @__PURE__ */ new Set());
1356
1441
  const handleExport = async () => {
1357
1442
  if (!onProjectExport) return;
1358
1443
  setIsExporting(true);
@@ -1398,13 +1483,13 @@ var ProjectSyncTab = ({
1398
1483
  });
1399
1484
  };
1400
1485
  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);
1486
+ const [hfProjects, setHfProjects] = useState8([]);
1487
+ const [hfLoading, setHfLoading] = useState8(false);
1488
+ const [hfSaving, setHfSaving] = useState8(false);
1489
+ const [hfError, setHfError] = useState8(null);
1490
+ const [hfSaveName, setHfSaveName] = useState8("");
1491
+ const [hfSyncProgress, setHfSyncProgress] = useState8(null);
1492
+ const [hfSyncing, setHfSyncing] = useState8(false);
1408
1493
  const loadHfProjects = async (token) => {
1409
1494
  setHfLoading(true);
1410
1495
  setHfError(null);
@@ -1601,7 +1686,7 @@ var ProjectSyncTab = ({
1601
1686
  {
1602
1687
  onClick: async () => {
1603
1688
  try {
1604
- const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-TC65WQXK.mjs");
1689
+ const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-5BU5DVQ6.mjs");
1605
1690
  const file = await hfDownloadProject2(p.path, hfToken);
1606
1691
  onHfLoad(file);
1607
1692
  } catch (e) {
@@ -1823,7 +1908,7 @@ function toPromptImages(images) {
1823
1908
  }
1824
1909
 
1825
1910
  // src/hooks/useHFState.ts
1826
- import { useState as useState7, useEffect as useEffect5, useRef as useRef6, useCallback } from "react";
1911
+ import { useState as useState9, useEffect as useEffect5, useRef as useRef6, useCallback } from "react";
1827
1912
 
1828
1913
  // src/lib/hfReducer.ts
1829
1914
  function applyEvent(state, event) {
@@ -1844,6 +1929,13 @@ function applyEvent(state, event) {
1844
1929
  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
1930
  return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
1846
1931
  }
1932
+ case "thumb_added": {
1933
+ const p = event.payload;
1934
+ return {
1935
+ ...state,
1936
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, hasThumb: true } : m)
1937
+ };
1938
+ }
1847
1939
  case "metadata_updated": {
1848
1940
  const p = event.payload;
1849
1941
  return {
@@ -1851,6 +1943,13 @@ function applyEvent(state, event) {
1851
1943
  metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
1852
1944
  };
1853
1945
  }
1946
+ case "title_set": {
1947
+ const p = event.payload;
1948
+ return {
1949
+ ...state,
1950
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, titleTs: event.ts } : m)
1951
+ };
1952
+ }
1854
1953
  default:
1855
1954
  return state;
1856
1955
  }
@@ -1941,15 +2040,15 @@ function writeOfflineBuffer(events) {
1941
2040
  }
1942
2041
  }
1943
2042
  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);
2043
+ const [state, setState] = useState9(null);
2044
+ const [isLoading, setIsLoading] = useState9(false);
2045
+ const [error, setError] = useState9(null);
2046
+ const [eventCount, setEventCount] = useState9(0);
2047
+ const [localOnlyCount, setLocalOnlyCount] = useState9(0);
2048
+ const [forks, setForks] = useState9([]);
2049
+ const [pendingBufferCount, setPendingBufferCount] = useState9(readOfflineBuffer().length);
2050
+ const [lastEventTs, setLastEventTs] = useState9(0);
2051
+ const [hasStateZip, setHasStateZip] = useState9(false);
1953
2052
  const knownEventPaths = useRef6(/* @__PURE__ */ new Set());
1954
2053
  const allEventsRef = useRef6([]);
1955
2054
  const applyNewEvents = useCallback((snapshot, newEvents) => {
@@ -2085,13 +2184,13 @@ function useHFState(token, namespace) {
2085
2184
  }
2086
2185
 
2087
2186
  // src/components/labs/LabsTab.tsx
2088
- import { useState as useState14 } from "react";
2187
+ import { useState as useState16 } from "react";
2089
2188
 
2090
2189
  // src/components/labs/LabRemix.tsx
2091
- import { useState as useState9 } from "react";
2190
+ import { useState as useState11 } from "react";
2092
2191
 
2093
2192
  // src/components/labs/LabImagePicker.tsx
2094
- import { useState as useState8 } from "react";
2193
+ import { useState as useState10 } from "react";
2095
2194
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
2096
2195
  var LabImagePicker = ({
2097
2196
  availableItems,
@@ -2100,8 +2199,8 @@ var LabImagePicker = ({
2100
2199
  onClose,
2101
2200
  title = "Bild w\xE4hlen"
2102
2201
  }) => {
2103
- const [search, setSearch] = useState8("");
2104
- const [drillItem, setDrillItem] = useState8(null);
2202
+ const [search, setSearch] = useState10("");
2203
+ const [drillItem, setDrillItem] = useState10(null);
2105
2204
  const filtered = availableItems.filter(
2106
2205
  (item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
2107
2206
  );
@@ -2203,13 +2302,13 @@ var LabImagePicker = ({
2203
2302
  // src/components/labs/LabRemix.tsx
2204
2303
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
2205
2304
  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);
2305
+ const [showPicker, setShowPicker] = useState11(false);
2306
+ const [selected, setSelected] = useState11(null);
2307
+ const [instruction, setInstruction] = useState11("");
2308
+ const [generatedPrompt, setGeneratedPrompt] = useState11("");
2309
+ const [resultImage, setResultImage] = useState11(null);
2310
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState11(false);
2311
+ const [isGeneratingImage, setIsGeneratingImage] = useState11(false);
2213
2312
  const handleSelectImage = (item, frame) => {
2214
2313
  services.onItemUsed(item);
2215
2314
  setSelected({
@@ -2392,16 +2491,16 @@ var LabRemix = ({ services, onResult }) => {
2392
2491
  };
2393
2492
 
2394
2493
  // src/components/labs/LabBlend.tsx
2395
- import { useState as useState10 } from "react";
2494
+ import { useState as useState12 } from "react";
2396
2495
  import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2397
2496
  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);
2497
+ const [showPickerFor, setShowPickerFor] = useState12(null);
2498
+ const [selectedImages, setSelectedImages] = useState12([]);
2499
+ const [instruction, setInstruction] = useState12("");
2500
+ const [generatedPrompt, setGeneratedPrompt] = useState12("");
2501
+ const [resultImage, setResultImage] = useState12(null);
2502
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = useState12(false);
2503
+ const [isGeneratingImage, setIsGeneratingImage] = useState12(false);
2405
2504
  const handleSelectImage = (index, item, frame) => {
2406
2505
  services.onItemUsed(item);
2407
2506
  const newImg = {
@@ -2588,17 +2687,17 @@ var LabBlend = ({ services, onResult }) => {
2588
2687
  };
2589
2688
 
2590
2689
  // src/components/labs/LabCompare.tsx
2591
- import { useState as useState11 } from "react";
2690
+ import { useState as useState13 } from "react";
2592
2691
  import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2593
2692
  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);
2693
+ const [showPickerFor, setShowPickerFor] = useState13(null);
2694
+ const [selectedImages, setSelectedImages] = useState13([]);
2695
+ const [instruction, setInstruction] = useState13("");
2696
+ const [analysis, setAnalysis] = useState13("");
2697
+ const [generatedPrompt, setGeneratedPrompt] = useState13("");
2698
+ const [resultImage, setResultImage] = useState13(null);
2699
+ const [isAnalyzing, setIsAnalyzing] = useState13(false);
2700
+ const [isGeneratingImage, setIsGeneratingImage] = useState13(false);
2602
2701
  const handleSelectImage = (index, item, frame) => {
2603
2702
  services.onItemUsed(item);
2604
2703
  const newImg = {
@@ -2761,14 +2860,14 @@ var LabCompare = ({ services, onResult }) => {
2761
2860
  };
2762
2861
 
2763
2862
  // src/components/labs/LabLoop.tsx
2764
- import { useState as useState12 } from "react";
2863
+ import { useState as useState14 } from "react";
2765
2864
  import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
2766
2865
  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);
2866
+ const [rounds, setRounds] = useState14([]);
2867
+ const [currentInstruction, setCurrentInstruction] = useState14("");
2868
+ const [showPickerForRound, setShowPickerForRound] = useState14(null);
2869
+ const [pendingImages, setPendingImages] = useState14([]);
2870
+ const [isGenerating, setIsGenerating] = useState14(false);
2772
2871
  const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
2773
2872
  const handleAddImage = (item, frame) => {
2774
2873
  services.onItemUsed(item);
@@ -2924,7 +3023,7 @@ var LabLoop = ({ services, onResult }) => {
2924
3023
  };
2925
3024
 
2926
3025
  // src/components/labs/LabFrameExtractor.tsx
2927
- import { useRef as useRef7, useState as useState13, useCallback as useCallback2 } from "react";
3026
+ import { useRef as useRef7, useState as useState15, useCallback as useCallback2 } from "react";
2928
3027
  import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2929
3028
  var formatTime = (s) => {
2930
3029
  const m = Math.floor(s / 60);
@@ -2941,12 +3040,12 @@ var LabFrameExtractor = ({
2941
3040
  const videoRef = useRef7(null);
2942
3041
  const canvasRef = useRef7(null);
2943
3042
  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");
3043
+ const [selectedItem, setSelectedItem] = useState15(null);
3044
+ const [videoSrc, setVideoSrc] = useState15(null);
3045
+ const [videoReady, setVideoReady] = useState15(false);
3046
+ const [frames, setFrames] = useState15([]);
3047
+ const [isExtracting, setIsExtracting] = useState15(false);
3048
+ const [intervalSec, setIntervalSec] = useState15("1");
2950
3049
  const handleVideoSelect = (item) => {
2951
3050
  const mediaId = item.frames[0]?.mediaId;
2952
3051
  if (!mediaId) return;
@@ -3183,7 +3282,7 @@ var BASE_TABS = [
3183
3282
  ];
3184
3283
  var FRAMES_TAB = { key: "frames", label: "Frames", icon: "crop_original" };
3185
3284
  var LabsTab = ({ services, onResult, videoItems, resolveVideoUrl }) => {
3186
- const [activeTab, setActiveTab] = useState14("remix");
3285
+ const [activeTab, setActiveTab] = useState16("remix");
3187
3286
  const showFrames = !!(videoItems && resolveVideoUrl);
3188
3287
  const tabs = showFrames ? [...BASE_TABS, FRAMES_TAB] : BASE_TABS;
3189
3288
  return /* @__PURE__ */ jsxs17("div", { className: "flex flex-col h-full overflow-hidden", children: [
@@ -3217,19 +3316,19 @@ var LabsTab = ({ services, onResult, videoItems, resolveVideoUrl }) => {
3217
3316
  };
3218
3317
 
3219
3318
  // src/components/TagManagerPanel.tsx
3220
- import { useState as useState15 } from "react";
3319
+ import { useState as useState17 } from "react";
3221
3320
  import { jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
3222
3321
  function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
3223
3322
  const categories = Object.keys(workspaceTags.by_category).filter(
3224
3323
  (cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
3225
3324
  );
3226
- const [selectedCategory, setSelectedCategory] = useState15(categories[0] || "");
3325
+ const [selectedCategory, setSelectedCategory] = useState17(categories[0] || "");
3227
3326
  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("");
3327
+ const [editingLabel, setEditingLabel] = useState17(null);
3328
+ const [editState, setEditState] = useState17({ label: "", value: "" });
3329
+ const [newTag, setNewTag] = useState17({ label: "", value: "" });
3330
+ const [movingLabel, setMovingLabel] = useState17(null);
3331
+ const [moveTarget, setMoveTarget] = useState17("");
3233
3332
  const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
3234
3333
  const otherCategories = categories.filter((c) => c !== effectiveCategory);
3235
3334
  const startEdit = (tag) => {
@@ -3425,7 +3524,7 @@ function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete,
3425
3524
  }
3426
3525
 
3427
3526
  // src/components/HFTestTab.tsx
3428
- import { useState as useState16 } from "react";
3527
+ import { useState as useState18 } from "react";
3429
3528
  import { jsx as jsx21, jsxs as jsxs19 } from "react/jsx-runtime";
3430
3529
  var HF_BASE = "https://huggingface.co";
3431
3530
  var HF_REPO = "RolandSch/fa-app-state";
@@ -3618,7 +3717,7 @@ function tryFmt(s) {
3618
3717
  }
3619
3718
  }
3620
3719
  function CopyBtn({ text }) {
3621
- const [done, setDone] = useState16(false);
3720
+ const [done, setDone] = useState18(false);
3622
3721
  return /* @__PURE__ */ jsxs19(
3623
3722
  "button",
3624
3723
  {
@@ -3789,10 +3888,10 @@ function EventMonitor({ events, confirmedEventKeys, galleryItems, imageUploadSta
3789
3888
  ] })
3790
3889
  ] });
3791
3890
  }
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({});
3891
+ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEventKeys = /* @__PURE__ */ new Set(), imageUploadStatus = /* @__PURE__ */ new Map(), missingImages = [] }) {
3892
+ const [selected, setSelected] = useState18(null);
3893
+ const [results, setResults] = useState18({});
3894
+ const [expanded, setExpanded] = useState18({});
3796
3895
  const withResults = galleryItems.filter((g) => g.base64 && g.status === "done");
3797
3896
  const setRunning = (id) => setResults((r) => ({ ...r, [id]: { status: "running", steps: [], totalMs: 0 } }));
3798
3897
  const setDone = (id, steps, t0) => {
@@ -3859,6 +3958,33 @@ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEv
3859
3958
  )
3860
3959
  }
3861
3960
  ) }),
3961
+ missingImages.length > 0 && /* @__PURE__ */ jsx21("div", { style: { marginBottom: 12 }, children: /* @__PURE__ */ jsx21(
3962
+ CollapsibleCard,
3963
+ {
3964
+ title: `Fehlende Bilder auf HF (${missingImages.length})`,
3965
+ icon: /* @__PURE__ */ jsx21("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "broken_image" }),
3966
+ defaultOpen: false,
3967
+ children: /* @__PURE__ */ jsxs19("div", { style: { padding: "8px 10px 4px" }, children: [
3968
+ /* @__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." }),
3969
+ /* @__PURE__ */ jsxs19("div", { style: { display: "flex", gap: 8, marginBottom: 8, fontSize: 11, color: "rgba(255,255,255,0.5)" }, children: [
3970
+ /* @__PURE__ */ jsxs19("span", { children: [
3971
+ "UUID (Flow-App): ",
3972
+ missingImages.filter((e) => !e.filename).length
3973
+ ] }),
3974
+ /* @__PURE__ */ jsx21("span", { children: "\xB7" }),
3975
+ /* @__PURE__ */ jsxs19("span", { children: [
3976
+ "Filename (Server-Upload): ",
3977
+ missingImages.filter((e) => !!e.filename).length
3978
+ ] })
3979
+ ] }),
3980
+ /* @__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: [
3981
+ /* @__PURE__ */ jsx21("span", { style: { color: e.filename ? "#fb923c" : "#60a5fa", minWidth: 60 }, children: e.filename ? "filename" : "uuid" }),
3982
+ /* @__PURE__ */ jsx21("span", { style: { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: e.filename ?? e.id }),
3983
+ /* @__PURE__ */ jsx21("span", { style: { color: "rgba(255,255,255,0.3)" }, children: new Date(e.timestamp).toLocaleDateString("de") })
3984
+ ] }, e.id)) })
3985
+ ] })
3986
+ }
3987
+ ) }),
3862
3988
  /* @__PURE__ */ jsx21(
3863
3989
  CollapsibleCard,
3864
3990
  {
@@ -3963,75 +4089,44 @@ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEv
3963
4089
  }
3964
4090
 
3965
4091
  // 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
4092
+ import { useState as useState19, useEffect as useEffect6 } from "react";
4002
4093
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
4003
4094
  function StarRating({ rating = 0 }) {
4004
4095
  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
4096
  }
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);
4097
+ async function serverGet(baseUrl, path) {
4098
+ const url = `${baseUrl.replace(/\/$/, "")}${path}`;
4099
+ const res = await fetch(url, { credentials: "include" });
4100
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
4101
+ const json = await res.json();
4102
+ return json && typeof json === "object" && "data" in json ? json.data : json;
4103
+ }
4104
+ function ServerTab({ serverBaseUrl }) {
4105
+ const [step, setStep] = useState19("user");
4106
+ const [users, setUsers] = useState19([]);
4107
+ const [usersLoading, setUsersLoading] = useState19(false);
4108
+ const [usersError, setUsersError] = useState19(null);
4109
+ const [selectedUser, setSelectedUser] = useState19(null);
4110
+ const [contexts, setContexts] = useState19([]);
4111
+ const [contextsLoading, setContextsLoading] = useState19(false);
4112
+ const [selectedContext, setSelectedContext] = useState19(null);
4113
+ const [tags, setTags] = useState19([]);
4114
+ const [items, setItems] = useState19([]);
4115
+ const [libLoading, setLibLoading] = useState19(false);
4116
+ const [libError, setLibError] = useState19(null);
4117
+ const [activeTag, setActiveTag] = useState19(null);
4118
+ const [preview, setPreview] = useState19(null);
4024
4119
  useEffect6(() => {
4025
- if (!hasToken) return;
4120
+ if (!serverBaseUrl) return;
4026
4121
  setUsersLoading(true);
4027
4122
  setUsersError(null);
4028
- faServerGet("/api/v2/users", env, token).then(setUsers).catch((e) => setUsersError(String(e))).finally(() => setUsersLoading(false));
4029
- }, [env, hasToken]);
4123
+ serverGet(serverBaseUrl, "/api/v2/users").then(setUsers).catch((e) => setUsersError(String(e))).finally(() => setUsersLoading(false));
4124
+ }, [serverBaseUrl]);
4030
4125
  const selectUser = async (user) => {
4031
4126
  setSelectedUser(user);
4032
4127
  setContextsLoading(true);
4033
4128
  try {
4034
- const data = await faServerGet(`/api2/contexts?user_id=${user.id}`, env, token);
4129
+ const data = await serverGet(serverBaseUrl, `/api2/contexts?user_id=${user.id}`);
4035
4130
  if (data.length === 1) {
4036
4131
  await loadLibrary(user, data[0]);
4037
4132
  } else {
@@ -4051,12 +4146,11 @@ function ServerTab({ hfToken }) {
4051
4146
  setLibError(null);
4052
4147
  try {
4053
4148
  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)
4149
+ serverGet(serverBaseUrl, `/api2/tags?user_id=${user.id}`),
4150
+ serverGet(serverBaseUrl, `/api/storage/${user.id}/library`)
4056
4151
  ]);
4057
4152
  setTags(Array.isArray(tagsRes) ? tagsRes : []);
4058
- const raw = Array.isArray(libRes) ? libRes : libRes.data ?? [];
4059
- setItems(raw);
4153
+ setItems(Array.isArray(libRes) ? libRes : []);
4060
4154
  } catch (e) {
4061
4155
  setLibError(String(e));
4062
4156
  } finally {
@@ -4075,13 +4169,7 @@ function ServerTab({ hfToken }) {
4075
4169
  setLibError(null);
4076
4170
  };
4077
4171
  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
- }
4172
+ if (!serverBaseUrl) return null;
4085
4173
  return /* @__PURE__ */ jsxs20("div", { className: "flex flex-col h-full min-h-0", children: [
4086
4174
  /* @__PURE__ */ jsxs20("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-white/8", children: [
4087
4175
  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 +4177,7 @@ function ServerTab({ hfToken }) {
4089
4177
  step === "user" && "Server Browser",
4090
4178
  step === "context" && `${selectedUser?.username} \u2014 Kontext w\xE4hlen`,
4091
4179
  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
- )) })
4180
+ ] })
4102
4181
  ] }),
4103
4182
  step === "user" && /* @__PURE__ */ jsxs20("div", { className: "flex flex-col flex-1 min-h-0 overflow-y-auto p-3 gap-2", children: [
4104
4183
  usersLoading && /* @__PURE__ */ jsx22("p", { className: "text-white/30 text-[11px] text-center py-4", children: "Lade User\u2026" }),
@@ -4189,7 +4268,7 @@ function ServerTab({ hfToken }) {
4189
4268
 
4190
4269
  // src/components/AvatarArchitectApp.tsx
4191
4270
  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 }) {
4271
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
4193
4272
  useEffect7(() => {
4194
4273
  const id = "flow-styles";
4195
4274
  if (!document.getElementById(id)) {
@@ -4199,19 +4278,19 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4199
4278
  document.head.appendChild(style);
4200
4279
  }
4201
4280
  }, []);
4202
- const [showStart, setShowStart] = useState18(true);
4203
- const [layoutChoice, setLayoutChoice] = useState18(() => {
4281
+ const [showStart, setShowStart] = useState20(true);
4282
+ const [layoutChoice, setLayoutChoice] = useState20(() => {
4204
4283
  try {
4205
4284
  return localStorage.getItem("aa-layout") || null;
4206
4285
  } catch {
4207
4286
  return null;
4208
4287
  }
4209
4288
  });
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(() => {
4289
+ const [projectLoaded, setProjectLoaded] = useState20(false);
4290
+ const [hfToken, setHfToken] = useState20(initialHfToken || "");
4291
+ const [hfTokenInput, setHfTokenInput] = useState20(initialHfToken || "");
4292
+ const [isLoadingFromHF, setIsLoadingFromHF] = useState20(false);
4293
+ const [hfNamespaceLocal, setHfNamespaceLocal] = useState20(() => {
4215
4294
  try {
4216
4295
  const stored = localStorage.getItem("aa-hf-namespace");
4217
4296
  if (stored !== null) return stored;
@@ -4220,7 +4299,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4220
4299
  return "";
4221
4300
  }
4222
4301
  });
4223
- const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState18(null);
4302
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState20(null);
4224
4303
  useEffect7(() => {
4225
4304
  if (hfNamespace !== void 0) return;
4226
4305
  const backendUrl = typeof window !== "undefined" ? window.BACKEND_URL || window.location.origin : null;
@@ -4243,9 +4322,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4243
4322
  refresh: refreshHF,
4244
4323
  hasStateZip
4245
4324
  } = useHFState(hfToken, effectiveNamespace);
4246
- const [imageUploadStatus, setImageUploadStatus] = useState18(/* @__PURE__ */ new Map());
4247
- const [bootstrapLog, setBootstrapLog] = useState18([]);
4248
- const [isBootstrapping, setIsBootstrapping] = useState18(false);
4325
+ const [imageUploadStatus, setImageUploadStatus] = useState20(/* @__PURE__ */ new Map());
4326
+ const [bootstrapLog, setBootstrapLog] = useState20([]);
4327
+ const [isBootstrapping, setIsBootstrapping] = useState20(false);
4328
+ const [hfMissingImages, setHfMissingImages] = useState20([]);
4249
4329
  const syncTopSlot = /* @__PURE__ */ jsxs21(Fragment9, { children: [
4250
4330
  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
4331
  "\u26A0 ",
@@ -4304,10 +4384,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4304
4384
  setLayoutChoice(choice);
4305
4385
  setShowStart(false);
4306
4386
  };
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([]);
4387
+ const [nodes, setNodes] = useState20([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
4388
+ const [edges, setEdges] = useState20([]);
4389
+ const [history, setHistory] = useState20([]);
4390
+ const [galleryItems, setGalleryItems] = useState20([]);
4311
4391
  const galleryItemsRef = useRef8([]);
4312
4392
  useEffect7(() => {
4313
4393
  galleryItemsRef.current = galleryItems;
@@ -4325,7 +4405,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4325
4405
  model: m.model,
4326
4406
  tags: m.tags || [],
4327
4407
  timestamp: m.timestamp,
4328
- status: "done"
4408
+ status: "done",
4409
+ filename: m.filename,
4410
+ hasThumb: m.hasThumb
4329
4411
  }));
4330
4412
  setGalleryItems((prev) => {
4331
4413
  const localOnly = prev.filter((g) => !hfIds.has(g.id));
@@ -4337,77 +4419,83 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4337
4419
  const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4338
4420
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
4339
4421
  });
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) {
4422
+ const sortedEntries = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
4423
+ (async () => {
4424
+ for (const entry of sortedEntries) {
4425
+ if (galleryItemsRef.current.find((g) => g.id === entry.id)?.base64) continue;
4426
+ if (hfImageNotFoundRef.current.has(entry.id)) continue;
4427
+ try {
4428
+ const b64 = await hfLoadImageAsBase64(entry.id, hfToken, effectiveNamespace, entry.filename, void 0, entry.mimeType, entry.hasThumb);
4429
+ if (!b64) {
4430
+ hfImageNotFoundRef.current.set(entry.id, Date.now());
4431
+ setHfMissingImages((prev) => {
4432
+ if (prev.find((e) => e.id === entry.id)) return prev;
4433
+ return [...prev, { id: entry.id, filename: entry.filename, mimeType: entry.mimeType, timestamp: entry.timestamp }];
4434
+ });
4435
+ continue;
4436
+ }
4437
+ const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4438
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4439
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4440
+ } catch {
4347
4441
  hfImageNotFoundRef.current.set(entry.id, Date.now());
4348
- return;
4349
4442
  }
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
- }
4443
+ }
4444
+ })();
4357
4445
  }, [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(() => {
4446
+ const [activePrompt, setActivePrompt] = useState20("");
4447
+ const [isSynthesizing, setIsSynthesizing] = useState20(false);
4448
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState20(0);
4449
+ const [currentResult, setCurrentResult] = useState20(null);
4450
+ const [focusedNodeId, setFocusedNodeId] = useState20(null);
4451
+ const [leftTab, setLeftTab] = useState20("prompt");
4452
+ const [promptFeedback, setPromptFeedback] = useState20(null);
4453
+ const [lastPromptPayload, setLastPromptPayload] = useState20(null);
4454
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState20(false);
4455
+ const [activeTab, setActiveTab] = useState20("history");
4456
+ const [mobileTab, setMobileTab] = useState20("stage");
4457
+ const [middlePanel, setMiddlePanel] = useState20("stage");
4458
+ const [recentLabItems, setRecentLabItems] = useState20([]);
4459
+ const [aspectRatio, setAspectRatio] = useState20("1:1");
4460
+ const [selectedModel, setSelectedModel] = useState20("\u{1F34C} Nano Banana Pro");
4461
+ const [seed, setSeed] = useState20(Math.floor(Math.random() * 1e6));
4462
+ const [seedMode, setSeedMode] = useState20("random");
4463
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState20(false);
4464
+ const [isRightCollapsed, setIsRightCollapsed] = useState20(false);
4465
+ const [leftPanelWidth, setLeftPanelWidth] = useState20(() => {
4378
4466
  try {
4379
4467
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
4380
4468
  } catch {
4381
4469
  return 260;
4382
4470
  }
4383
4471
  });
4384
- const [rightPanelWidth, setRightPanelWidth] = useState18(() => {
4472
+ const [rightPanelWidth, setRightPanelWidth] = useState20(() => {
4385
4473
  try {
4386
4474
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
4387
4475
  } catch {
4388
4476
  return 320;
4389
4477
  }
4390
4478
  });
4391
- const [isPromptCollapsed, setIsPromptCollapsed] = useState18(false);
4392
- const [projectActionState, setProjectActionState] = useState18("idle");
4479
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState20(false);
4480
+ const [projectActionState, setProjectActionState] = useState20("idle");
4393
4481
  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(() => {
4482
+ const [workspaceTags, setWorkspaceTags] = useState20(null);
4483
+ const [serverProjects, setServerProjects] = useState20([]);
4484
+ const [isLoadingFromServer, setIsLoadingFromServer] = useState20(false);
4485
+ const [highContrast, setHighContrast] = useState20(() => {
4398
4486
  try {
4399
4487
  return localStorage.getItem("aa-contrast") === "high";
4400
4488
  } catch {
4401
4489
  return false;
4402
4490
  }
4403
4491
  });
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 });
4492
+ const [activeReferenceId, setActiveReferenceId] = useState20(null);
4493
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState20(null);
4494
+ const [isScanningImage, setIsScanningImage] = useState20(false);
4495
+ const [touchStartX, setTouchStartX] = useState20(null);
4496
+ const [isFullscreen, setIsFullscreen] = useState20(false);
4497
+ const [zoomScale, setZoomScale] = useState20(1);
4498
+ const [zoomOffset, setZoomOffset] = useState20({ x: 0, y: 0 });
4411
4499
  const lastPinchDist = useRef8(null);
4412
4500
  const lastTapTime = useRef8(0);
4413
4501
  const dragStart = useRef8(null);
@@ -4560,6 +4648,34 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4560
4648
  const goToNext = useCallback3(() => {
4561
4649
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
4562
4650
  }, [currentIndex, history]);
4651
+ const handleGallerySelect = useCallback3((g) => {
4652
+ setCurrentResult(g);
4653
+ setMobileTab("stage");
4654
+ if (g.filename && hfToken && !g.fullBase64) {
4655
+ hfLoadImageAsBase64(g.id, hfToken, effectiveNamespace, g.filename, void 0, g.mimeType, false).then((b64) => {
4656
+ if (!b64) return;
4657
+ const full = `data:${g.mimeType || "image/jpeg"};base64,${b64}`;
4658
+ setCurrentResult((prev) => prev?.id === g.id ? { ...prev, fullBase64: full } : prev);
4659
+ setGalleryItems((prev) => prev.map((item) => item.id === g.id ? { ...item, fullBase64: full } : item));
4660
+ }).catch(() => {
4661
+ });
4662
+ }
4663
+ }, [hfToken, effectiveNamespace]);
4664
+ const handleTitleSet = useCallback3((id) => {
4665
+ const ts = Date.now();
4666
+ setGalleryItems((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
4667
+ setHistory((prev) => prev.map((g) => g.id === id ? { ...g, titleTs: ts } : g));
4668
+ if (hfToken && effectiveNamespace) {
4669
+ hfWriteEvent("title_set", { id }).catch(() => {
4670
+ });
4671
+ }
4672
+ }, [hfToken, effectiveNamespace, hfWriteEvent]);
4673
+ const currentGroup = useMemo2(() => {
4674
+ if (!currentResult?.prompt) return [];
4675
+ const groups = groupByPrompt(galleryItems.filter((g) => g.status === "done" && !!g.base64));
4676
+ const group = groups.find((gr) => gr.prompt === currentResult.prompt);
4677
+ return group ? group.items : [];
4678
+ }, [galleryItems, currentResult?.prompt]);
4563
4679
  const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
4564
4680
  const isGenerating = activeGenerationsCount > 0;
4565
4681
  useKeyboardNavigation(history, currentResult, setCurrentResult);
@@ -4741,8 +4857,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4741
4857
  const handleDownloadSingle = async () => {
4742
4858
  if (!currentResult?.base64) return;
4743
4859
  try {
4744
- const base64Data = currentResult.base64.split(",")[1];
4745
- const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
4860
+ const downloadSrc = currentResult.fullBase64 || currentResult.base64;
4861
+ const base64Data = downloadSrc.split(",")[1];
4862
+ const mimeType = downloadSrc.split(";")[0].split(":")[1] || "image/png";
4746
4863
  let finalBase64 = base64Data;
4747
4864
  if (mimeType === "image/png") finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
4748
4865
  await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
@@ -4942,7 +5059,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4942
5059
  return { by_category: merged, all };
4943
5060
  };
4944
5061
  if (isFullscreen && currentResult?.base64) {
4945
- const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
5062
+ const fsRaw = currentResult.fullBase64 || currentResult.base64;
5063
+ const fsBase64 = fsRaw.startsWith("data:") ? fsRaw : `data:image/png;base64,${fsRaw}`;
4946
5064
  return /* @__PURE__ */ jsxs21(
4947
5065
  "div",
4948
5066
  {
@@ -5094,7 +5212,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5094
5212
  onClick: async () => {
5095
5213
  setIsLoadingFromHF(true);
5096
5214
  try {
5097
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-TC65WQXK.mjs");
5215
+ const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-5BU5DVQ6.mjs");
5098
5216
  const projects = await hfListProjects2(hfToken);
5099
5217
  if (projects.length > 0) {
5100
5218
  const file = await hfDownloadProject2(projects[0].path, hfToken);
@@ -5189,7 +5307,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5189
5307
  setMobileTab("stage");
5190
5308
  }
5191
5309
  } }) }),
5192
- mobileTab === "server" && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0 relative", children: /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 }) }),
5310
+ mobileTab === "server" && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0 relative", children: /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl }) }),
5193
5311
  mobileTab === "tags" && workspaceTags && /* @__PURE__ */ jsx23("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx23(
5194
5312
  TagManagerPanel,
5195
5313
  {
@@ -5267,7 +5385,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5267
5385
  /* @__PURE__ */ jsx23("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
5268
5386
  /* @__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
5387
  ] }),
5270
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
5388
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, className: "w-full h-full object-contain" }),
5271
5389
  !currentResult && /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5272
5390
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
5273
5391
  /* @__PURE__ */ jsx23("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
@@ -5285,6 +5403,49 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5285
5403
  ]
5286
5404
  }
5287
5405
  ),
5406
+ 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: [
5407
+ /* @__PURE__ */ jsx23(
5408
+ "div",
5409
+ {
5410
+ onClick: () => handleGallerySelect(item),
5411
+ style: {
5412
+ width: 44,
5413
+ height: 44,
5414
+ borderRadius: 8,
5415
+ overflow: "hidden",
5416
+ cursor: "pointer",
5417
+ border: item.id === currentResult.id ? "2px solid rgba(255,255,255,0.8)" : "2px solid rgba(255,255,255,0.15)",
5418
+ opacity: item.id === currentResult.id ? 1 : 0.55
5419
+ },
5420
+ children: /* @__PURE__ */ jsx23("img", { src: item.base64, style: { width: "100%", height: "100%", objectFit: "cover" }, alt: "" })
5421
+ }
5422
+ ),
5423
+ /* @__PURE__ */ jsx23(
5424
+ "button",
5425
+ {
5426
+ onClick: (e) => {
5427
+ e.stopPropagation();
5428
+ handleTitleSet(item.id);
5429
+ },
5430
+ style: {
5431
+ position: "absolute",
5432
+ bottom: 2,
5433
+ right: 2,
5434
+ width: 14,
5435
+ height: 14,
5436
+ background: item.titleTs ? "#f59e0b" : "rgba(0,0,0,0.75)",
5437
+ border: "1px solid rgba(255,255,255,0.25)",
5438
+ borderRadius: 3,
5439
+ cursor: "pointer",
5440
+ padding: 0,
5441
+ display: "flex",
5442
+ alignItems: "center",
5443
+ justifyContent: "center"
5444
+ },
5445
+ 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" })
5446
+ }
5447
+ )
5448
+ ] }, item.id)) }),
5288
5449
  currentResult?.status === "done" && /* @__PURE__ */ jsxs21("div", { className: "flex gap-2 mt-3", children: [
5289
5450
  /* @__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
5451
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
@@ -5307,10 +5468,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5307
5468
  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
5469
  ] }),
5309
5470
  /* @__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)) }),
5471
+ activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: handleGallerySelect, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5314
5472
  activeTab === "gallery" && /* @__PURE__ */ jsx23(
5315
5473
  MediaLibrary,
5316
5474
  {
@@ -5321,10 +5479,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5321
5479
  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
5480
  },
5323
5481
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
5324
- onSelect: (g) => {
5325
- setCurrentResult(g);
5326
- setMobileTab("stage");
5327
- },
5482
+ onSelect: handleGallerySelect,
5328
5483
  onGenerateReference: (item) => {
5329
5484
  handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true });
5330
5485
  setMobileTab("stage");
@@ -5364,7 +5519,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5364
5519
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[20px]", children: "biotech" }),
5365
5520
  "HF"
5366
5521
  ] }),
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" }) }),
5522
+ 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
5523
  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
5524
  ] }),
5370
5525
  /* @__PURE__ */ jsxs21("div", { className: "flex-1 overflow-hidden relative", children: [
@@ -5447,10 +5602,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5447
5602
  galleryItems,
5448
5603
  allEvents: hfAllEvents,
5449
5604
  confirmedEventKeys: hfConfirmedKeys,
5450
- imageUploadStatus
5605
+ imageUploadStatus,
5606
+ missingImages: hfMissingImages
5451
5607
  }
5452
5608
  ) }),
5453
- activeTab === "server" && /* @__PURE__ */ jsx23("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 }) })
5609
+ activeTab === "server" && /* @__PURE__ */ jsx23("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl }) })
5454
5610
  ] })
5455
5611
  ] }),
5456
5612
  /* @__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 +5615,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5459
5615
  { id: "labs", icon: "science", label: "Labs" },
5460
5616
  ...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
5461
5617
  { id: "browse", icon: "photo_library", label: "Galerie" },
5462
- { id: "server", icon: "storage", label: "Server" }
5618
+ ...serverBaseUrl ? [{ id: "server", icon: "storage", label: "Server" }] : []
5463
5619
  ].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
5620
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
5465
5621
  /* @__PURE__ */ jsx23("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
@@ -5510,7 +5666,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5510
5666
  ] })
5511
5667
  }
5512
5668
  ) }),
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)) }) })
5669
+ /* @__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
5670
  ] }),
5515
5671
  /* @__PURE__ */ jsxs21("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
5516
5672
  /* @__PURE__ */ jsx23(
@@ -5535,7 +5691,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5535
5691
  /* @__PURE__ */ jsx23("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
5536
5692
  /* @__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
5693
  ] }),
5538
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
5694
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
5539
5695
  !currentResult && /* @__PURE__ */ jsxs21("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
5540
5696
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
5541
5697
  /* @__PURE__ */ jsx23("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
@@ -5677,33 +5833,81 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5677
5833
  middlePanel === "labs" ? /* @__PURE__ */ jsx23("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx23(LabsTab, { services: labServices, onResult: (item) => {
5678
5834
  const frame = item.frames[0];
5679
5835
  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 })
5836
+ } }) }) : /* @__PURE__ */ jsxs21("div", { className: "flex-1 overflow-hidden flex flex-col", children: [
5837
+ /* @__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: [
5838
+ 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: [
5839
+ /* @__PURE__ */ jsx23("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
5840
+ /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
5693
5841
  ] }),
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" })
5842
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-4", children: [
5843
+ /* @__PURE__ */ jsx23("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5844
+ /* @__PURE__ */ jsx23("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5845
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs21("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
5846
+ /* @__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" }) }),
5847
+ /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-2", children: [
5848
+ /* @__PURE__ */ jsx23("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
5849
+ /* @__PURE__ */ jsx23("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
5850
+ ] }),
5851
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
5852
+ ] }) : /* @__PURE__ */ jsxs21("div", { className: "h-full w-full relative flex items-center justify-center", children: [
5853
+ /* @__PURE__ */ jsx23("img", { src: currentResult.fullBase64 || currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
5854
+ /* @__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: [
5855
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
5856
+ /* @__PURE__ */ jsx23(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
5857
+ /* @__PURE__ */ jsx23(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
5858
+ ] })
5859
+ ] }) : /* @__PURE__ */ jsxs21("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5860
+ /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
5861
+ /* @__PURE__ */ jsx23("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5701
5862
  ] })
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
- ] }) })
5863
+ ] }) }),
5864
+ 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: [
5865
+ /* @__PURE__ */ jsx23(
5866
+ "div",
5867
+ {
5868
+ onClick: () => handleGallerySelect(item),
5869
+ style: {
5870
+ width: 52,
5871
+ height: 52,
5872
+ borderRadius: 10,
5873
+ overflow: "hidden",
5874
+ cursor: "pointer",
5875
+ border: item.id === currentResult.id ? "2px solid rgba(255,255,255,0.8)" : "2px solid rgba(255,255,255,0.12)",
5876
+ opacity: item.id === currentResult.id ? 1 : 0.5,
5877
+ transition: "opacity 0.15s, border-color 0.15s"
5878
+ },
5879
+ className: "hover:opacity-80",
5880
+ children: /* @__PURE__ */ jsx23("img", { src: item.base64, style: { width: "100%", height: "100%", objectFit: "cover" }, alt: "" })
5881
+ }
5882
+ ),
5883
+ /* @__PURE__ */ jsx23(
5884
+ "button",
5885
+ {
5886
+ onClick: (e) => {
5887
+ e.stopPropagation();
5888
+ handleTitleSet(item.id);
5889
+ },
5890
+ title: "Als Titel markieren",
5891
+ style: {
5892
+ position: "absolute",
5893
+ bottom: 3,
5894
+ right: 3,
5895
+ width: 16,
5896
+ height: 16,
5897
+ background: item.titleTs ? "#f59e0b" : "rgba(0,0,0,0.75)",
5898
+ border: "1px solid rgba(255,255,255,0.25)",
5899
+ borderRadius: 4,
5900
+ cursor: "pointer",
5901
+ padding: 0,
5902
+ display: "flex",
5903
+ alignItems: "center",
5904
+ justifyContent: "center"
5905
+ },
5906
+ 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" })
5907
+ }
5908
+ )
5909
+ ] }, item.id)) })
5910
+ ] })
5707
5911
  ] })
5708
5912
  ] }),
5709
5913
  !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 +5918,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5714
5918
  setActiveTab(tab);
5715
5919
  setIsRightCollapsed(false);
5716
5920
  }, 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: () => {
5921
+ serverBaseUrl && /* @__PURE__ */ jsx23("button", { onClick: () => {
5718
5922
  setActiveTab("server");
5719
5923
  setIsRightCollapsed(false);
5720
5924
  }, 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 +5942,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5738
5942
  /* @__PURE__ */ jsx23("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
5739
5943
  /* @__PURE__ */ jsx23("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
5740
5944
  ] }) }),
5741
- activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5945
+ activeTab === "history" && /* @__PURE__ */ jsx23(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: handleGallerySelect, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5742
5946
  activeTab === "gallery" && /* @__PURE__ */ jsx23(
5743
5947
  MediaLibrary,
5744
5948
  {
@@ -5749,7 +5953,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5749
5953
  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
5954
  },
5751
5955
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
5752
- onSelect: setCurrentResult,
5956
+ onSelect: handleGallerySelect,
5753
5957
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
5754
5958
  }
5755
5959
  ),
@@ -5779,14 +5983,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5779
5983
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
5780
5984
  }
5781
5985
  ),
5782
- activeTab === "server" && /* @__PURE__ */ jsx23(ServerTab, { hfToken: hfToken || void 0 })
5986
+ activeTab === "server" && /* @__PURE__ */ jsx23(ServerTab, { serverBaseUrl })
5783
5987
  ] })
5784
5988
  ] })
5785
5989
  ] });
5786
5990
  }
5787
5991
 
5788
5992
  // src/components/FaApp.tsx
5789
- import { useState as useState19, useEffect as useEffect8 } from "react";
5993
+ import { useState as useState21, useEffect as useEffect8 } from "react";
5790
5994
  import { jsx as jsx24 } from "react/jsx-runtime";
5791
5995
  function FaApp({
5792
5996
  onGenerateImage,
@@ -5806,7 +6010,7 @@ function FaApp({
5806
6010
  onServerDelete,
5807
6011
  buildInfo
5808
6012
  }) {
5809
- const [hfNamespace, setHfNamespace] = useState19(void 0);
6013
+ const [hfNamespace, setHfNamespace] = useState21(void 0);
5810
6014
  useEffect8(() => {
5811
6015
  if (!serverBaseUrl) return;
5812
6016
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
@@ -5828,6 +6032,7 @@ function FaApp({
5828
6032
  initialHfToken: libToken ? libToken.startsWith("hf_") ? libToken : `hf_${libToken}` : void 0,
5829
6033
  hfNamespace,
5830
6034
  allowDevNamespace: !hfNamespace && allowDevNamespace,
6035
+ serverBaseUrl,
5831
6036
  onFetchServerProjects,
5832
6037
  onServerSave,
5833
6038
  onServerLoad,
@@ -5838,7 +6043,7 @@ function FaApp({
5838
6043
  }
5839
6044
 
5840
6045
  // src/index.ts
5841
- var LIB_VERSION = "2.0.30";
6046
+ var LIB_VERSION = "2.0.46";
5842
6047
  export {
5843
6048
  AvatarArchitectApp,
5844
6049
  CollapsibleCard,
@@ -5882,10 +6087,6 @@ export {
5882
6087
  cleanAiResponse,
5883
6088
  createFlowServices,
5884
6089
  exportProjectToZip,
5885
- faServerDelete,
5886
- faServerGet,
5887
- faServerPost,
5888
- faServerPut,
5889
6090
  findForks,
5890
6091
  findTips,
5891
6092
  formatTreeToMarkdown,