@rslsp1/fa-app-tools 1.0.2 → 1.0.3

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.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  AvatarArchitectApp: () => AvatarArchitectApp,
34
+ CollapsibleCard: () => CollapsibleCard,
34
35
  CompactDropdown: () => CompactDropdown,
35
36
  FaToolsBadge: () => FaToolsBadge,
36
37
  GLOBAL_STYLES: () => GLOBAL_STYLES,
@@ -39,6 +40,7 @@ __export(index_exports, {
39
40
  ListView: () => ListView,
40
41
  MediaLibrary: () => MediaLibrary,
41
42
  PillButton: () => PillButton,
43
+ ProjectSyncTab: () => ProjectSyncTab,
42
44
  PromptTab: () => PromptTab,
43
45
  SectionLabel: () => SectionLabel,
44
46
  SetupPanel: () => SetupPanel,
@@ -259,7 +261,7 @@ function generateMarkdownReport(nodes, history) {
259
261
  });
260
262
  return md;
261
263
  }
262
- async function exportProjectToZip(nodes, edges, history, galleryItems, settings) {
264
+ async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags) {
263
265
  const zip = new import_jszip.default();
264
266
  const projectData = {
265
267
  nodes,
@@ -267,6 +269,7 @@ async function exportProjectToZip(nodes, edges, history, galleryItems, settings)
267
269
  history: history.map((h) => ({ ...h, base64: void 0 })),
268
270
  galleryItems: galleryItems.map((g) => ({ ...g, base64: void 0 })),
269
271
  settings,
272
+ workspaceTags: workspaceTags || null,
270
273
  version: "1.2.6"
271
274
  };
272
275
  zip.file("project.json", JSON.stringify(projectData, null, 2));
@@ -355,14 +358,44 @@ function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaI
355
358
  }
356
359
  return options;
357
360
  }
358
- function buildPromptTabPayload(selectedValues, instructions, treeText) {
361
+ var CONTENT_FIRST = ["subject", "clothing", "environment", "mood", "action", "content", "general"];
362
+ var RULES_LAST = ["system", "rules", "safety", "generated", "source", "status", "type", "model"];
363
+ function categoryZone(cat) {
364
+ const c = cat.toLowerCase();
365
+ if (CONTENT_FIRST.some((k) => c.includes(k))) return 0;
366
+ if (RULES_LAST.some((k) => c.includes(k))) return 2;
367
+ return 1;
368
+ }
369
+ var ZONE_SEPARATORS = { 1: "\u2500\u2500\u2500\u2500 STYLE \u2500\u2500\u2500\u2500", 2: "\u2500\u2500\u2500\u2500 SYSTEM / RULES \u2500\u2500\u2500\u2500" };
370
+ function buildPromptTabPayload(selectedTags, instructions, rules, treeText) {
359
371
  const parts = [];
360
372
  if (treeText) parts.push(`HIERARCHY:
361
373
  ${treeText}`);
362
- if (selectedValues.length > 0) parts.push(`SELECTED TAGS:
363
- ${selectedValues.join(", ")}`);
364
- if (instructions.trim()) parts.push(`ADDITIONAL INSTRUCTIONS:
374
+ if (selectedTags.length > 0) {
375
+ const sorted = [...selectedTags].sort((a, b) => categoryZone(a.category) - categoryZone(b.category) || a.category.localeCompare(b.category));
376
+ const byCategory = sorted.reduce((acc, t) => {
377
+ (acc[t.category] = acc[t.category] || []).push(t);
378
+ return acc;
379
+ }, {});
380
+ const tagParts = [];
381
+ let lastZone = -1;
382
+ for (const [cat, tags] of Object.entries(byCategory)) {
383
+ const zone = categoryZone(cat);
384
+ if (lastZone >= 0 && zone > lastZone && ZONE_SEPARATORS[zone]) {
385
+ tagParts.push(ZONE_SEPARATORS[zone]);
386
+ }
387
+ tagParts.push(...tags.map((t) => `[${cat.toUpperCase()}]
388
+ ${t.value}`));
389
+ lastZone = zone;
390
+ }
391
+ parts.push(`SELECTED TAGS:
392
+
393
+ ${tagParts.join("\n\n")}`);
394
+ }
395
+ if (instructions.trim()) parts.push(`DESCRIPTION:
365
396
  ${instructions.trim()}`);
397
+ if (rules.trim()) parts.push(`GENERATION RULES:
398
+ ${rules.trim()}`);
366
399
  return [
367
400
  "Create a high-quality image generation prompt based on the following inputs. Translate everything to English.",
368
401
  `Return a JSON object with exactly two keys: "prompt" (the prompt string) and "feedback" (a short note on your choices in the user's language, or null).`,
@@ -591,9 +624,11 @@ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
591
624
  };
592
625
 
593
626
  // src/components/InspectPanel.tsx
594
- var import_react5 = require("motion/react");
627
+ var import_react5 = require("react");
628
+ var import_react6 = require("motion/react");
595
629
  var import_jsx_runtime6 = require("react/jsx-runtime");
596
630
  var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagToggle }) => {
631
+ const currentIndex = (0, import_react5.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
597
632
  if (!currentResult) {
598
633
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
599
634
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "info" }),
@@ -601,11 +636,22 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
601
636
  ] });
602
637
  }
603
638
  const fullDateStr = new Date(currentResult.timestamp).toLocaleString([], { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
604
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react5.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 flex flex-col overflow-hidden", children: [
639
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 flex flex-col overflow-hidden", children: [
605
640
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-shrink-0 border-b border-white/5 bg-black/20 py-3 px-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex gap-2 overflow-x-auto no-scrollbar pb-1", children: history.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onSelect(gen), className: `w-10 h-10 rounded-lg overflow-hidden border shrink-0 transition-all ${currentResult.id === gen.id ? "border-white scale-105 shadow-lg" : "border-white/5 opacity-40 hover:opacity-100"}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" }) }, gen.id)) }) }),
606
641
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-6 dark-scrollbar pb-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-4", children: [
607
642
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Vorschau" }),
608
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: currentResult.base64 ? currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}` : "", className: "w-full h-full object-cover", alt: "Preview" }) }),
643
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl relative", children: [
644
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: currentResult.base64 ? currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}` : "", className: "w-full h-full object-cover", alt: "Preview" }),
645
+ history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => currentIndex > 0 && onSelect(history[currentIndex - 1]), disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
647
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => currentIndex < history.length - 1 && onSelect(history[currentIndex + 1]), disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
648
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
649
+ currentIndex + 1,
650
+ " / ",
651
+ history.length
652
+ ] })
653
+ ] })
654
+ ] }),
609
655
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-4 bg-white/5 rounded-2xl border border-white/5", children: [
610
656
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[8px] font-bold text-white/20 uppercase mb-2 tracking-widest", children: "Prompt Details" }),
611
657
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-[10px] text-white/70 italic leading-relaxed font-medium", children: [
@@ -643,13 +689,12 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
643
689
  };
644
690
 
645
691
  // src/components/SetupPanel.tsx
646
- var import_react6 = require("react");
647
- var import_react7 = require("motion/react");
692
+ var import_react7 = require("react");
693
+ var import_react8 = require("motion/react");
648
694
  var import_jsx_runtime7 = require("react/jsx-runtime");
649
- var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, projectActionState }) => {
650
- const workspaceInputRef = (0, import_react6.useRef)(null);
651
- const projectInputRef = (0, import_react6.useRef)(null);
652
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
695
+ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
696
+ const workspaceInputRef = (0, import_react7.useRef)(null);
697
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react8.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
653
698
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
654
699
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
655
700
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Workspace Management" }),
@@ -662,37 +707,22 @@ var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, project
662
707
  e.target.value = "";
663
708
  } })
664
709
  ] }),
665
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
666
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
667
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Projekt Management" }),
668
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Speichere den gesamten Baum und alle Assets." })
669
- ] }),
670
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
671
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "filled", icon: "download", onClick: onProjectExport, loading: projectActionState === "working", children: "Exportieren (.zip)" }),
672
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "Projekt laden (.zip)" }),
673
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref: projectInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
674
- const f = e.target.files?.[0];
675
- if (f) onProjectImport(f);
676
- e.target.value = "";
677
- } })
678
- ] })
679
- ] }),
680
710
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
681
711
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
682
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
683
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Version" }),
684
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "1.2.0" })
685
- ] }),
686
712
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
687
713
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Engine" }),
688
714
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "Avatar Architect" })
715
+ ] }),
716
+ buildInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
717
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Build" }),
718
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: buildInfo })
689
719
  ] })
690
720
  ] })
691
721
  ] });
692
722
  };
693
723
 
694
724
  // src/components/MediaLibrary.tsx
695
- var import_react8 = require("motion/react");
725
+ var import_react9 = require("motion/react");
696
726
  var import_jsx_runtime8 = require("react/jsx-runtime");
697
727
  var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
698
728
  const selectedCount = items.filter((i) => i.selectedForExport).length;
@@ -711,7 +741,7 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
711
741
  "Laden"
712
742
  ] })
713
743
  ] }),
714
- selectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, className: "flex items-center justify-between bg-blue-500/10 border border-blue-500/20 p-2 rounded-xl", children: [
744
+ selectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react9.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, className: "flex items-center justify-between bg-blue-500/10 border border-blue-500/20 p-2 rounded-xl", children: [
715
745
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[9px] font-bold uppercase text-blue-400 ml-1", children: [
716
746
  selectedCount,
717
747
  " ausgew\xE4hlt"
@@ -728,7 +758,7 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
728
758
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
729
759
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
730
760
  ] })
731
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react8.motion.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: [
761
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react9.motion.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: [
732
762
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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 }),
733
763
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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) => {
734
764
  e.stopPropagation();
@@ -753,10 +783,10 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
753
783
  };
754
784
 
755
785
  // src/components/ListView.tsx
756
- var import_react9 = require("react");
786
+ var import_react10 = require("react");
757
787
  var import_jsx_runtime9 = require("react/jsx-runtime");
758
788
  var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
759
- const inputRef = (0, import_react9.useRef)(null);
789
+ const inputRef = (0, import_react10.useRef)(null);
760
790
  const hasChildren = children && children.length > 0;
761
791
  const handleKeyDown = (e) => {
762
792
  if (e.key === "Tab") {
@@ -784,7 +814,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
784
814
  onMoveNode(node.id, "down");
785
815
  }
786
816
  };
787
- (0, import_react9.useEffect)(() => {
817
+ (0, import_react10.useEffect)(() => {
788
818
  if (isActive && inputRef.current) inputRef.current.focus();
789
819
  }, [isActive]);
790
820
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col w-full", children: [
@@ -818,7 +848,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
818
848
  ] });
819
849
  };
820
850
  function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
821
- const [collapsed, setCollapsed] = (0, import_react9.useState)(/* @__PURE__ */ new Set());
851
+ const [collapsed, setCollapsed] = (0, import_react10.useState)(/* @__PURE__ */ new Set());
822
852
  const toggleCollapse = (id) => {
823
853
  setCollapsed((prev) => {
824
854
  const next = new Set(prev);
@@ -846,17 +876,84 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
846
876
  }
847
877
 
848
878
  // src/components/AvatarArchitectApp.tsx
849
- var import_react12 = require("react");
850
- var import_react13 = require("motion/react");
879
+ var import_react14 = require("react");
851
880
 
852
881
  // src/components/PromptTab.tsx
853
- var import_react10 = require("react");
854
- var import_react11 = require("motion/react");
882
+ var import_react12 = require("react");
883
+
884
+ // src/components/CollapsibleCard.tsx
885
+ var import_react11 = require("react");
855
886
  var import_jsx_runtime10 = require("react/jsx-runtime");
856
- var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
857
- const [selectedLabels, setSelectedLabels] = (0, import_react10.useState)(/* @__PURE__ */ new Set());
858
- const [instructions, setInstructions] = (0, import_react10.useState)("");
859
- const [showFeedback, setShowFeedback] = (0, import_react10.useState)(true);
887
+ var CollapsibleCard = ({
888
+ title,
889
+ icon,
890
+ actions,
891
+ children,
892
+ defaultOpen = true,
893
+ collapsible = true,
894
+ className = ""
895
+ }) => {
896
+ const [isOpen, setIsOpen] = (0, import_react11.useState)(defaultOpen);
897
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
898
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
899
+ "div",
900
+ {
901
+ className: `flex items-center justify-between px-4 py-3 bg-neutral-900/50 ${collapsible ? "cursor-pointer active:bg-neutral-800/70 select-none" : ""}`,
902
+ onClick: () => collapsible && setIsOpen((o) => !o),
903
+ children: [
904
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
905
+ icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-neutral-400", children: icon }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm font-bold uppercase tracking-widest text-neutral-300", children: title })
907
+ ] }),
908
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
909
+ actions && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { onClick: (e) => e.stopPropagation(), children: actions }),
910
+ collapsible && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[16px] text-neutral-500", children: isOpen ? "expand_less" : "expand_more" })
911
+ ] })
912
+ ]
913
+ }
914
+ ),
915
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children })
916
+ ] });
917
+ };
918
+
919
+ // src/components/PromptTab.tsx
920
+ var import_jsx_runtime11 = require("react/jsx-runtime");
921
+ var EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
922
+ var LONG_PRESS_MS = 500;
923
+ var PromptTab = ({
924
+ workspaceTags,
925
+ onGenerate,
926
+ isGenerating,
927
+ feedback,
928
+ promptResult,
929
+ lastPayload,
930
+ onGenerateImage,
931
+ isGeneratingImage,
932
+ onRandom,
933
+ onTokenize,
934
+ isTokenizing,
935
+ onScanImage,
936
+ isScanning,
937
+ onExpand,
938
+ isExpanding,
939
+ onTagCreate,
940
+ onTagUpdate,
941
+ onTagDelete
942
+ }) => {
943
+ const [selectedLabels, setSelectedLabels] = (0, import_react12.useState)(/* @__PURE__ */ new Set());
944
+ const [instructions, setInstructions] = (0, import_react12.useState)("");
945
+ const [rules, setRules] = (0, import_react12.useState)("");
946
+ const [activeCategory, setActiveCategory] = (0, import_react12.useState)(null);
947
+ const [copied, setCopied] = (0, import_react12.useState)(false);
948
+ const imgInputRef = (0, import_react12.useRef)(null);
949
+ const [addingInCat, setAddingInCat] = (0, import_react12.useState)(null);
950
+ const [newLabel, setNewLabel] = (0, import_react12.useState)("");
951
+ const [newValue, setNewValue] = (0, import_react12.useState)("");
952
+ const [editingTag, setEditingTag] = (0, import_react12.useState)(null);
953
+ const [editLabel, setEditLabel] = (0, import_react12.useState)("");
954
+ const [editValue, setEditValue] = (0, import_react12.useState)("");
955
+ const longPressTimer = (0, import_react12.useRef)(null);
956
+ const longPressActivated = (0, import_react12.useRef)(false);
860
957
  const toggleTag = (label) => {
861
958
  setSelectedLabels((prev) => {
862
959
  const next = new Set(prev);
@@ -866,97 +963,671 @@ var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
866
963
  });
867
964
  };
868
965
  const handleGenerate = () => {
869
- const selectedValues = workspaceTags.all.filter((t) => selectedLabels.has(t.label)).map((t) => t.value);
870
- onGenerate(selectedValues, instructions);
966
+ const selectedTags = workspaceTags.all.filter((t) => selectedLabels.has(t.label) && !t.is_deleted).map((t) => ({ label: t.label, value: t.value, category: t.category }));
967
+ onGenerate(selectedTags, instructions, rules);
968
+ };
969
+ const handleCopy = () => {
970
+ if (!promptResult) return;
971
+ navigator.clipboard.writeText(promptResult).then(() => {
972
+ setCopied(true);
973
+ setTimeout(() => setCopied(false), 2e3);
974
+ });
975
+ };
976
+ const startLongPress = (tag, cat) => {
977
+ longPressActivated.current = false;
978
+ longPressTimer.current = setTimeout(() => {
979
+ longPressActivated.current = true;
980
+ setEditingTag({ ...tag, category: cat });
981
+ setEditLabel(tag.label);
982
+ setEditValue(tag.value);
983
+ }, LONG_PRESS_MS);
871
984
  };
872
- const categories = Object.entries(workspaceTags.by_category).filter(([, tags]) => tags.length > 0);
873
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "absolute inset-0 flex flex-col overflow-hidden", children: [
874
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 py-3 flex flex-col gap-4", children: [
875
- categories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
876
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
877
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
985
+ const cancelLongPress = () => {
986
+ if (longPressTimer.current) {
987
+ clearTimeout(longPressTimer.current);
988
+ longPressTimer.current = null;
989
+ }
990
+ };
991
+ const handleTagClick = (label) => {
992
+ if (longPressActivated.current) return;
993
+ toggleTag(label);
994
+ };
995
+ const handleSaveEdit = () => {
996
+ if (!editingTag || !editLabel.trim()) return;
997
+ onTagUpdate?.(editingTag.label, editingTag.category, {
998
+ label: editLabel.trim(),
999
+ value: editValue.trim() || editLabel.trim()
1000
+ });
1001
+ setSelectedLabels((prev) => {
1002
+ const next = new Set(prev);
1003
+ if (next.has(editingTag.label)) {
1004
+ next.delete(editingTag.label);
1005
+ next.add(editLabel.trim());
1006
+ }
1007
+ return next;
1008
+ });
1009
+ setEditingTag(null);
1010
+ };
1011
+ const handleDeleteTag = () => {
1012
+ if (!editingTag) return;
1013
+ onTagDelete?.(editingTag.label, editingTag.category);
1014
+ setSelectedLabels((prev) => {
1015
+ const next = new Set(prev);
1016
+ next.delete(editingTag.label);
1017
+ return next;
1018
+ });
1019
+ setEditingTag(null);
1020
+ };
1021
+ const handleCreateBased = () => {
1022
+ if (!editingTag) return;
1023
+ setAddingInCat(editingTag.category);
1024
+ setActiveCategory(editingTag.category);
1025
+ setNewLabel(editingTag.label);
1026
+ setNewValue(editingTag.value);
1027
+ setEditingTag(null);
1028
+ };
1029
+ const categories = Object.entries(workspaceTags.by_category).filter(([cat, tags]) => !EXCLUDED.includes(cat) && tags.some((t) => !t.is_deleted));
1030
+ const tagToolbar = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-1.5", children: [
1031
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1032
+ "button",
1033
+ {
1034
+ onClick: onRandom,
1035
+ disabled: !onRandom,
1036
+ className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
1037
+ children: "RANDOM"
1038
+ }
1039
+ ),
1040
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1041
+ "button",
1042
+ {
1043
+ onClick: onTokenize,
1044
+ disabled: !onTokenize || isTokenizing || !instructions,
1045
+ className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
1046
+ children: isTokenizing ? "..." : "TOKENIZE"
1047
+ }
1048
+ ),
1049
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1050
+ "button",
1051
+ {
1052
+ onClick: () => imgInputRef.current?.click(),
1053
+ disabled: !onScanImage || isScanning,
1054
+ className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
1055
+ children: isScanning ? "..." : "SCAN"
1056
+ }
1057
+ ),
1058
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1059
+ "input",
1060
+ {
1061
+ ref: imgInputRef,
1062
+ type: "file",
1063
+ accept: "image/*",
1064
+ className: "hidden",
1065
+ onChange: (e) => {
1066
+ if (e.target.files?.[0] && onScanImage) onScanImage(e.target.files[0]);
1067
+ }
1068
+ }
1069
+ ),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1071
+ "button",
1072
+ {
1073
+ onClick: onExpand,
1074
+ disabled: !onExpand || isExpanding || !instructions,
1075
+ className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
1076
+ children: isExpanding ? "..." : "EXPAND"
1077
+ }
1078
+ )
1079
+ ] });
1080
+ const canGenerate = selectedLabels.size > 0 || instructions.trim().length > 0 || rules.trim().length > 0;
1081
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar flex flex-col", children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-3 p-3 pb-6", children: [
1083
+ categories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
1084
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
1085
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
878
1086
  "Keine Tags geladen.",
879
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("br", {}),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("br", {}),
880
1088
  "Importiere ein Workspace-JSON im Setup-Tab."
881
1089
  ] })
882
- ] }) : categories.map(([cat, tags]) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
883
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SectionLabel, { children: cat }),
884
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-wrap gap-1", children: tags.map((t) => {
885
- const active = selectedLabels.has(t.label);
886
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
887
- "button",
1090
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1091
+ CollapsibleCard,
1092
+ {
1093
+ title: "INHALT",
1094
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "layers" }),
1095
+ defaultOpen: true,
1096
+ children: categories.map(([cat, tags]) => {
1097
+ const visibleTags = tags.filter((t) => !t.is_deleted);
1098
+ const selectedInCat = visibleTags.filter((t) => selectedLabels.has(t.label));
1099
+ const isOpen = activeCategory === cat;
1100
+ const isAdding = addingInCat === cat;
1101
+ const commitNewTag = () => {
1102
+ if (!newLabel.trim()) return;
1103
+ const valueToUse = newValue.trim() || newLabel.trim();
1104
+ onTagCreate?.({ label: newLabel.trim(), value: valueToUse, category: cat, is_user_created: true });
1105
+ setNewLabel("");
1106
+ setNewValue("");
1107
+ setAddingInCat(null);
1108
+ };
1109
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border-b border-neutral-800 last:border-0", children: [
1110
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center", children: [
1111
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1112
+ "button",
1113
+ {
1114
+ onClick: () => setActiveCategory(isOpen ? null : cat),
1115
+ className: "flex-grow flex items-center justify-between px-4 py-3 active:bg-white/5",
1116
+ children: [
1117
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm font-bold tracking-widest uppercase text-blue-400", children: cat }),
1118
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
1119
+ selectedInCat.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] text-green-400 truncate max-w-[150px]", children: selectedInCat.map((t) => t.label).join(", ") }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px] text-white/30 shrink-0", children: isOpen ? "expand_less" : "expand_more" })
1121
+ ] })
1122
+ ]
1123
+ }
1124
+ ),
1125
+ onTagCreate && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1126
+ "button",
1127
+ {
1128
+ onClick: (e) => {
1129
+ e.stopPropagation();
1130
+ setAddingInCat(isAdding ? null : cat);
1131
+ setActiveCategory(cat);
1132
+ setNewLabel("");
1133
+ setNewValue("");
1134
+ },
1135
+ className: "px-3 py-3 text-white/20 active:text-white/60 hover:text-white/50 transition-colors",
1136
+ title: "Neues Tag anlegen",
1137
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "add" })
1138
+ }
1139
+ )
1140
+ ] }),
1141
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 pb-3 flex flex-wrap gap-1.5", children: visibleTags.map((t) => {
1142
+ const active = selectedLabels.has(t.label);
1143
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1144
+ "button",
1145
+ {
1146
+ onMouseDown: () => startLongPress(t, cat),
1147
+ onMouseUp: cancelLongPress,
1148
+ onMouseLeave: cancelLongPress,
1149
+ onTouchStart: () => startLongPress(t, cat),
1150
+ onTouchEnd: cancelLongPress,
1151
+ onTouchCancel: cancelLongPress,
1152
+ onContextMenu: (e) => e.preventDefault(),
1153
+ onClick: () => handleTagClick(t.label),
1154
+ className: `px-2.5 py-1.5 rounded text-xs font-medium border transition-colors ${active ? "bg-blue-900/50 text-blue-200 border-blue-700" : "bg-blue-900/20 text-blue-400 border-blue-900/50 active:bg-blue-900/40 active:text-blue-200"} ${t.is_user_created ? "border-dashed" : ""}`,
1155
+ children: t.label
1156
+ },
1157
+ t.label
1158
+ );
1159
+ }) }),
1160
+ isAdding && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "px-4 pb-3 space-y-2 border-t border-neutral-800/50 pt-2", children: [
1161
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1162
+ "input",
1163
+ {
1164
+ autoFocus: true,
1165
+ value: newLabel,
1166
+ onChange: (e) => setNewLabel(e.target.value),
1167
+ onKeyDown: (e) => {
1168
+ if (e.key === "Enter") {
1169
+ e.preventDefault();
1170
+ commitNewTag();
1171
+ }
1172
+ },
1173
+ placeholder: "Label (z.B. Picasso)",
1174
+ className: "w-full bg-black border border-neutral-700 rounded px-3 py-1.5 text-xs text-white outline-none focus:border-blue-600"
1175
+ }
1176
+ ),
1177
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1178
+ "textarea",
1179
+ {
1180
+ value: newValue,
1181
+ onChange: (e) => setNewValue(e.target.value),
1182
+ placeholder: "Value (Prompt-Text) \u2014 leer l\xE4sst Label als Value",
1183
+ rows: 3,
1184
+ className: "w-full bg-black border border-neutral-700 rounded px-3 py-1.5 text-xs text-white outline-none focus:border-blue-600 resize-none"
1185
+ }
1186
+ ),
1187
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2", children: [
1188
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1189
+ "button",
1190
+ {
1191
+ onClick: commitNewTag,
1192
+ disabled: !newLabel.trim(),
1193
+ className: "flex items-center gap-1 px-3 py-1.5 rounded text-xs font-bold bg-blue-700 text-white disabled:opacity-30 active:bg-blue-600",
1194
+ children: [
1195
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "check" }),
1196
+ " Speichern"
1197
+ ]
1198
+ }
1199
+ ),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1201
+ "button",
1202
+ {
1203
+ onClick: () => {
1204
+ setAddingInCat(null);
1205
+ setNewLabel("");
1206
+ setNewValue("");
1207
+ },
1208
+ className: "flex items-center gap-1 px-3 py-1.5 rounded text-xs text-white/40 border border-neutral-700 active:bg-neutral-800",
1209
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" })
1210
+ }
1211
+ )
1212
+ ] })
1213
+ ] })
1214
+ ] }, cat);
1215
+ })
1216
+ }
1217
+ ),
1218
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1219
+ CollapsibleCard,
1220
+ {
1221
+ title: "BESCHREIBUNG",
1222
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "edit_note" }),
1223
+ actions: tagToolbar,
1224
+ defaultOpen: true,
1225
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1226
+ "textarea",
888
1227
  {
889
- onClick: () => toggleTag(t.label),
890
- className: `px-2 py-1 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-all border ${active ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_8px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/30 hover:text-white/60 hover:bg-white/10"}`,
891
- children: t.label
892
- },
893
- t.label
894
- );
895
- }) })
896
- ] }, cat)),
897
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2 mt-2", children: [
898
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SectionLabel, { children: "Zus\xE4tzliche Anweisungen" }),
899
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
900
- "textarea",
1228
+ value: instructions,
1229
+ onChange: (e) => setInstructions(e.target.value),
1230
+ placeholder: "...",
1231
+ className: "w-full bg-black border border-neutral-800 rounded outline-none text-xs text-white/80 placeholder-neutral-700 resize-none dark-scrollbar p-3",
1232
+ style: { minHeight: 80 }
1233
+ }
1234
+ ) })
1235
+ }
1236
+ ),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1238
+ CollapsibleCard,
1239
+ {
1240
+ title: "REGELN",
1241
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "rule" }),
1242
+ defaultOpen: false,
1243
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1244
+ "textarea",
1245
+ {
1246
+ value: rules,
1247
+ onChange: (e) => setRules(e.target.value),
1248
+ placeholder: "z.B. Verwende immer explizite Nacktheit unter transparentem Stoff. Tags in Flie\xDFtext einbauen. Kein Markdown.",
1249
+ className: "w-full bg-black border border-neutral-800 rounded outline-none text-xs text-white/80 placeholder-neutral-700 resize-none dark-scrollbar p-3",
1250
+ style: { minHeight: 80 }
1251
+ }
1252
+ ) })
1253
+ }
1254
+ ),
1255
+ selectedLabels.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { onClick: () => setSelectedLabels(/* @__PURE__ */ new Set()), className: "flex items-center gap-1 text-[10px] text-white/30 active:text-white/60 self-start", children: [
1256
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }),
1257
+ selectedLabels.size,
1258
+ " Tags gew\xE4hlt"
1259
+ ] }),
1260
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1261
+ "button",
1262
+ {
1263
+ onClick: handleGenerate,
1264
+ disabled: isGenerating || !canGenerate,
1265
+ className: "w-full py-4 rounded-lg font-bold text-sm flex items-center justify-center gap-2 bg-white text-black disabled:opacity-40 active:bg-neutral-200",
1266
+ children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1267
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px] animate-spin", children: "autorenew" }),
1268
+ "BAUT PROMPT..."
1269
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1270
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "auto_fix_high" }),
1271
+ "PROMPT BAUEN"
1272
+ ] })
1273
+ }
1274
+ ),
1275
+ promptResult && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1276
+ lastPayload && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1277
+ CollapsibleCard,
1278
+ {
1279
+ title: "INPUT MONITOR",
1280
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "terminal" }),
1281
+ defaultOpen: false,
1282
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("pre", { className: "text-[10px] font-mono text-blue-300/80 bg-blue-950/10 border border-blue-900/20 rounded p-3 overflow-x-auto whitespace-pre-wrap leading-relaxed", children: lastPayload }) })
1283
+ }
1284
+ ),
1285
+ feedback && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1286
+ CollapsibleCard,
901
1287
  {
902
- value: instructions,
903
- onChange: (e) => setInstructions(e.target.value),
904
- placeholder: "Weitere Vorgaben f\xFCr den AI-Prompt...",
905
- className: "w-full bg-white/5 border border-white/10 rounded-xl p-3 text-[10px] text-white/70 placeholder-white/20 resize-none h-20 outline-none focus:border-white/20 dark-scrollbar"
1288
+ title: "ANALYSE",
1289
+ icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "chat" }),
1290
+ defaultOpen: true,
1291
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs font-mono text-orange-200/80 bg-orange-950/10 border border-orange-900/20 rounded p-3 leading-relaxed", children: feedback }) })
1292
+ }
1293
+ ),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border border-neutral-800 rounded-lg", children: [
1295
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between px-4 py-3 bg-neutral-900/50 rounded-t-lg", children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-widest text-neutral-400", children: "Prompt" }),
1297
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1298
+ "button",
1299
+ {
1300
+ onClick: handleCopy,
1301
+ className: "flex items-center gap-1.5 text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800",
1302
+ children: [
1303
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: copied ? "check" : "content_copy" }),
1304
+ copied ? "KOPIERT" : "KOPIEREN"
1305
+ ]
1306
+ }
1307
+ )
1308
+ ] }),
1309
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-white/80 leading-relaxed italic", children: promptResult }) })
1310
+ ] }),
1311
+ onGenerateImage && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1312
+ "button",
1313
+ {
1314
+ onClick: () => onGenerateImage(promptResult),
1315
+ disabled: isGeneratingImage,
1316
+ className: "w-full py-4 rounded-lg bg-white text-black font-bold text-sm flex items-center justify-center gap-2 disabled:opacity-40 active:bg-neutral-200",
1317
+ children: [
1318
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isGeneratingImage ? "hourglass_empty" : "image" }),
1319
+ isGeneratingImage ? "GENERIERE..." : "GENERIEREN"
1320
+ ]
906
1321
  }
907
1322
  )
908
1323
  ] })
909
1324
  ] }),
910
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "shrink-0 border-t border-white/5 p-3 flex flex-col gap-2", children: [
911
- feedback && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col", children: [
912
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
913
- "button",
1325
+ editingTag && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1326
+ "div",
1327
+ {
1328
+ className: "fixed inset-0 z-50 flex items-end justify-center",
1329
+ style: { background: "rgba(0,0,0,0.75)" },
1330
+ onClick: () => setEditingTag(null),
1331
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1332
+ "div",
914
1333
  {
915
- onClick: () => setShowFeedback((p) => !p),
916
- className: "flex items-center gap-1.5 text-[8px] font-bold uppercase text-amber-400/60 hover:text-amber-400 transition-colors mb-1",
1334
+ className: "w-full max-w-lg bg-[#1c1c1c] border border-neutral-700 rounded-t-2xl p-5 space-y-4",
1335
+ onClick: (e) => e.stopPropagation(),
917
1336
  children: [
918
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[12px]", children: "psychology" }),
919
- "KI-Analyse",
920
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[12px] ml-auto", children: showFeedback ? "expand_less" : "expand_more" })
1337
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between", children: [
1338
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] font-bold text-neutral-400 uppercase tracking-widest", children: editingTag.category }),
1339
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setEditingTag(null), className: "text-white/30 active:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
1340
+ ] }),
1341
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1342
+ "input",
1343
+ {
1344
+ autoFocus: true,
1345
+ value: editLabel,
1346
+ onChange: (e) => setEditLabel(e.target.value),
1347
+ placeholder: "Label",
1348
+ className: "w-full bg-black border border-neutral-700 rounded px-3 py-2 text-sm text-white outline-none focus:border-blue-600"
1349
+ }
1350
+ ),
1351
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1352
+ "textarea",
1353
+ {
1354
+ value: editValue,
1355
+ onChange: (e) => setEditValue(e.target.value),
1356
+ placeholder: "Value (Prompt-Text)",
1357
+ rows: 4,
1358
+ className: "w-full bg-black border border-neutral-700 rounded px-3 py-2 text-xs text-white outline-none focus:border-blue-600 resize-none"
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2 flex-wrap", children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1363
+ "button",
1364
+ {
1365
+ onClick: handleSaveEdit,
1366
+ disabled: !editLabel.trim(),
1367
+ className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold bg-blue-700 text-white disabled:opacity-30 active:bg-blue-600",
1368
+ children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "check" }),
1370
+ " Speichern"
1371
+ ]
1372
+ }
1373
+ ),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1375
+ "button",
1376
+ {
1377
+ onClick: handleCreateBased,
1378
+ className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold border border-neutral-600 text-white/70 active:bg-neutral-800",
1379
+ children: [
1380
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "content_copy" }),
1381
+ " Neu auf Basis"
1382
+ ]
1383
+ }
1384
+ ),
1385
+ onTagDelete && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1386
+ "button",
1387
+ {
1388
+ onClick: handleDeleteTag,
1389
+ className: "flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold border border-red-900/60 text-red-400 active:bg-red-900/20",
1390
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "delete" })
1391
+ }
1392
+ )
1393
+ ] })
921
1394
  ]
922
1395
  }
1396
+ )
1397
+ }
1398
+ )
1399
+ ] });
1400
+ };
1401
+
1402
+ // src/components/ProjectSyncTab.tsx
1403
+ var import_react13 = require("react");
1404
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1405
+ var ProjectSyncTab = ({
1406
+ onProjectExport,
1407
+ onProjectImport,
1408
+ onWorkspaceImport,
1409
+ projectActionState,
1410
+ serverProjects,
1411
+ onServerSave,
1412
+ onServerLoad,
1413
+ onServerDelete,
1414
+ onRefreshServerProjects,
1415
+ onComputeSyncDiff,
1416
+ onExecuteSync
1417
+ }) => {
1418
+ const projectInputRef = (0, import_react13.useRef)(null);
1419
+ const workspaceInputRef = (0, import_react13.useRef)(null);
1420
+ const [saveName, setSaveName] = (0, import_react13.useState)("");
1421
+ const [isSaving, setIsSaving] = (0, import_react13.useState)(false);
1422
+ const [isExporting, setIsExporting] = (0, import_react13.useState)(false);
1423
+ const [syncState, setSyncState] = (0, import_react13.useState)("idle");
1424
+ const [syncDiff, setSyncDiff] = (0, import_react13.useState)(null);
1425
+ const [selectedLocalIds, setSelectedLocalIds] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
1426
+ const handleExport = async () => {
1427
+ if (!onProjectExport) return;
1428
+ setIsExporting(true);
1429
+ try {
1430
+ await onProjectExport();
1431
+ } finally {
1432
+ setIsExporting(false);
1433
+ }
1434
+ };
1435
+ const handleComputeDiff = async () => {
1436
+ if (!onComputeSyncDiff) return;
1437
+ setSyncState("computing");
1438
+ setSyncDiff(null);
1439
+ try {
1440
+ const diff = await onComputeSyncDiff();
1441
+ setSyncDiff(diff);
1442
+ setSelectedLocalIds(new Set(diff.localOnly.map((g) => g.id)));
1443
+ setSyncState("ready");
1444
+ } catch {
1445
+ setSyncState("error");
1446
+ setTimeout(() => setSyncState("idle"), 4e3);
1447
+ }
1448
+ };
1449
+ const handleExecuteSync = async () => {
1450
+ if (!onExecuteSync || !syncDiff) return;
1451
+ setSyncState("executing");
1452
+ try {
1453
+ await onExecuteSync([...selectedLocalIds]);
1454
+ setSyncState("done");
1455
+ setSyncDiff(null);
1456
+ setTimeout(() => setSyncState("idle"), 3e3);
1457
+ } catch {
1458
+ setSyncState("error");
1459
+ setTimeout(() => setSyncState("idle"), 4e3);
1460
+ }
1461
+ };
1462
+ const toggleLocalId = (id) => {
1463
+ setSelectedLocalIds((prev) => {
1464
+ const next = new Set(prev);
1465
+ if (next.has(id)) next.delete(id);
1466
+ else next.add(id);
1467
+ return next;
1468
+ });
1469
+ };
1470
+ const isWorking = projectActionState === "working" || projectActionState === "working-full";
1471
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-6 flex flex-col gap-8", children: [
1472
+ (onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
1473
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Projekt-ZIP" }),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
1475
+ onProjectExport && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "filled", icon: "download", loading: isExporting || isWorking, onClick: handleExport, children: "Exportieren (.zip)" }),
1476
+ onProjectImport && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1477
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "ZIP laden" }),
1478
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1479
+ "input",
1480
+ {
1481
+ ref: projectInputRef,
1482
+ type: "file",
1483
+ accept: ".zip",
1484
+ className: "hidden",
1485
+ onChange: (e) => {
1486
+ const f = e.target.files?.[0];
1487
+ if (f) onProjectImport(f);
1488
+ e.target.value = "";
1489
+ }
1490
+ }
1491
+ )
1492
+ ] })
1493
+ ] }),
1494
+ onWorkspaceImport && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
1496
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1497
+ "input",
1498
+ {
1499
+ ref: workspaceInputRef,
1500
+ type: "file",
1501
+ accept: ".json",
1502
+ className: "hidden",
1503
+ onChange: (e) => {
1504
+ const f = e.target.files?.[0];
1505
+ if (f) onWorkspaceImport(f);
1506
+ e.target.value = "";
1507
+ }
1508
+ }
1509
+ )
1510
+ ] })
1511
+ ] }),
1512
+ (onServerSave || serverProjects || onServerDelete) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Server-Speicher" }),
1514
+ onServerSave && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-2", children: [
1515
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1516
+ "input",
1517
+ {
1518
+ type: "text",
1519
+ value: saveName,
1520
+ onChange: (e) => setSaveName(e.target.value),
1521
+ placeholder: "Name (optional)",
1522
+ className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 text-[12px] text-white/70 outline-none placeholder:text-white/20",
1523
+ style: { height: 40 }
1524
+ }
923
1525
  ),
924
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.AnimatePresence, { children: showFeedback && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
925
- import_react11.motion.div,
1526
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1527
+ PillButton,
926
1528
  {
927
- initial: { height: 0, opacity: 0 },
928
- animate: { height: "auto", opacity: 1 },
929
- exit: { height: 0, opacity: 0 },
930
- className: "overflow-hidden",
931
- children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[9px] text-amber-400/50 leading-relaxed bg-amber-500/5 border border-amber-500/10 rounded-xl p-2 italic", children: feedback })
1529
+ variant: "filled",
1530
+ icon: "cloud_upload",
1531
+ loading: isSaving,
1532
+ onClick: async () => {
1533
+ setIsSaving(true);
1534
+ try {
1535
+ await onServerSave(saveName || void 0);
1536
+ setSaveName("");
1537
+ await onRefreshServerProjects?.();
1538
+ } finally {
1539
+ setIsSaving(false);
1540
+ }
1541
+ },
1542
+ children: "Speichern"
932
1543
  }
933
- ) })
1544
+ )
1545
+ ] }),
1546
+ (!serverProjects || serverProjects.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts gespeichert." }),
1547
+ serverProjects && serverProjects.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col gap-1", children: serverProjects.map((p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
1548
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
1549
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[11px] text-white/70 font-bold truncate", children: p.name }),
1550
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/25", children: [
1551
+ new Date(p.saved_at).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }),
1552
+ " \xB7 ",
1553
+ (p.size / 1024).toFixed(0),
1554
+ " KB"
1555
+ ] })
1556
+ ] }),
1557
+ onServerLoad && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: () => onServerLoad(p.id), className: "w-8 h-8 flex items-center justify-center text-white/30 active:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "cloud_download" }) }),
1558
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: () => onServerDelete?.(p.id), className: "w-8 h-8 flex items-center justify-center text-white/20 active:text-red-400 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "delete" }) })
1559
+ ] }, p.id)) })
1560
+ ] }),
1561
+ onComputeSyncDiff && onExecuteSync && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
1562
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Sync" }),
1563
+ (syncState === "idle" || syncState === "computing") && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "compare_arrows", loading: syncState === "computing", onClick: handleComputeDiff, children: "Diff berechnen" }),
1564
+ syncState === "done" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 px-2 text-[11px] text-green-400", children: [
1565
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "check_circle" }),
1566
+ "Sync abgeschlossen"
934
1567
  ] }),
935
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
936
- selectedLabels.size > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "text-[8px] text-white/30 font-bold uppercase", children: [
937
- selectedLabels.size,
938
- " Tags gew\xE4hlt"
939
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[8px] text-white/20 italic", children: "Keine Tags gew\xE4hlt" }),
940
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1568
+ syncState === "error" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[11px] text-red-400 px-2", children: "Fehler beim Sync." }),
1569
+ syncDiff && (syncState === "ready" || syncState === "executing") && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1570
+ syncDiff.localOnly.length === 0 && syncDiff.serverOnly.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/30 px-2", children: "Alles synchron \u2014 keine Unterschiede." }),
1571
+ syncDiff.localOnly.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
1573
+ "Nur lokal (",
1574
+ syncDiff.localOnly.length,
1575
+ ")"
1576
+ ] }),
1577
+ syncDiff.localOnly.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1578
+ "button",
1579
+ {
1580
+ onClick: () => toggleLocalId(gen.id),
1581
+ className: `flex items-center gap-3 px-3 py-2 rounded-xl border transition-all text-left ${selectedLocalIds.has(gen.id) ? "bg-blue-500/10 border-blue-500/30" : "bg-white/3 border-white/5 opacity-60"}`,
1582
+ children: [
1583
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: `w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 ${selectedLocalIds.has(gen.id) ? "bg-blue-500 border-blue-500" : "border-white/20"}`, children: selectedLocalIds.has(gen.id) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[11px] text-white", children: "check" }) }),
1584
+ gen.base64 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
1585
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
1586
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
1587
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
1588
+ ] })
1589
+ ]
1590
+ },
1591
+ gen.id
1592
+ ))
1593
+ ] }),
1594
+ syncDiff.serverOnly.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
1596
+ "Nur auf Server (",
1597
+ syncDiff.serverOnly.length,
1598
+ ")"
1599
+ ] }),
1600
+ syncDiff.serverOnly.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-3 px-3 py-2 rounded-xl bg-white/3 border border-white/5 opacity-40", children: [
1601
+ gen.base64 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
1602
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
1603
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
1604
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
1605
+ ] })
1606
+ ] }, gen.id))
1607
+ ] }),
1608
+ (syncDiff.localOnly.length > 0 || syncDiff.serverOnly.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
941
1609
  PillButton,
942
1610
  {
943
1611
  variant: "solid",
944
- icon: "auto_fix_high",
945
- loading: isGenerating,
946
- disabled: selectedLabels.size === 0 && !instructions.trim(),
947
- onClick: handleGenerate,
948
- children: "Prompt bauen"
1612
+ icon: "sync",
1613
+ loading: syncState === "executing",
1614
+ disabled: selectedLocalIds.size === 0,
1615
+ onClick: handleExecuteSync,
1616
+ children: [
1617
+ selectedLocalIds.size,
1618
+ " \xFCbertragen"
1619
+ ]
949
1620
  }
950
1621
  )
951
1622
  ] })
952
1623
  ] })
953
- ] });
1624
+ ] }) });
954
1625
  };
955
1626
 
956
1627
  // src/components/AvatarArchitectApp.tsx
957
- var import_jsx_runtime11 = require("react/jsx-runtime");
958
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
959
- (0, import_react12.useEffect)(() => {
1628
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1629
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
1630
+ (0, import_react14.useEffect)(() => {
960
1631
  const id = "flow-styles";
961
1632
  if (!document.getElementById(id)) {
962
1633
  const style = document.createElement("style");
@@ -965,31 +1636,125 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
965
1636
  document.head.appendChild(style);
966
1637
  }
967
1638
  }, []);
968
- const [nodes, setNodes] = (0, import_react12.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
969
- const [edges, setEdges] = (0, import_react12.useState)([]);
970
- const [history, setHistory] = (0, import_react12.useState)([]);
971
- const [galleryItems, setGalleryItems] = (0, import_react12.useState)([]);
972
- const [activePrompt, setActivePrompt] = (0, import_react12.useState)("");
973
- const [isSynthesizing, setIsSynthesizing] = (0, import_react12.useState)(false);
974
- const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react12.useState)(0);
975
- const [currentResult, setCurrentResult] = (0, import_react12.useState)(null);
976
- const [focusedNodeId, setFocusedNodeId] = (0, import_react12.useState)(null);
977
- const [leftTab, setLeftTab] = (0, import_react12.useState)("hierarchy");
978
- const [promptFeedback, setPromptFeedback] = (0, import_react12.useState)(null);
979
- const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react12.useState)(false);
980
- const [activeTab, setActiveTab] = (0, import_react12.useState)("history");
981
- const [aspectRatio, setAspectRatio] = (0, import_react12.useState)("1:1");
982
- const [selectedModel, setSelectedModel] = (0, import_react12.useState)("\u{1F34C} Nano Banana Pro");
983
- const [seed, setSeed] = (0, import_react12.useState)(Math.floor(Math.random() * 1e6));
984
- const [seedMode, setSeedMode] = (0, import_react12.useState)("random");
985
- const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react12.useState)(false);
986
- const [isRightCollapsed, setIsRightCollapsed] = (0, import_react12.useState)(false);
987
- const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react12.useState)(false);
988
- const [projectActionState, setProjectActionState] = (0, import_react12.useState)("idle");
989
- const [workspaceTags, setWorkspaceTags] = (0, import_react12.useState)(null);
1639
+ const [showStart, setShowStart] = (0, import_react14.useState)(true);
1640
+ const [layoutChoice, setLayoutChoice] = (0, import_react14.useState)(() => {
1641
+ try {
1642
+ return localStorage.getItem("aa-layout") || null;
1643
+ } catch {
1644
+ return null;
1645
+ }
1646
+ });
1647
+ const [projectLoaded, setProjectLoaded] = (0, import_react14.useState)(false);
1648
+ const wsInputRef = (0, import_react14.useRef)(null);
1649
+ const startApp = (choice) => {
1650
+ try {
1651
+ localStorage.setItem("aa-layout", choice);
1652
+ } catch {
1653
+ }
1654
+ setLayoutChoice(choice);
1655
+ setShowStart(false);
1656
+ };
1657
+ const [nodes, setNodes] = (0, import_react14.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
1658
+ const [edges, setEdges] = (0, import_react14.useState)([]);
1659
+ const [history, setHistory] = (0, import_react14.useState)([]);
1660
+ const [galleryItems, setGalleryItems] = (0, import_react14.useState)([]);
1661
+ const [activePrompt, setActivePrompt] = (0, import_react14.useState)("");
1662
+ const [isSynthesizing, setIsSynthesizing] = (0, import_react14.useState)(false);
1663
+ const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react14.useState)(0);
1664
+ const [currentResult, setCurrentResult] = (0, import_react14.useState)(null);
1665
+ const [focusedNodeId, setFocusedNodeId] = (0, import_react14.useState)(null);
1666
+ const [leftTab, setLeftTab] = (0, import_react14.useState)("prompt");
1667
+ const [promptFeedback, setPromptFeedback] = (0, import_react14.useState)(null);
1668
+ const [lastPromptPayload, setLastPromptPayload] = (0, import_react14.useState)(null);
1669
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react14.useState)(false);
1670
+ const [activeTab, setActiveTab] = (0, import_react14.useState)("history");
1671
+ const [mobileTab, setMobileTab] = (0, import_react14.useState)("stage");
1672
+ const [aspectRatio, setAspectRatio] = (0, import_react14.useState)("1:1");
1673
+ const [selectedModel, setSelectedModel] = (0, import_react14.useState)("\u{1F34C} Nano Banana Pro");
1674
+ const [seed, setSeed] = (0, import_react14.useState)(Math.floor(Math.random() * 1e6));
1675
+ const [seedMode, setSeedMode] = (0, import_react14.useState)("random");
1676
+ const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react14.useState)(false);
1677
+ const [isRightCollapsed, setIsRightCollapsed] = (0, import_react14.useState)(false);
1678
+ const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react14.useState)(false);
1679
+ const [projectActionState, setProjectActionState] = (0, import_react14.useState)("idle");
1680
+ const syncServerDataRef = (0, import_react14.useRef)(null);
1681
+ const [workspaceTags, setWorkspaceTags] = (0, import_react14.useState)(null);
1682
+ const [serverProjects, setServerProjects] = (0, import_react14.useState)([]);
1683
+ const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react14.useState)(false);
1684
+ const [highContrast, setHighContrast] = (0, import_react14.useState)(() => {
1685
+ try {
1686
+ return localStorage.getItem("aa-contrast") === "high";
1687
+ } catch {
1688
+ return false;
1689
+ }
1690
+ });
1691
+ const [touchStartX, setTouchStartX] = (0, import_react14.useState)(null);
1692
+ const [isFullscreen, setIsFullscreen] = (0, import_react14.useState)(false);
1693
+ const [zoomScale, setZoomScale] = (0, import_react14.useState)(1);
1694
+ const [zoomOffset, setZoomOffset] = (0, import_react14.useState)({ x: 0, y: 0 });
1695
+ const lastPinchDist = (0, import_react14.useRef)(null);
1696
+ const lastTapTime = (0, import_react14.useRef)(0);
1697
+ const dragStart = (0, import_react14.useRef)(null);
1698
+ const openFullscreen = () => {
1699
+ setIsFullscreen(true);
1700
+ setZoomScale(1);
1701
+ setZoomOffset({ x: 0, y: 0 });
1702
+ };
1703
+ const closeFullscreen = () => {
1704
+ setIsFullscreen(false);
1705
+ setZoomScale(1);
1706
+ setZoomOffset({ x: 0, y: 0 });
1707
+ };
1708
+ const handleFsTouchStart = (e) => {
1709
+ if (e.touches.length === 2) {
1710
+ lastPinchDist.current = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
1711
+ } else if (e.touches.length === 1) {
1712
+ const now = Date.now();
1713
+ if (now - lastTapTime.current < 280) {
1714
+ setZoomScale((s) => s > 1 ? 1 : 2.5);
1715
+ setZoomOffset({ x: 0, y: 0 });
1716
+ }
1717
+ lastTapTime.current = now;
1718
+ dragStart.current = { x: e.touches[0].clientX, y: e.touches[0].clientY, ox: zoomOffset.x, oy: zoomOffset.y };
1719
+ }
1720
+ };
1721
+ const handleFsTouchMove = (e) => {
1722
+ if (e.touches.length === 2 && lastPinchDist.current !== null) {
1723
+ const dist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
1724
+ setZoomScale((s) => Math.min(Math.max(s * (dist / lastPinchDist.current), 1), 5));
1725
+ lastPinchDist.current = dist;
1726
+ } else if (e.touches.length === 1 && dragStart.current && zoomScale > 1) {
1727
+ setZoomOffset({ x: dragStart.current.ox + e.touches[0].clientX - dragStart.current.x, y: dragStart.current.oy + e.touches[0].clientY - dragStart.current.y });
1728
+ }
1729
+ };
1730
+ const handleFsTouchEnd = (e) => {
1731
+ lastPinchDist.current = null;
1732
+ if (e.changedTouches.length === 1 && zoomScale <= 1 && dragStart.current) {
1733
+ const dx = e.changedTouches[0].clientX - dragStart.current.x;
1734
+ if (dx < -50) goToNext();
1735
+ else if (dx > 50) goToPrev();
1736
+ }
1737
+ dragStart.current = null;
1738
+ };
1739
+ const toggleContrast = () => {
1740
+ const next = !highContrast;
1741
+ setHighContrast(next);
1742
+ try {
1743
+ localStorage.setItem("aa-contrast", next ? "high" : "low");
1744
+ } catch {
1745
+ }
1746
+ };
1747
+ const currentIndex = (0, import_react14.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1748
+ const goToPrev = (0, import_react14.useCallback)(() => {
1749
+ if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
1750
+ }, [currentIndex, history]);
1751
+ const goToNext = (0, import_react14.useCallback)(() => {
1752
+ if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
1753
+ }, [currentIndex, history]);
1754
+ const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
990
1755
  const isGenerating = activeGenerationsCount > 0;
991
1756
  useKeyboardNavigation(history, currentResult, setCurrentResult);
992
- const getSubtreeFormat = (0, import_react12.useCallback)((nodeId, depth = 0) => {
1757
+ const getSubtreeFormat = (0, import_react14.useCallback)((nodeId, depth = 0) => {
993
1758
  const node = nodes.find((n) => n.id === nodeId);
994
1759
  if (!node) return "";
995
1760
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -997,7 +1762,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
997
1762
  return `${indent}- ${node.data.label || "(unbenannt)"}
998
1763
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
999
1764
  }, [nodes, edges]);
1000
- const activePath = (0, import_react12.useMemo)(() => {
1765
+ const activePath = (0, import_react14.useMemo)(() => {
1001
1766
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
1002
1767
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
1003
1768
  let currId = focusedNodeId;
@@ -1015,17 +1780,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1015
1780
  setActiveGenerationsCount((prev) => prev + 1);
1016
1781
  const genId = crypto.randomUUID();
1017
1782
  const activeSeed = seedMode === "random" ? Math.floor(Math.random() * 1e9) : seed;
1018
- const newGen = {
1019
- id: genId,
1020
- nodeId: overrideNodeId || focusedNodeId || "1",
1021
- status: "processing",
1022
- timestamp: Date.now(),
1023
- prompt: promptToUse,
1024
- seed: activeSeed,
1025
- model: selectedModel,
1026
- tags: [],
1027
- type: "generation"
1028
- };
1783
+ const newGen = { id: genId, nodeId: overrideNodeId || focusedNodeId || "1", status: "processing", timestamp: Date.now(), prompt: promptToUse, seed: activeSeed, model: selectedModel, tags: [], type: "generation" };
1029
1784
  setHistory((prev) => [newGen, ...prev]);
1030
1785
  if (!options.silent) setCurrentResult(newGen);
1031
1786
  if (seedMode === "random") setSeed(activeSeed);
@@ -1062,12 +1817,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1062
1817
  setIsSynthesizing(false);
1063
1818
  }
1064
1819
  };
1065
- const handlePromptTabGenerate = async (selectedValues, instructions) => {
1820
+ const handleTagCreate = (tag) => {
1821
+ setWorkspaceTags((prev) => {
1822
+ if (!prev) return prev;
1823
+ const newTagOption = { label: tag.label, value: tag.value, is_user_created: true };
1824
+ const updatedCat = [...prev.by_category[tag.category] || [], newTagOption];
1825
+ return {
1826
+ by_category: { ...prev.by_category, [tag.category]: updatedCat },
1827
+ all: [...prev.all, { ...newTagOption, category: tag.category }]
1828
+ };
1829
+ });
1830
+ };
1831
+ const handleTagUpdate = (originalLabel, originalCategory, updates) => {
1832
+ setWorkspaceTags((prev) => {
1833
+ if (!prev) return prev;
1834
+ const updatedCat = (prev.by_category[originalCategory] || []).map(
1835
+ (t) => t.label === originalLabel ? { ...t, ...updates } : t
1836
+ );
1837
+ const updatedAll = prev.all.map(
1838
+ (t) => t.label === originalLabel && t.category === originalCategory ? { ...t, ...updates } : t
1839
+ );
1840
+ return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
1841
+ });
1842
+ };
1843
+ const handleTagDelete = (label, category) => {
1844
+ setWorkspaceTags((prev) => {
1845
+ if (!prev) return prev;
1846
+ const updatedCat = (prev.by_category[category] || []).map(
1847
+ (t) => t.label === label ? { ...t, is_deleted: true } : t
1848
+ );
1849
+ const updatedAll = prev.all.map(
1850
+ (t) => t.label === label && t.category === category ? { ...t, is_deleted: true } : t
1851
+ );
1852
+ return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
1853
+ });
1854
+ };
1855
+ const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
1066
1856
  setIsPromptTabGenerating(true);
1067
1857
  setPromptFeedback(null);
1068
1858
  try {
1069
1859
  const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : void 0;
1070
- const payload = buildPromptTabPayload(selectedValues, instructions, treeText);
1860
+ const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
1861
+ setLastPromptPayload(payload);
1071
1862
  const raw = await onGeneratePrompt(payload);
1072
1863
  const { prompt, feedback } = parsePromptResponse(raw);
1073
1864
  setActivePrompt(prompt);
@@ -1084,9 +1875,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1084
1875
  const base64Data = currentResult.base64.split(",")[1];
1085
1876
  const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
1086
1877
  let finalBase64 = base64Data;
1087
- if (mimeType === "image/png") {
1088
- finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
1089
- }
1878
+ if (mimeType === "image/png") finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
1090
1879
  await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
1091
1880
  } catch (e) {
1092
1881
  console.error("Download Error", e);
@@ -1095,7 +1884,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1095
1884
  const handleProjectExport = async () => {
1096
1885
  setProjectActionState("working-full");
1097
1886
  try {
1098
- const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode });
1887
+ const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
1099
1888
  await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
1100
1889
  setProjectActionState("done");
1101
1890
  setTimeout(() => setProjectActionState("idle"), 3e3);
@@ -1111,7 +1900,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1111
1900
  const data = await importProjectFromZip(file);
1112
1901
  if (data.nodes) setNodes(data.nodes);
1113
1902
  if (data.edges) setEdges(data.edges);
1114
- if (data.history) setHistory(data.history);
1903
+ if (data.history) {
1904
+ setHistory(data.history);
1905
+ if (data.history.length > 0) setCurrentResult(data.history[0]);
1906
+ }
1115
1907
  if (data.galleryItems) setGalleryItems(data.galleryItems);
1116
1908
  if (data.settings) {
1117
1909
  setAspectRatio(data.settings.aspectRatio || "1:1");
@@ -1119,7 +1911,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1119
1911
  setSeed(data.settings.seed || 0);
1120
1912
  setSeedMode(data.settings.seedMode || "random");
1121
1913
  }
1914
+ if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
1122
1915
  setProjectActionState("done");
1916
+ setProjectLoaded(true);
1123
1917
  setTimeout(() => setProjectActionState("idle"), 2e3);
1124
1918
  } catch {
1125
1919
  setProjectActionState("error");
@@ -1134,12 +1928,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1134
1928
  const rawTags = root.tags || root;
1135
1929
  if (!rawTags?.by_category) return;
1136
1930
  const filtered = {};
1137
- const EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
1931
+ const EXCLUDED2 = ["pdf-export", "book.micro-edit-rules"];
1138
1932
  Object.entries(rawTags.by_category).forEach(([cat, items]) => {
1139
- if (EXCLUDED.includes(cat) || !Array.isArray(items)) return;
1933
+ if (EXCLUDED2.includes(cat) || !Array.isArray(items)) return;
1140
1934
  filtered[cat] = items.filter((t) => t && typeof t === "object" && t.label && t.value);
1141
1935
  });
1142
- const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED.includes(t.category));
1936
+ const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED2.includes(t.category));
1143
1937
  setWorkspaceTags({ by_category: filtered, all: filteredAll });
1144
1938
  } catch (err) {
1145
1939
  console.error("Workspace Load failed", err);
@@ -1147,37 +1941,571 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1147
1941
  };
1148
1942
  reader.readAsText(file);
1149
1943
  };
1150
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", children: [
1151
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react13.motion.div, { animate: { width: isLeftCollapsed ? 48 : 260 }, className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", children: [
1152
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
1153
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-1 gap-1", children: [
1154
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1155
- "button",
1944
+ const fetchServerProjects = async () => {
1945
+ if (!onFetchServerProjects) return;
1946
+ try {
1947
+ setServerProjects(await onFetchServerProjects());
1948
+ } catch {
1949
+ }
1950
+ };
1951
+ const handleServerSave = async (name) => {
1952
+ if (!onServerSave) return;
1953
+ setProjectActionState("working-full");
1954
+ try {
1955
+ const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
1956
+ await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
1957
+ await fetchServerProjects();
1958
+ setProjectActionState("done");
1959
+ setTimeout(() => setProjectActionState("idle"), 2e3);
1960
+ } catch {
1961
+ setProjectActionState("error");
1962
+ setTimeout(() => setProjectActionState("idle"), 4e3);
1963
+ }
1964
+ };
1965
+ const handleServerLoad = async (id) => {
1966
+ if (!onServerLoad) return;
1967
+ const file = await onServerLoad(id);
1968
+ await handleProjectImport(file);
1969
+ };
1970
+ const handleServerDelete = async (id) => {
1971
+ if (!onServerDelete) return;
1972
+ await onServerDelete(id);
1973
+ await fetchServerProjects();
1974
+ };
1975
+ const handleComputeSyncDiff = async () => {
1976
+ if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
1977
+ const projects = await onFetchServerProjects();
1978
+ if (projects.length === 0) return { localOnly: [...history], serverOnly: [] };
1979
+ const file = await onServerLoad(projects[0].id);
1980
+ const serverData = await importProjectFromZip(file);
1981
+ syncServerDataRef.current = serverData;
1982
+ const localOnly = history.filter((h) => !(serverData.history || []).find((s) => s.id === h.id));
1983
+ const serverOnly = (serverData.history || []).filter((s) => !history.find((h) => h.id === s.id));
1984
+ return { localOnly, serverOnly };
1985
+ };
1986
+ const handleExecuteSync = async (selectedLocalIds) => {
1987
+ const serverData = syncServerDataRef.current;
1988
+ if (!serverData || !onServerSave) return;
1989
+ setProjectActionState("working-full");
1990
+ try {
1991
+ const selectedLocal = history.filter((h) => selectedLocalIds.includes(h.id));
1992
+ const mergedHistory = [...serverData.history || [], ...selectedLocal];
1993
+ const { base64 } = await exportProjectToZip(
1994
+ serverData.nodes || nodes,
1995
+ serverData.edges || edges,
1996
+ mergedHistory,
1997
+ serverData.galleryItems || galleryItems,
1998
+ serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
1999
+ serverData.workspaceTags || workspaceTags
2000
+ );
2001
+ await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
2002
+ await fetchServerProjects();
2003
+ setProjectActionState("done");
2004
+ setTimeout(() => setProjectActionState("idle"), 2e3);
2005
+ } catch {
2006
+ setProjectActionState("error");
2007
+ setTimeout(() => setProjectActionState("idle"), 4e3);
2008
+ }
2009
+ };
2010
+ (0, import_react14.useEffect)(() => {
2011
+ if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
2012
+ }, [activeTab]);
2013
+ if (isFullscreen && currentResult?.base64) {
2014
+ const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
2015
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2016
+ "div",
2017
+ {
2018
+ className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
2019
+ onTouchStart: handleFsTouchStart,
2020
+ onTouchMove: handleFsTouchMove,
2021
+ onTouchEnd: handleFsTouchEnd,
2022
+ children: [
2023
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2024
+ "img",
1156
2025
  {
1157
- onClick: () => setLeftTab("hierarchy"),
1158
- className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`,
1159
- children: [
1160
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
1161
- "Hierarchie"
1162
- ]
2026
+ src: fsBase64,
2027
+ alt: "",
2028
+ draggable: false,
2029
+ style: {
2030
+ width: "100%",
2031
+ height: "100%",
2032
+ objectFit: "contain",
2033
+ transform: `scale(${zoomScale}) translate(${zoomOffset.x / zoomScale}px, ${zoomOffset.y / zoomScale}px)`,
2034
+ transformOrigin: "center center",
2035
+ userSelect: "none",
2036
+ pointerEvents: "none"
2037
+ }
1163
2038
  }
1164
2039
  ),
1165
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1166
- "button",
2040
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: closeFullscreen, className: "absolute top-4 right-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
2041
+ zoomScale > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
2042
+ setZoomScale(1);
2043
+ setZoomOffset({ x: 0, y: 0 });
2044
+ }, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
2045
+ history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2046
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
2047
+ if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
2048
+ }, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
2049
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
2050
+ if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
2051
+ }, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
2052
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
2053
+ currentIndex + 1,
2054
+ " / ",
2055
+ history.length
2056
+ ] })
2057
+ ] }),
2058
+ zoomScale === 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
2059
+ ]
2060
+ }
2061
+ );
2062
+ }
2063
+ if (showStart) {
2064
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
2065
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
2066
+ const f = e.target.files?.[0];
2067
+ if (f) handleProjectImport(f);
2068
+ e.target.value = "";
2069
+ } }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
2071
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
2072
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" })
2073
+ ] }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2075
+ "button",
2076
+ {
2077
+ onClick: toggleContrast,
2078
+ className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
2079
+ style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
2080
+ children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-start", children: [
2083
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
2084
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
2085
+ ] })
2086
+ ]
2087
+ }
2088
+ ),
2089
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2090
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2091
+ "button",
2092
+ {
2093
+ onClick: () => wsInputRef.current?.click(),
2094
+ className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform",
2095
+ style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
2096
+ children: [
2097
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
2098
+ projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
2099
+ ]
2100
+ }
2101
+ ),
2102
+ !projectLoaded && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
2103
+ ] }),
2104
+ onFetchServerProjects && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2105
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2106
+ "button",
2107
+ {
2108
+ disabled: isLoadingFromServer,
2109
+ onClick: async () => {
2110
+ setIsLoadingFromServer(true);
2111
+ try {
2112
+ const projects = await onFetchServerProjects();
2113
+ setServerProjects(projects);
2114
+ if (projects.length > 0 && onServerLoad) {
2115
+ const file = await onServerLoad(projects[0].id);
2116
+ await handleProjectImport(file);
2117
+ }
2118
+ } catch {
2119
+ } finally {
2120
+ setIsLoadingFromServer(false);
2121
+ }
2122
+ },
2123
+ className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform disabled:opacity-50",
2124
+ style: { height: 56, background: "#7c3aed" },
2125
+ children: [
2126
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
2127
+ isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
2128
+ ]
2129
+ }
2130
+ ),
2131
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
2132
+ ] }),
2133
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
2135
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
2136
+ { id: "mobile", icon: "smartphone", label: "Mobile" },
2137
+ { id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
2138
+ { id: "desktop", icon: "desktop_windows", label: "Desktop" },
2139
+ { id: "tablet-landscape", icon: "tablet", label: "Landscape" }
2140
+ ].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2141
+ "button",
2142
+ {
2143
+ onClick: () => startApp(opt.id),
2144
+ className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
2145
+ style: { borderColor: layoutChoice === opt.id ? "rgba(255,255,255,0.35)" : "rgba(255,255,255,0.08)", background: layoutChoice === opt.id ? "rgba(255,255,255,0.07)" : "transparent" },
2146
+ children: [
2147
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
2148
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
2149
+ ]
2150
+ },
2151
+ opt.id
2152
+ )) }),
2153
+ layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
2154
+ layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
2155
+ ] })
2156
+ ] });
2157
+ }
2158
+ if (layoutChoice === "mobile" || layoutChoice === "mobile-desktop") {
2159
+ const mdMode = layoutChoice === "mobile-desktop";
2160
+ const mdScale = mdMode ? window.innerWidth / 430 : 1;
2161
+ const mdW = mdMode ? 430 : void 0;
2162
+ const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
2163
+ const mobileRoot = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
2164
+ width: mdMode ? mdW : "100vw",
2165
+ height: mdMode ? mdH : "100dvh",
2166
+ transform: mdMode ? `scale(${mdScale})` : void 0,
2167
+ transformOrigin: mdMode ? "top left" : void 0,
2168
+ ...hcStyle || {}
2169
+ }, children: [
2170
+ mobileTab === "stage" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
2171
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
2174
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1" }),
2175
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
2176
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
2177
+ ] }),
2178
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
2179
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2180
+ "textarea",
1167
2181
  {
1168
- onClick: () => setLeftTab("prompt"),
1169
- className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`,
2182
+ value: activePrompt,
2183
+ onChange: (e) => setActivePrompt(e.target.value),
2184
+ className: "w-full bg-transparent border-none outline-none text-[14px] leading-relaxed text-white/80 resize-none dark-scrollbar px-4 pt-3 pb-3",
2185
+ style: { height: 80 },
2186
+ placeholder: "Prompt eingeben..."
2187
+ }
2188
+ ),
2189
+ activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-white/20 active:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
2190
+ ] }) }),
2191
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2192
+ "button",
2193
+ {
2194
+ onClick: () => handleGenerateImage(),
2195
+ disabled: !activePrompt.trim() || isGenerating,
2196
+ className: "w-full flex items-center justify-center gap-2 rounded-xl font-bold text-[14px] uppercase tracking-wide transition-all disabled:opacity-30 active:scale-95",
2197
+ style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
2198
+ children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2199
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
2200
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generiere..." })
2201
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2202
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
2203
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generieren" })
2204
+ ] })
2205
+ }
2206
+ ) }),
2207
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
2208
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
2209
+ "div",
2210
+ {
2211
+ className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
2212
+ style: { aspectRatio: "1/1" },
2213
+ onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
2214
+ onTouchEnd: (e) => {
2215
+ if (touchStartX === null) return;
2216
+ const dx = e.changedTouches[0].clientX - touchStartX;
2217
+ if (dx < -50) goToNext();
2218
+ else if (dx > 50) goToPrev();
2219
+ setTouchStartX(null);
2220
+ },
1170
2221
  children: [
1171
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
1172
- "Prompt"
2222
+ currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
2223
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
2224
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
2225
+ ] }),
2226
+ currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
2227
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
2228
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" })
2230
+ ] }),
2231
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
2232
+ !currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
2233
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
2234
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
2235
+ ] }),
2236
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
2237
+ history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2238
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
2239
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
2240
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
2241
+ currentIndex + 1,
2242
+ " / ",
2243
+ history.length
2244
+ ] })
2245
+ ] })
1173
2246
  ]
1174
2247
  }
2248
+ ),
2249
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-2 mt-3", children: [
2250
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
2251
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
2252
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/60", children: "Prompt" })
2253
+ ] }),
2254
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl bg-white/10 active:bg-white/15 transition-colors", style: { height: 44 }, children: [
2255
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
2256
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
2257
+ ] }),
2258
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: handleDownloadSingle, className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
2259
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/60", children: "Laden" })
2261
+ ] })
2262
+ ] })
2263
+ ] })
2264
+ ] }),
2265
+ mobileTab === "browse" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
2266
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setActiveTab(tab), className: `flex-1 flex items-center justify-center gap-1.5 transition-colors text-[11px] font-bold uppercase tracking-wide ${activeTab === tab ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)) }),
2267
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
2268
+ activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
2269
+ setCurrentResult(g);
2270
+ setMobileTab("stage");
2271
+ }, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
2272
+ activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2273
+ MediaLibrary,
2274
+ {
2275
+ items: galleryItems,
2276
+ onImport: async () => {
2277
+ const media = await onSelectMedia();
2278
+ if (!media?.length) return;
2279
+ 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]);
2280
+ },
2281
+ onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
2282
+ onSelect: (g) => {
2283
+ setCurrentResult(g);
2284
+ setMobileTab("stage");
2285
+ },
2286
+ onGenerateReference: (item) => {
2287
+ handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true });
2288
+ setMobileTab("stage");
2289
+ }
2290
+ }
2291
+ ),
2292
+ activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
2293
+ setCurrentResult(g);
2294
+ } })
2295
+ ] })
2296
+ ] }),
2297
+ mobileTab === "tools" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
2298
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
2299
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => {
2300
+ setLeftTab("prompt");
2301
+ if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
2302
+ }, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
2303
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
2304
+ "Prompt"
2305
+ ] }),
2306
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => {
2307
+ setLeftTab("hierarchy");
2308
+ if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
2309
+ }, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
2310
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
2311
+ "Hierarchie"
2312
+ ] }),
2313
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setActiveTab("setup"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "setup" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
2314
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
2315
+ "Setup"
2316
+ ] }),
2317
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setActiveTab("sync"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
2318
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
2319
+ "Sync"
2320
+ ] })
2321
+ ] }),
2322
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
2323
+ leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2324
+ ListView,
2325
+ {
2326
+ nodes,
2327
+ edges,
2328
+ onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
2329
+ onAddChild: (pId) => {
2330
+ const newId = crypto.randomUUID();
2331
+ setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
2332
+ setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
2333
+ setFocusedNodeId(newId);
2334
+ return newId;
2335
+ },
2336
+ onDeleteNode: (id) => {
2337
+ setNodes((nds) => nds.filter((n) => n.id !== id));
2338
+ setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
2339
+ },
2340
+ onFocus: setFocusedNodeId,
2341
+ focusedNodeId,
2342
+ activePath,
2343
+ onGenerate: (id) => {
2344
+ handleSynthesizePrompt(id);
2345
+ setMobileTab("stage");
2346
+ },
2347
+ onGenerateBranch: (id) => {
2348
+ handleSynthesizePrompt(id, true);
2349
+ setMobileTab("stage");
2350
+ },
2351
+ isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2352
+ }
2353
+ ) }),
2354
+ leftTab === "prompt" && workspaceTags && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2355
+ handleGenerateImage(prompt);
2356
+ setMobileTab("stage");
2357
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2358
+ activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2359
+ activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2360
+ ProjectSyncTab,
2361
+ {
2362
+ onProjectExport: handleProjectExport,
2363
+ onProjectImport: (f) => handleProjectImport(f),
2364
+ onWorkspaceImport: handleWorkspaceImport,
2365
+ projectActionState,
2366
+ serverProjects: onFetchServerProjects ? serverProjects : void 0,
2367
+ onServerSave: onServerSave ? handleServerSave : void 0,
2368
+ onServerLoad: onServerLoad ? handleServerLoad : void 0,
2369
+ onServerDelete: onServerDelete ? handleServerDelete : void 0,
2370
+ onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
2371
+ onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
2372
+ onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
2373
+ }
1175
2374
  )
2375
+ ] })
2376
+ ] }),
2377
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
2378
+ { id: "tools", icon: "auto_fix_high", label: "Prompt" },
2379
+ { id: "stage", icon: "palette", label: "Stage" },
2380
+ { id: "browse", icon: "photo_library", label: "Galerie" }
2381
+ ].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
2382
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
2383
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
2384
+ ] }, tab.id)) })
2385
+ ] });
2386
+ if (mdMode) {
2387
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
2388
+ }
2389
+ return mobileRoot;
2390
+ }
2391
+ if (layoutChoice === "tablet-landscape") {
2392
+ const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
2393
+ const tlW = 920;
2394
+ const tlH = 520;
2395
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
2396
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
2397
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { height: 52, borderBottom: "1px solid rgba(255,255,255,0.05)", display: "flex", alignItems: "center", gap: 8, padding: "0 12px", flexShrink: 0 }, children: [
2398
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2399
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
2400
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1 } }),
2401
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
2402
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
2403
+ ] }),
2404
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { position: "relative", borderRadius: 12, border: `1px solid ${isSynthesizing ? "rgba(255,255,255,0.2)" : "rgba(255,255,255,0.1)"}`, background: "rgba(255,255,255,0.05)" }, children: [
2405
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2406
+ "textarea",
2407
+ {
2408
+ value: activePrompt,
2409
+ onChange: (e) => setActivePrompt(e.target.value),
2410
+ style: { width: "100%", background: "transparent", border: "none", outline: "none", fontSize: 12, lineHeight: 1.5, color: "rgba(255,255,255,0.8)", resize: "none", height: 80, padding: "10px 32px 10px 12px", fontFamily: "inherit", boxSizing: "border-box", display: "block" },
2411
+ placeholder: "Prompt eingeben..."
2412
+ }
2413
+ ),
2414
+ activePrompt && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setActivePrompt(""), style: { position: "absolute", top: 6, right: 6, width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
2415
+ ] }) }),
2416
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2417
+ "button",
2418
+ {
2419
+ onClick: () => handleGenerateImage(),
2420
+ disabled: !activePrompt.trim() || isGenerating,
2421
+ style: { width: "100%", height: 42, display: "flex", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 10, fontWeight: "bold", fontSize: 13, textTransform: "uppercase", letterSpacing: "0.05em", border: "1px solid rgba(255,255,255,0.1)", background: activePrompt.trim() && !isGenerating ? "#0284c7" : "transparent", color: "#fff", cursor: activePrompt.trim() && !isGenerating ? "pointer" : "default", opacity: !activePrompt.trim() || isGenerating ? 0.3 : 1, fontFamily: "inherit", transition: "background 0.2s" },
2422
+ children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2423
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
2424
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generiere..." })
2425
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2426
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
2427
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generieren" })
2428
+ ] })
2429
+ }
2430
+ ) }),
2431
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
2432
+ ] }),
2433
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
2434
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2435
+ "div",
2436
+ {
2437
+ style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
2438
+ onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
2439
+ onTouchEnd: (e) => {
2440
+ if (touchStartX === null) return;
2441
+ const dx = e.changedTouches[0].clientX - touchStartX;
2442
+ if (dx < -50) goToNext();
2443
+ else if (dx > 50) goToPrev();
2444
+ setTouchStartX(null);
2445
+ },
2446
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { height: "100%", width: "100%", borderRadius: 20, border: "1px solid rgba(255,255,255,0.05)", background: "rgba(0,0,0,0.4)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
2447
+ currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
2448
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
2449
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
2450
+ ] }),
2451
+ currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
2452
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
2453
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
2454
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" })
2455
+ ] }),
2456
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
2457
+ !currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
2458
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
2459
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
2460
+ ] }),
2461
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
2462
+ history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
2463
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
2465
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
2466
+ currentIndex + 1,
2467
+ " / ",
2468
+ history.length
2469
+ ] })
2470
+ ] })
2471
+ ] })
2472
+ }
2473
+ ),
2474
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
2475
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
2476
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
2477
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Prompt" })
2478
+ ] }),
2479
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "none", background: "rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.8)", fontSize: 11, fontWeight: "bold", cursor: "pointer", fontFamily: "inherit" }, children: [
2480
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
2481
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Referenz" })
2482
+ ] }),
2483
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: handleDownloadSingle, style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
2484
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
2485
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Laden" })
2486
+ ] })
2487
+ ] })
2488
+ ] })
2489
+ ] }) });
2490
+ }
2491
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
2492
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
2493
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : 260, transition: "width 0.2s" }, children: [
2494
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
2495
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 gap-1", children: [
2496
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
2497
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
2498
+ "Prompt"
2499
+ ] }),
2500
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setLeftTab("hierarchy"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
2501
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
2502
+ "Hierarchie"
2503
+ ] })
1176
2504
  ] }),
1177
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
2505
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
1178
2506
  ] }),
1179
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react13.AnimatePresence, { mode: "wait", children: [
1180
- leftTab === "hierarchy" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react13.motion.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2507
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
2508
+ leftTab === "hierarchy" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1181
2509
  ListView,
1182
2510
  {
1183
2511
  nodes,
@@ -1201,125 +2529,105 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1201
2529
  onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
1202
2530
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
1203
2531
  }
1204
- ) }, "hierarchy"),
1205
- leftTab === "prompt" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react13.motion.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1206
- PromptTab,
1207
- {
1208
- workspaceTags,
1209
- onGenerate: handlePromptTabGenerate,
1210
- isGenerating: isPromptTabGenerating,
1211
- feedback: promptFeedback
1212
- }
1213
- ) }, "prompt")
1214
- ] }) })
2532
+ ) }),
2533
+ leftTab === "prompt" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete })
2534
+ ] })
1215
2535
  ] }),
1216
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
1217
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
1218
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
1220
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
2536
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
2537
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
2538
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5", children: [
2539
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2540
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
1221
2541
  ] }),
1222
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2", children: [
1223
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
1224
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
2542
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
2543
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
2544
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
1225
2545
  ] })
1226
2546
  ] }),
1227
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
1228
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react13.AnimatePresence, { children: !isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react13.motion.div, { initial: { height: 0 }, animate: { height: "auto" }, exit: { height: 0 }, className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
1229
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1230
- "textarea",
1231
- {
1232
- value: activePrompt,
1233
- onChange: (e) => setActivePrompt(e.target.value),
1234
- className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar",
1235
- placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
1236
- }
1237
- ),
1238
- activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
1239
- ] }) }) }),
1240
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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: [
1241
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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: [
1242
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
1243
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
2547
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
2548
+ !isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
2549
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("textarea", { value: activePrompt, onChange: (e) => setActivePrompt(e.target.value), className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar", placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..." }),
2550
+ activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2551
+ ] }) }),
2552
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
2553
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
2554
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
2555
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
1244
2556
  ] }),
1245
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
1246
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
1247
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
1248
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
1249
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
1250
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2", children: [
1251
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
2557
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
2558
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
2559
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
2560
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
2561
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
2562
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-2", children: [
2563
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
2564
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
1253
2565
  ] }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
1255
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
1256
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
1257
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("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: [
1258
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
1259
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
1260
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
2566
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
2567
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
2568
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
2569
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
2570
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
2571
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
2572
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
1261
2573
  ] })
1262
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
1263
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
1264
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
2574
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
2575
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
2576
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
1265
2577
  ] })
1266
2578
  ] }) })
1267
2579
  ] })
1268
2580
  ] }),
1269
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react13.motion.div, { animate: { width: isRightCollapsed ? 60 : 320 }, className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", children: [
1270
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
1271
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => {
2581
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : 320, transition: "width 0.2s" }, children: [
2582
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
2583
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
1272
2584
  setActiveTab(tab);
1273
2585
  setIsRightCollapsed(false);
1274
- }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : "settings" }) }, tab)) }),
1275
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
2586
+ }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : "cloud_sync" }) }, tab)) }),
2587
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
1276
2588
  ] }),
1277
- !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react13.AnimatePresence, { mode: "wait", children: [
1278
- activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }, "history"),
1279
- activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2589
+ !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
2590
+ activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
2591
+ activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1280
2592
  MediaLibrary,
1281
2593
  {
1282
2594
  items: galleryItems,
1283
2595
  onImport: async () => {
1284
2596
  const media = await onSelectMedia();
1285
2597
  if (!media?.length) return;
1286
- const newItems = media.map((m) => ({
1287
- id: crypto.randomUUID(),
1288
- nodeId: "1",
1289
- base64: `data:${m.mimeType};base64,${m.base64}`,
1290
- mediaId: m.mediaId,
1291
- prompt: m.name || "Importiert",
1292
- timestamp: Date.now(),
1293
- status: "done",
1294
- tags: [],
1295
- type: "import"
1296
- }));
1297
- setGalleryItems((prev) => [...newItems, ...prev]);
2598
+ 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]);
1298
2599
  },
1299
2600
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
1300
2601
  onSelect: setCurrentResult,
1301
2602
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
1302
- },
1303
- "gallery"
2603
+ }
1304
2604
  ),
1305
- activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1306
- activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1307
- SetupPanel,
2605
+ activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
2606
+ activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2607
+ activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2608
+ ProjectSyncTab,
1308
2609
  {
1309
2610
  onProjectExport: handleProjectExport,
1310
- onProjectImport: handleProjectImport,
2611
+ onProjectImport: (f) => handleProjectImport(f),
1311
2612
  onWorkspaceImport: handleWorkspaceImport,
1312
- projectActionState
1313
- },
1314
- "setup"
2613
+ projectActionState,
2614
+ serverProjects: onFetchServerProjects ? serverProjects : void 0,
2615
+ onServerSave: onServerSave ? handleServerSave : void 0,
2616
+ onServerLoad: onServerLoad ? handleServerLoad : void 0,
2617
+ onServerDelete: onServerDelete ? handleServerDelete : void 0,
2618
+ onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
2619
+ onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
2620
+ onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
2621
+ }
1315
2622
  )
1316
- ] }) })
2623
+ ] })
1317
2624
  ] })
1318
2625
  ] });
1319
2626
  }
1320
2627
  // Annotate the CommonJS export names for ESM import in node:
1321
2628
  0 && (module.exports = {
1322
2629
  AvatarArchitectApp,
2630
+ CollapsibleCard,
1323
2631
  CompactDropdown,
1324
2632
  FaToolsBadge,
1325
2633
  GLOBAL_STYLES,
@@ -1328,6 +2636,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1328
2636
  ListView,
1329
2637
  MediaLibrary,
1330
2638
  PillButton,
2639
+ ProjectSyncTab,
1331
2640
  PromptTab,
1332
2641
  SectionLabel,
1333
2642
  SetupPanel,