@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.mjs CHANGED
@@ -197,7 +197,7 @@ function generateMarkdownReport(nodes, history) {
197
197
  });
198
198
  return md;
199
199
  }
200
- async function exportProjectToZip(nodes, edges, history, galleryItems, settings) {
200
+ async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags) {
201
201
  const zip = new JSZip();
202
202
  const projectData = {
203
203
  nodes,
@@ -205,6 +205,7 @@ async function exportProjectToZip(nodes, edges, history, galleryItems, settings)
205
205
  history: history.map((h) => ({ ...h, base64: void 0 })),
206
206
  galleryItems: galleryItems.map((g) => ({ ...g, base64: void 0 })),
207
207
  settings,
208
+ workspaceTags: workspaceTags || null,
208
209
  version: "1.2.6"
209
210
  };
210
211
  zip.file("project.json", JSON.stringify(projectData, null, 2));
@@ -293,14 +294,44 @@ function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaI
293
294
  }
294
295
  return options;
295
296
  }
296
- function buildPromptTabPayload(selectedValues, instructions, treeText) {
297
+ var CONTENT_FIRST = ["subject", "clothing", "environment", "mood", "action", "content", "general"];
298
+ var RULES_LAST = ["system", "rules", "safety", "generated", "source", "status", "type", "model"];
299
+ function categoryZone(cat) {
300
+ const c = cat.toLowerCase();
301
+ if (CONTENT_FIRST.some((k) => c.includes(k))) return 0;
302
+ if (RULES_LAST.some((k) => c.includes(k))) return 2;
303
+ return 1;
304
+ }
305
+ var ZONE_SEPARATORS = { 1: "\u2500\u2500\u2500\u2500 STYLE \u2500\u2500\u2500\u2500", 2: "\u2500\u2500\u2500\u2500 SYSTEM / RULES \u2500\u2500\u2500\u2500" };
306
+ function buildPromptTabPayload(selectedTags, instructions, rules, treeText) {
297
307
  const parts = [];
298
308
  if (treeText) parts.push(`HIERARCHY:
299
309
  ${treeText}`);
300
- if (selectedValues.length > 0) parts.push(`SELECTED TAGS:
301
- ${selectedValues.join(", ")}`);
302
- if (instructions.trim()) parts.push(`ADDITIONAL INSTRUCTIONS:
310
+ if (selectedTags.length > 0) {
311
+ const sorted = [...selectedTags].sort((a, b) => categoryZone(a.category) - categoryZone(b.category) || a.category.localeCompare(b.category));
312
+ const byCategory = sorted.reduce((acc, t) => {
313
+ (acc[t.category] = acc[t.category] || []).push(t);
314
+ return acc;
315
+ }, {});
316
+ const tagParts = [];
317
+ let lastZone = -1;
318
+ for (const [cat, tags] of Object.entries(byCategory)) {
319
+ const zone = categoryZone(cat);
320
+ if (lastZone >= 0 && zone > lastZone && ZONE_SEPARATORS[zone]) {
321
+ tagParts.push(ZONE_SEPARATORS[zone]);
322
+ }
323
+ tagParts.push(...tags.map((t) => `[${cat.toUpperCase()}]
324
+ ${t.value}`));
325
+ lastZone = zone;
326
+ }
327
+ parts.push(`SELECTED TAGS:
328
+
329
+ ${tagParts.join("\n\n")}`);
330
+ }
331
+ if (instructions.trim()) parts.push(`DESCRIPTION:
303
332
  ${instructions.trim()}`);
333
+ if (rules.trim()) parts.push(`GENERATION RULES:
334
+ ${rules.trim()}`);
304
335
  return [
305
336
  "Create a high-quality image generation prompt based on the following inputs. Translate everything to English.",
306
337
  `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).`,
@@ -529,9 +560,11 @@ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
529
560
  };
530
561
 
531
562
  // src/components/InspectPanel.tsx
563
+ import { useMemo } from "react";
532
564
  import { motion as motion2 } from "motion/react";
533
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
565
+ import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
534
566
  var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagToggle }) => {
567
+ const currentIndex = useMemo(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
535
568
  if (!currentResult) {
536
569
  return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
537
570
  /* @__PURE__ */ jsx6("span", { className: "material-symbols-outlined text-[64px]", children: "info" }),
@@ -543,7 +576,18 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
543
576
  /* @__PURE__ */ jsx6("div", { className: "flex-shrink-0 border-b border-white/5 bg-black/20 py-3 px-4", children: /* @__PURE__ */ jsx6("div", { className: "flex gap-2 overflow-x-auto no-scrollbar pb-1", children: history.map((gen) => /* @__PURE__ */ jsx6("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__ */ jsx6("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)) }) }),
544
577
  /* @__PURE__ */ jsx6("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-6 dark-scrollbar pb-10", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-4", children: [
545
578
  /* @__PURE__ */ jsx6(SectionLabel, { children: "Vorschau" }),
546
- /* @__PURE__ */ jsx6("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl", children: /* @__PURE__ */ jsx6("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" }) }),
579
+ /* @__PURE__ */ jsxs4("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl relative", children: [
580
+ /* @__PURE__ */ jsx6("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" }),
581
+ history.length > 1 && /* @__PURE__ */ jsxs4(Fragment, { children: [
582
+ /* @__PURE__ */ jsx6("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__ */ jsx6("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
583
+ /* @__PURE__ */ jsx6("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__ */ jsx6("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
584
+ /* @__PURE__ */ jsxs4("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: [
585
+ currentIndex + 1,
586
+ " / ",
587
+ history.length
588
+ ] })
589
+ ] })
590
+ ] }),
547
591
  /* @__PURE__ */ jsxs4("div", { className: "p-4 bg-white/5 rounded-2xl border border-white/5", children: [
548
592
  /* @__PURE__ */ jsx6("p", { className: "text-[8px] font-bold text-white/20 uppercase mb-2 tracking-widest", children: "Prompt Details" }),
549
593
  /* @__PURE__ */ jsxs4("p", { className: "text-[10px] text-white/70 italic leading-relaxed font-medium", children: [
@@ -584,9 +628,8 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
584
628
  import { useRef as useRef2 } from "react";
585
629
  import { motion as motion3 } from "motion/react";
586
630
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
587
- var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, projectActionState }) => {
631
+ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
588
632
  const workspaceInputRef = useRef2(null);
589
- const projectInputRef = useRef2(null);
590
633
  return /* @__PURE__ */ jsxs5(motion3.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
591
634
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
592
635
  /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
@@ -600,30 +643,15 @@ var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, project
600
643
  e.target.value = "";
601
644
  } })
602
645
  ] }),
603
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
604
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
605
- /* @__PURE__ */ jsx7(SectionLabel, { children: "Projekt Management" }),
606
- /* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Speichere den gesamten Baum und alle Assets." })
607
- ] }),
608
- /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
609
- /* @__PURE__ */ jsx7(PillButton, { variant: "filled", icon: "download", onClick: onProjectExport, loading: projectActionState === "working", children: "Exportieren (.zip)" }),
610
- /* @__PURE__ */ jsx7(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "Projekt laden (.zip)" }),
611
- /* @__PURE__ */ jsx7("input", { ref: projectInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
612
- const f = e.target.files?.[0];
613
- if (f) onProjectImport(f);
614
- e.target.value = "";
615
- } })
616
- ] })
617
- ] }),
618
646
  /* @__PURE__ */ jsxs5("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
619
647
  /* @__PURE__ */ jsx7("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
620
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
621
- /* @__PURE__ */ jsx7("span", { children: "Version" }),
622
- /* @__PURE__ */ jsx7("span", { className: "font-mono", children: "1.2.0" })
623
- ] }),
624
648
  /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
625
649
  /* @__PURE__ */ jsx7("span", { children: "Engine" }),
626
650
  /* @__PURE__ */ jsx7("span", { className: "font-mono", children: "Avatar Architect" })
651
+ ] }),
652
+ buildInfo && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
653
+ /* @__PURE__ */ jsx7("span", { children: "Build" }),
654
+ /* @__PURE__ */ jsx7("span", { className: "font-mono", children: buildInfo })
627
655
  ] })
628
656
  ] })
629
657
  ] });
@@ -784,17 +812,84 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
784
812
  }
785
813
 
786
814
  // src/components/AvatarArchitectApp.tsx
787
- import { useState as useState4, useCallback, useMemo, useEffect as useEffect4 } from "react";
788
- import { motion as motion6, AnimatePresence as AnimatePresence2 } from "motion/react";
815
+ import { useState as useState6, useCallback, useMemo as useMemo2, useEffect as useEffect4, useRef as useRef6 } from "react";
789
816
 
790
817
  // src/components/PromptTab.tsx
818
+ import { useRef as useRef4, useState as useState4 } from "react";
819
+
820
+ // src/components/CollapsibleCard.tsx
791
821
  import { useState as useState3 } from "react";
792
- import { motion as motion5, AnimatePresence } from "motion/react";
793
822
  import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
794
- var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
795
- const [selectedLabels, setSelectedLabels] = useState3(/* @__PURE__ */ new Set());
796
- const [instructions, setInstructions] = useState3("");
797
- const [showFeedback, setShowFeedback] = useState3(true);
823
+ var CollapsibleCard = ({
824
+ title,
825
+ icon,
826
+ actions,
827
+ children,
828
+ defaultOpen = true,
829
+ collapsible = true,
830
+ className = ""
831
+ }) => {
832
+ const [isOpen, setIsOpen] = useState3(defaultOpen);
833
+ return /* @__PURE__ */ jsxs8("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
834
+ /* @__PURE__ */ jsxs8(
835
+ "div",
836
+ {
837
+ className: `flex items-center justify-between px-4 py-3 bg-neutral-900/50 ${collapsible ? "cursor-pointer active:bg-neutral-800/70 select-none" : ""}`,
838
+ onClick: () => collapsible && setIsOpen((o) => !o),
839
+ children: [
840
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
841
+ icon && /* @__PURE__ */ jsx10("span", { className: "text-neutral-400", children: icon }),
842
+ /* @__PURE__ */ jsx10("span", { className: "text-sm font-bold uppercase tracking-widest text-neutral-300", children: title })
843
+ ] }),
844
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
845
+ actions && /* @__PURE__ */ jsx10("div", { onClick: (e) => e.stopPropagation(), children: actions }),
846
+ collapsible && /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[16px] text-neutral-500", children: isOpen ? "expand_less" : "expand_more" })
847
+ ] })
848
+ ]
849
+ }
850
+ ),
851
+ isOpen && /* @__PURE__ */ jsx10("div", { children })
852
+ ] });
853
+ };
854
+
855
+ // src/components/PromptTab.tsx
856
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
857
+ var EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
858
+ var LONG_PRESS_MS = 500;
859
+ var PromptTab = ({
860
+ workspaceTags,
861
+ onGenerate,
862
+ isGenerating,
863
+ feedback,
864
+ promptResult,
865
+ lastPayload,
866
+ onGenerateImage,
867
+ isGeneratingImage,
868
+ onRandom,
869
+ onTokenize,
870
+ isTokenizing,
871
+ onScanImage,
872
+ isScanning,
873
+ onExpand,
874
+ isExpanding,
875
+ onTagCreate,
876
+ onTagUpdate,
877
+ onTagDelete
878
+ }) => {
879
+ const [selectedLabels, setSelectedLabels] = useState4(/* @__PURE__ */ new Set());
880
+ const [instructions, setInstructions] = useState4("");
881
+ const [rules, setRules] = useState4("");
882
+ const [activeCategory, setActiveCategory] = useState4(null);
883
+ const [copied, setCopied] = useState4(false);
884
+ const imgInputRef = useRef4(null);
885
+ const [addingInCat, setAddingInCat] = useState4(null);
886
+ const [newLabel, setNewLabel] = useState4("");
887
+ const [newValue, setNewValue] = useState4("");
888
+ const [editingTag, setEditingTag] = useState4(null);
889
+ const [editLabel, setEditLabel] = useState4("");
890
+ const [editValue, setEditValue] = useState4("");
891
+ const longPressTimer = useRef4(null);
892
+ const longPressActivated = useRef4(false);
798
893
  const toggleTag = (label) => {
799
894
  setSelectedLabels((prev) => {
800
895
  const next = new Set(prev);
@@ -804,96 +899,670 @@ var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
804
899
  });
805
900
  };
806
901
  const handleGenerate = () => {
807
- const selectedValues = workspaceTags.all.filter((t) => selectedLabels.has(t.label)).map((t) => t.value);
808
- onGenerate(selectedValues, instructions);
902
+ const selectedTags = workspaceTags.all.filter((t) => selectedLabels.has(t.label) && !t.is_deleted).map((t) => ({ label: t.label, value: t.value, category: t.category }));
903
+ onGenerate(selectedTags, instructions, rules);
904
+ };
905
+ const handleCopy = () => {
906
+ if (!promptResult) return;
907
+ navigator.clipboard.writeText(promptResult).then(() => {
908
+ setCopied(true);
909
+ setTimeout(() => setCopied(false), 2e3);
910
+ });
911
+ };
912
+ const startLongPress = (tag, cat) => {
913
+ longPressActivated.current = false;
914
+ longPressTimer.current = setTimeout(() => {
915
+ longPressActivated.current = true;
916
+ setEditingTag({ ...tag, category: cat });
917
+ setEditLabel(tag.label);
918
+ setEditValue(tag.value);
919
+ }, LONG_PRESS_MS);
920
+ };
921
+ const cancelLongPress = () => {
922
+ if (longPressTimer.current) {
923
+ clearTimeout(longPressTimer.current);
924
+ longPressTimer.current = null;
925
+ }
926
+ };
927
+ const handleTagClick = (label) => {
928
+ if (longPressActivated.current) return;
929
+ toggleTag(label);
930
+ };
931
+ const handleSaveEdit = () => {
932
+ if (!editingTag || !editLabel.trim()) return;
933
+ onTagUpdate?.(editingTag.label, editingTag.category, {
934
+ label: editLabel.trim(),
935
+ value: editValue.trim() || editLabel.trim()
936
+ });
937
+ setSelectedLabels((prev) => {
938
+ const next = new Set(prev);
939
+ if (next.has(editingTag.label)) {
940
+ next.delete(editingTag.label);
941
+ next.add(editLabel.trim());
942
+ }
943
+ return next;
944
+ });
945
+ setEditingTag(null);
809
946
  };
810
- const categories = Object.entries(workspaceTags.by_category).filter(([, tags]) => tags.length > 0);
811
- return /* @__PURE__ */ jsxs8("div", { className: "absolute inset-0 flex flex-col overflow-hidden", children: [
812
- /* @__PURE__ */ jsxs8("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 py-3 flex flex-col gap-4", children: [
813
- categories.length === 0 ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
814
- /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
815
- /* @__PURE__ */ jsxs8("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
947
+ const handleDeleteTag = () => {
948
+ if (!editingTag) return;
949
+ onTagDelete?.(editingTag.label, editingTag.category);
950
+ setSelectedLabels((prev) => {
951
+ const next = new Set(prev);
952
+ next.delete(editingTag.label);
953
+ return next;
954
+ });
955
+ setEditingTag(null);
956
+ };
957
+ const handleCreateBased = () => {
958
+ if (!editingTag) return;
959
+ setAddingInCat(editingTag.category);
960
+ setActiveCategory(editingTag.category);
961
+ setNewLabel(editingTag.label);
962
+ setNewValue(editingTag.value);
963
+ setEditingTag(null);
964
+ };
965
+ const categories = Object.entries(workspaceTags.by_category).filter(([cat, tags]) => !EXCLUDED.includes(cat) && tags.some((t) => !t.is_deleted));
966
+ const tagToolbar = /* @__PURE__ */ jsxs9("div", { className: "flex gap-1.5", children: [
967
+ /* @__PURE__ */ jsx11(
968
+ "button",
969
+ {
970
+ onClick: onRandom,
971
+ disabled: !onRandom,
972
+ 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",
973
+ children: "RANDOM"
974
+ }
975
+ ),
976
+ /* @__PURE__ */ jsx11(
977
+ "button",
978
+ {
979
+ onClick: onTokenize,
980
+ disabled: !onTokenize || isTokenizing || !instructions,
981
+ 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",
982
+ children: isTokenizing ? "..." : "TOKENIZE"
983
+ }
984
+ ),
985
+ /* @__PURE__ */ jsx11(
986
+ "button",
987
+ {
988
+ onClick: () => imgInputRef.current?.click(),
989
+ disabled: !onScanImage || isScanning,
990
+ 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",
991
+ children: isScanning ? "..." : "SCAN"
992
+ }
993
+ ),
994
+ /* @__PURE__ */ jsx11(
995
+ "input",
996
+ {
997
+ ref: imgInputRef,
998
+ type: "file",
999
+ accept: "image/*",
1000
+ className: "hidden",
1001
+ onChange: (e) => {
1002
+ if (e.target.files?.[0] && onScanImage) onScanImage(e.target.files[0]);
1003
+ }
1004
+ }
1005
+ ),
1006
+ /* @__PURE__ */ jsx11(
1007
+ "button",
1008
+ {
1009
+ onClick: onExpand,
1010
+ disabled: !onExpand || isExpanding || !instructions,
1011
+ 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",
1012
+ children: isExpanding ? "..." : "EXPAND"
1013
+ }
1014
+ )
1015
+ ] });
1016
+ const canGenerate = selectedLabels.size > 0 || instructions.trim().length > 0 || rules.trim().length > 0;
1017
+ return /* @__PURE__ */ jsxs9("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar flex flex-col", children: [
1018
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3 p-3 pb-6", children: [
1019
+ categories.length === 0 ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
1020
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
1021
+ /* @__PURE__ */ jsxs9("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
816
1022
  "Keine Tags geladen.",
817
- /* @__PURE__ */ jsx10("br", {}),
1023
+ /* @__PURE__ */ jsx11("br", {}),
818
1024
  "Importiere ein Workspace-JSON im Setup-Tab."
819
1025
  ] })
820
- ] }) : categories.map(([cat, tags]) => /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
821
- /* @__PURE__ */ jsx10(SectionLabel, { children: cat }),
822
- /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-1", children: tags.map((t) => {
823
- const active = selectedLabels.has(t.label);
824
- return /* @__PURE__ */ jsx10(
825
- "button",
1026
+ ] }) : /* @__PURE__ */ jsx11(
1027
+ CollapsibleCard,
1028
+ {
1029
+ title: "INHALT",
1030
+ icon: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "layers" }),
1031
+ defaultOpen: true,
1032
+ children: categories.map(([cat, tags]) => {
1033
+ const visibleTags = tags.filter((t) => !t.is_deleted);
1034
+ const selectedInCat = visibleTags.filter((t) => selectedLabels.has(t.label));
1035
+ const isOpen = activeCategory === cat;
1036
+ const isAdding = addingInCat === cat;
1037
+ const commitNewTag = () => {
1038
+ if (!newLabel.trim()) return;
1039
+ const valueToUse = newValue.trim() || newLabel.trim();
1040
+ onTagCreate?.({ label: newLabel.trim(), value: valueToUse, category: cat, is_user_created: true });
1041
+ setNewLabel("");
1042
+ setNewValue("");
1043
+ setAddingInCat(null);
1044
+ };
1045
+ return /* @__PURE__ */ jsxs9("div", { className: "border-b border-neutral-800 last:border-0", children: [
1046
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center", children: [
1047
+ /* @__PURE__ */ jsxs9(
1048
+ "button",
1049
+ {
1050
+ onClick: () => setActiveCategory(isOpen ? null : cat),
1051
+ className: "flex-grow flex items-center justify-between px-4 py-3 active:bg-white/5",
1052
+ children: [
1053
+ /* @__PURE__ */ jsx11("span", { className: "text-sm font-bold tracking-widest uppercase text-blue-400", children: cat }),
1054
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2 min-w-0", children: [
1055
+ selectedInCat.length > 0 && /* @__PURE__ */ jsx11("span", { className: "text-[10px] text-green-400 truncate max-w-[150px]", children: selectedInCat.map((t) => t.label).join(", ") }),
1056
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[16px] text-white/30 shrink-0", children: isOpen ? "expand_less" : "expand_more" })
1057
+ ] })
1058
+ ]
1059
+ }
1060
+ ),
1061
+ onTagCreate && /* @__PURE__ */ jsx11(
1062
+ "button",
1063
+ {
1064
+ onClick: (e) => {
1065
+ e.stopPropagation();
1066
+ setAddingInCat(isAdding ? null : cat);
1067
+ setActiveCategory(cat);
1068
+ setNewLabel("");
1069
+ setNewValue("");
1070
+ },
1071
+ className: "px-3 py-3 text-white/20 active:text-white/60 hover:text-white/50 transition-colors",
1072
+ title: "Neues Tag anlegen",
1073
+ children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[16px]", children: "add" })
1074
+ }
1075
+ )
1076
+ ] }),
1077
+ isOpen && /* @__PURE__ */ jsx11("div", { className: "px-4 pb-3 flex flex-wrap gap-1.5", children: visibleTags.map((t) => {
1078
+ const active = selectedLabels.has(t.label);
1079
+ return /* @__PURE__ */ jsx11(
1080
+ "button",
1081
+ {
1082
+ onMouseDown: () => startLongPress(t, cat),
1083
+ onMouseUp: cancelLongPress,
1084
+ onMouseLeave: cancelLongPress,
1085
+ onTouchStart: () => startLongPress(t, cat),
1086
+ onTouchEnd: cancelLongPress,
1087
+ onTouchCancel: cancelLongPress,
1088
+ onContextMenu: (e) => e.preventDefault(),
1089
+ onClick: () => handleTagClick(t.label),
1090
+ 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" : ""}`,
1091
+ children: t.label
1092
+ },
1093
+ t.label
1094
+ );
1095
+ }) }),
1096
+ isAdding && /* @__PURE__ */ jsxs9("div", { className: "px-4 pb-3 space-y-2 border-t border-neutral-800/50 pt-2", children: [
1097
+ /* @__PURE__ */ jsx11(
1098
+ "input",
1099
+ {
1100
+ autoFocus: true,
1101
+ value: newLabel,
1102
+ onChange: (e) => setNewLabel(e.target.value),
1103
+ onKeyDown: (e) => {
1104
+ if (e.key === "Enter") {
1105
+ e.preventDefault();
1106
+ commitNewTag();
1107
+ }
1108
+ },
1109
+ placeholder: "Label (z.B. Picasso)",
1110
+ 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"
1111
+ }
1112
+ ),
1113
+ /* @__PURE__ */ jsx11(
1114
+ "textarea",
1115
+ {
1116
+ value: newValue,
1117
+ onChange: (e) => setNewValue(e.target.value),
1118
+ placeholder: "Value (Prompt-Text) \u2014 leer l\xE4sst Label als Value",
1119
+ rows: 3,
1120
+ 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"
1121
+ }
1122
+ ),
1123
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-2", children: [
1124
+ /* @__PURE__ */ jsxs9(
1125
+ "button",
1126
+ {
1127
+ onClick: commitNewTag,
1128
+ disabled: !newLabel.trim(),
1129
+ 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",
1130
+ children: [
1131
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "check" }),
1132
+ " Speichern"
1133
+ ]
1134
+ }
1135
+ ),
1136
+ /* @__PURE__ */ jsx11(
1137
+ "button",
1138
+ {
1139
+ onClick: () => {
1140
+ setAddingInCat(null);
1141
+ setNewLabel("");
1142
+ setNewValue("");
1143
+ },
1144
+ 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",
1145
+ children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "close" })
1146
+ }
1147
+ )
1148
+ ] })
1149
+ ] })
1150
+ ] }, cat);
1151
+ })
1152
+ }
1153
+ ),
1154
+ /* @__PURE__ */ jsx11(
1155
+ CollapsibleCard,
1156
+ {
1157
+ title: "BESCHREIBUNG",
1158
+ icon: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "edit_note" }),
1159
+ actions: tagToolbar,
1160
+ defaultOpen: true,
1161
+ children: /* @__PURE__ */ jsx11("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx11(
1162
+ "textarea",
826
1163
  {
827
- onClick: () => toggleTag(t.label),
828
- 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"}`,
829
- children: t.label
830
- },
831
- t.label
832
- );
833
- }) })
834
- ] }, cat)),
835
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2 mt-2", children: [
836
- /* @__PURE__ */ jsx10(SectionLabel, { children: "Zus\xE4tzliche Anweisungen" }),
837
- /* @__PURE__ */ jsx10(
838
- "textarea",
1164
+ value: instructions,
1165
+ onChange: (e) => setInstructions(e.target.value),
1166
+ placeholder: "...",
1167
+ 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",
1168
+ style: { minHeight: 80 }
1169
+ }
1170
+ ) })
1171
+ }
1172
+ ),
1173
+ /* @__PURE__ */ jsx11(
1174
+ CollapsibleCard,
1175
+ {
1176
+ title: "REGELN",
1177
+ icon: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "rule" }),
1178
+ defaultOpen: false,
1179
+ children: /* @__PURE__ */ jsx11("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx11(
1180
+ "textarea",
1181
+ {
1182
+ value: rules,
1183
+ onChange: (e) => setRules(e.target.value),
1184
+ placeholder: "z.B. Verwende immer explizite Nacktheit unter transparentem Stoff. Tags in Flie\xDFtext einbauen. Kein Markdown.",
1185
+ 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",
1186
+ style: { minHeight: 80 }
1187
+ }
1188
+ ) })
1189
+ }
1190
+ ),
1191
+ selectedLabels.size > 0 && /* @__PURE__ */ jsxs9("button", { onClick: () => setSelectedLabels(/* @__PURE__ */ new Set()), className: "flex items-center gap-1 text-[10px] text-white/30 active:text-white/60 self-start", children: [
1192
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "close" }),
1193
+ selectedLabels.size,
1194
+ " Tags gew\xE4hlt"
1195
+ ] }),
1196
+ /* @__PURE__ */ jsx11(
1197
+ "button",
1198
+ {
1199
+ onClick: handleGenerate,
1200
+ disabled: isGenerating || !canGenerate,
1201
+ 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",
1202
+ children: isGenerating ? /* @__PURE__ */ jsxs9(Fragment2, { children: [
1203
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px] animate-spin", children: "autorenew" }),
1204
+ "BAUT PROMPT..."
1205
+ ] }) : /* @__PURE__ */ jsxs9(Fragment2, { children: [
1206
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "auto_fix_high" }),
1207
+ "PROMPT BAUEN"
1208
+ ] })
1209
+ }
1210
+ ),
1211
+ promptResult && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1212
+ lastPayload && /* @__PURE__ */ jsx11(
1213
+ CollapsibleCard,
839
1214
  {
840
- value: instructions,
841
- onChange: (e) => setInstructions(e.target.value),
842
- placeholder: "Weitere Vorgaben f\xFCr den AI-Prompt...",
843
- 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"
1215
+ title: "INPUT MONITOR",
1216
+ icon: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "terminal" }),
1217
+ defaultOpen: false,
1218
+ children: /* @__PURE__ */ jsx11("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx11("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 }) })
1219
+ }
1220
+ ),
1221
+ feedback && /* @__PURE__ */ jsx11(
1222
+ CollapsibleCard,
1223
+ {
1224
+ title: "ANALYSE",
1225
+ icon: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: "chat" }),
1226
+ defaultOpen: true,
1227
+ children: /* @__PURE__ */ jsx11("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx11("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 }) })
1228
+ }
1229
+ ),
1230
+ /* @__PURE__ */ jsxs9("div", { className: "border border-neutral-800 rounded-lg", children: [
1231
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between px-4 py-3 bg-neutral-900/50 rounded-t-lg", children: [
1232
+ /* @__PURE__ */ jsx11("span", { className: "text-[10px] font-bold uppercase tracking-widest text-neutral-400", children: "Prompt" }),
1233
+ /* @__PURE__ */ jsxs9(
1234
+ "button",
1235
+ {
1236
+ onClick: handleCopy,
1237
+ 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",
1238
+ children: [
1239
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: copied ? "check" : "content_copy" }),
1240
+ copied ? "KOPIERT" : "KOPIEREN"
1241
+ ]
1242
+ }
1243
+ )
1244
+ ] }),
1245
+ /* @__PURE__ */ jsx11("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx11("p", { className: "text-xs text-white/80 leading-relaxed italic", children: promptResult }) })
1246
+ ] }),
1247
+ onGenerateImage && /* @__PURE__ */ jsxs9(
1248
+ "button",
1249
+ {
1250
+ onClick: () => onGenerateImage(promptResult),
1251
+ disabled: isGeneratingImage,
1252
+ 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",
1253
+ children: [
1254
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: isGeneratingImage ? "hourglass_empty" : "image" }),
1255
+ isGeneratingImage ? "GENERIERE..." : "GENERIEREN"
1256
+ ]
844
1257
  }
845
1258
  )
846
1259
  ] })
847
1260
  ] }),
848
- /* @__PURE__ */ jsxs8("div", { className: "shrink-0 border-t border-white/5 p-3 flex flex-col gap-2", children: [
849
- feedback && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col", children: [
850
- /* @__PURE__ */ jsxs8(
851
- "button",
1261
+ editingTag && /* @__PURE__ */ jsx11(
1262
+ "div",
1263
+ {
1264
+ className: "fixed inset-0 z-50 flex items-end justify-center",
1265
+ style: { background: "rgba(0,0,0,0.75)" },
1266
+ onClick: () => setEditingTag(null),
1267
+ children: /* @__PURE__ */ jsxs9(
1268
+ "div",
852
1269
  {
853
- onClick: () => setShowFeedback((p) => !p),
854
- className: "flex items-center gap-1.5 text-[8px] font-bold uppercase text-amber-400/60 hover:text-amber-400 transition-colors mb-1",
1270
+ className: "w-full max-w-lg bg-[#1c1c1c] border border-neutral-700 rounded-t-2xl p-5 space-y-4",
1271
+ onClick: (e) => e.stopPropagation(),
855
1272
  children: [
856
- /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[12px]", children: "psychology" }),
857
- "KI-Analyse",
858
- /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[12px] ml-auto", children: showFeedback ? "expand_less" : "expand_more" })
1273
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between", children: [
1274
+ /* @__PURE__ */ jsx11("span", { className: "text-[10px] font-bold text-neutral-400 uppercase tracking-widest", children: editingTag.category }),
1275
+ /* @__PURE__ */ jsx11("button", { onClick: () => setEditingTag(null), className: "text-white/30 active:text-white", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
1276
+ ] }),
1277
+ /* @__PURE__ */ jsx11(
1278
+ "input",
1279
+ {
1280
+ autoFocus: true,
1281
+ value: editLabel,
1282
+ onChange: (e) => setEditLabel(e.target.value),
1283
+ placeholder: "Label",
1284
+ className: "w-full bg-black border border-neutral-700 rounded px-3 py-2 text-sm text-white outline-none focus:border-blue-600"
1285
+ }
1286
+ ),
1287
+ /* @__PURE__ */ jsx11(
1288
+ "textarea",
1289
+ {
1290
+ value: editValue,
1291
+ onChange: (e) => setEditValue(e.target.value),
1292
+ placeholder: "Value (Prompt-Text)",
1293
+ rows: 4,
1294
+ 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"
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsxs9("div", { className: "flex gap-2 flex-wrap", children: [
1298
+ /* @__PURE__ */ jsxs9(
1299
+ "button",
1300
+ {
1301
+ onClick: handleSaveEdit,
1302
+ disabled: !editLabel.trim(),
1303
+ 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",
1304
+ children: [
1305
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[16px]", children: "check" }),
1306
+ " Speichern"
1307
+ ]
1308
+ }
1309
+ ),
1310
+ /* @__PURE__ */ jsxs9(
1311
+ "button",
1312
+ {
1313
+ onClick: handleCreateBased,
1314
+ 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",
1315
+ children: [
1316
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[16px]", children: "content_copy" }),
1317
+ " Neu auf Basis"
1318
+ ]
1319
+ }
1320
+ ),
1321
+ onTagDelete && /* @__PURE__ */ jsx11(
1322
+ "button",
1323
+ {
1324
+ onClick: handleDeleteTag,
1325
+ 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",
1326
+ children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[16px]", children: "delete" })
1327
+ }
1328
+ )
1329
+ ] })
859
1330
  ]
860
1331
  }
1332
+ )
1333
+ }
1334
+ )
1335
+ ] });
1336
+ };
1337
+
1338
+ // src/components/ProjectSyncTab.tsx
1339
+ import { useRef as useRef5, useState as useState5 } from "react";
1340
+ import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1341
+ var ProjectSyncTab = ({
1342
+ onProjectExport,
1343
+ onProjectImport,
1344
+ onWorkspaceImport,
1345
+ projectActionState,
1346
+ serverProjects,
1347
+ onServerSave,
1348
+ onServerLoad,
1349
+ onServerDelete,
1350
+ onRefreshServerProjects,
1351
+ onComputeSyncDiff,
1352
+ onExecuteSync
1353
+ }) => {
1354
+ const projectInputRef = useRef5(null);
1355
+ const workspaceInputRef = useRef5(null);
1356
+ const [saveName, setSaveName] = useState5("");
1357
+ const [isSaving, setIsSaving] = useState5(false);
1358
+ const [isExporting, setIsExporting] = useState5(false);
1359
+ const [syncState, setSyncState] = useState5("idle");
1360
+ const [syncDiff, setSyncDiff] = useState5(null);
1361
+ const [selectedLocalIds, setSelectedLocalIds] = useState5(/* @__PURE__ */ new Set());
1362
+ const handleExport = async () => {
1363
+ if (!onProjectExport) return;
1364
+ setIsExporting(true);
1365
+ try {
1366
+ await onProjectExport();
1367
+ } finally {
1368
+ setIsExporting(false);
1369
+ }
1370
+ };
1371
+ const handleComputeDiff = async () => {
1372
+ if (!onComputeSyncDiff) return;
1373
+ setSyncState("computing");
1374
+ setSyncDiff(null);
1375
+ try {
1376
+ const diff = await onComputeSyncDiff();
1377
+ setSyncDiff(diff);
1378
+ setSelectedLocalIds(new Set(diff.localOnly.map((g) => g.id)));
1379
+ setSyncState("ready");
1380
+ } catch {
1381
+ setSyncState("error");
1382
+ setTimeout(() => setSyncState("idle"), 4e3);
1383
+ }
1384
+ };
1385
+ const handleExecuteSync = async () => {
1386
+ if (!onExecuteSync || !syncDiff) return;
1387
+ setSyncState("executing");
1388
+ try {
1389
+ await onExecuteSync([...selectedLocalIds]);
1390
+ setSyncState("done");
1391
+ setSyncDiff(null);
1392
+ setTimeout(() => setSyncState("idle"), 3e3);
1393
+ } catch {
1394
+ setSyncState("error");
1395
+ setTimeout(() => setSyncState("idle"), 4e3);
1396
+ }
1397
+ };
1398
+ const toggleLocalId = (id) => {
1399
+ setSelectedLocalIds((prev) => {
1400
+ const next = new Set(prev);
1401
+ if (next.has(id)) next.delete(id);
1402
+ else next.add(id);
1403
+ return next;
1404
+ });
1405
+ };
1406
+ const isWorking = projectActionState === "working" || projectActionState === "working-full";
1407
+ return /* @__PURE__ */ jsx12("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar", children: /* @__PURE__ */ jsxs10("div", { className: "p-6 flex flex-col gap-8", children: [
1408
+ (onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-4", children: [
1409
+ /* @__PURE__ */ jsx12(SectionLabel, { children: "Projekt-ZIP" }),
1410
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1411
+ onProjectExport && /* @__PURE__ */ jsx12(PillButton, { variant: "filled", icon: "download", loading: isExporting || isWorking, onClick: handleExport, children: "Exportieren (.zip)" }),
1412
+ onProjectImport && /* @__PURE__ */ jsxs10(Fragment3, { children: [
1413
+ /* @__PURE__ */ jsx12(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "ZIP laden" }),
1414
+ /* @__PURE__ */ jsx12(
1415
+ "input",
1416
+ {
1417
+ ref: projectInputRef,
1418
+ type: "file",
1419
+ accept: ".zip",
1420
+ className: "hidden",
1421
+ onChange: (e) => {
1422
+ const f = e.target.files?.[0];
1423
+ if (f) onProjectImport(f);
1424
+ e.target.value = "";
1425
+ }
1426
+ }
1427
+ )
1428
+ ] })
1429
+ ] }),
1430
+ onWorkspaceImport && /* @__PURE__ */ jsxs10(Fragment3, { children: [
1431
+ /* @__PURE__ */ jsx12(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
1432
+ /* @__PURE__ */ jsx12(
1433
+ "input",
1434
+ {
1435
+ ref: workspaceInputRef,
1436
+ type: "file",
1437
+ accept: ".json",
1438
+ className: "hidden",
1439
+ onChange: (e) => {
1440
+ const f = e.target.files?.[0];
1441
+ if (f) onWorkspaceImport(f);
1442
+ e.target.value = "";
1443
+ }
1444
+ }
1445
+ )
1446
+ ] })
1447
+ ] }),
1448
+ (onServerSave || serverProjects || onServerDelete) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-4", children: [
1449
+ /* @__PURE__ */ jsx12(SectionLabel, { children: "Server-Speicher" }),
1450
+ onServerSave && /* @__PURE__ */ jsxs10("div", { className: "flex gap-2", children: [
1451
+ /* @__PURE__ */ jsx12(
1452
+ "input",
1453
+ {
1454
+ type: "text",
1455
+ value: saveName,
1456
+ onChange: (e) => setSaveName(e.target.value),
1457
+ placeholder: "Name (optional)",
1458
+ 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",
1459
+ style: { height: 40 }
1460
+ }
861
1461
  ),
862
- /* @__PURE__ */ jsx10(AnimatePresence, { children: showFeedback && /* @__PURE__ */ jsx10(
863
- motion5.div,
1462
+ /* @__PURE__ */ jsx12(
1463
+ PillButton,
864
1464
  {
865
- initial: { height: 0, opacity: 0 },
866
- animate: { height: "auto", opacity: 1 },
867
- exit: { height: 0, opacity: 0 },
868
- className: "overflow-hidden",
869
- children: /* @__PURE__ */ jsx10("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 })
1465
+ variant: "filled",
1466
+ icon: "cloud_upload",
1467
+ loading: isSaving,
1468
+ onClick: async () => {
1469
+ setIsSaving(true);
1470
+ try {
1471
+ await onServerSave(saveName || void 0);
1472
+ setSaveName("");
1473
+ await onRefreshServerProjects?.();
1474
+ } finally {
1475
+ setIsSaving(false);
1476
+ }
1477
+ },
1478
+ children: "Speichern"
870
1479
  }
871
- ) })
1480
+ )
872
1481
  ] }),
873
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
874
- selectedLabels.size > 0 ? /* @__PURE__ */ jsxs8("span", { className: "text-[8px] text-white/30 font-bold uppercase", children: [
875
- selectedLabels.size,
876
- " Tags gew\xE4hlt"
877
- ] }) : /* @__PURE__ */ jsx10("span", { className: "text-[8px] text-white/20 italic", children: "Keine Tags gew\xE4hlt" }),
878
- /* @__PURE__ */ jsx10(
1482
+ (!serverProjects || serverProjects.length === 0) && /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts gespeichert." }),
1483
+ serverProjects && serverProjects.length > 0 && /* @__PURE__ */ jsx12("div", { className: "flex flex-col gap-1", children: serverProjects.map((p) => /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
1484
+ /* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0", children: [
1485
+ /* @__PURE__ */ jsx12("p", { className: "text-[11px] text-white/70 font-bold truncate", children: p.name }),
1486
+ /* @__PURE__ */ jsxs10("p", { className: "text-[9px] text-white/25", children: [
1487
+ new Date(p.saved_at).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }),
1488
+ " \xB7 ",
1489
+ (p.size / 1024).toFixed(0),
1490
+ " KB"
1491
+ ] })
1492
+ ] }),
1493
+ onServerLoad && /* @__PURE__ */ jsx12("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__ */ jsx12("span", { className: "material-symbols-outlined text-[18px]", children: "cloud_download" }) }),
1494
+ /* @__PURE__ */ jsx12("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__ */ jsx12("span", { className: "material-symbols-outlined text-[18px]", children: "delete" }) })
1495
+ ] }, p.id)) })
1496
+ ] }),
1497
+ onComputeSyncDiff && onExecuteSync && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-4", children: [
1498
+ /* @__PURE__ */ jsx12(SectionLabel, { children: "Sync" }),
1499
+ (syncState === "idle" || syncState === "computing") && /* @__PURE__ */ jsx12(PillButton, { variant: "outline", icon: "compare_arrows", loading: syncState === "computing", onClick: handleComputeDiff, children: "Diff berechnen" }),
1500
+ syncState === "done" && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 px-2 text-[11px] text-green-400", children: [
1501
+ /* @__PURE__ */ jsx12("span", { className: "material-symbols-outlined text-[16px]", children: "check_circle" }),
1502
+ "Sync abgeschlossen"
1503
+ ] }),
1504
+ syncState === "error" && /* @__PURE__ */ jsx12("p", { className: "text-[11px] text-red-400 px-2", children: "Fehler beim Sync." }),
1505
+ syncDiff && (syncState === "ready" || syncState === "executing") && /* @__PURE__ */ jsxs10(Fragment3, { children: [
1506
+ syncDiff.localOnly.length === 0 && syncDiff.serverOnly.length === 0 && /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-white/30 px-2", children: "Alles synchron \u2014 keine Unterschiede." }),
1507
+ syncDiff.localOnly.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1508
+ /* @__PURE__ */ jsxs10("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
1509
+ "Nur lokal (",
1510
+ syncDiff.localOnly.length,
1511
+ ")"
1512
+ ] }),
1513
+ syncDiff.localOnly.map((gen) => /* @__PURE__ */ jsxs10(
1514
+ "button",
1515
+ {
1516
+ onClick: () => toggleLocalId(gen.id),
1517
+ 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"}`,
1518
+ children: [
1519
+ /* @__PURE__ */ jsx12("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__ */ jsx12("span", { className: "material-symbols-outlined text-[11px] text-white", children: "check" }) }),
1520
+ gen.base64 && /* @__PURE__ */ jsx12("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
1521
+ /* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0", children: [
1522
+ /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
1523
+ /* @__PURE__ */ jsx12("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
1524
+ ] })
1525
+ ]
1526
+ },
1527
+ gen.id
1528
+ ))
1529
+ ] }),
1530
+ syncDiff.serverOnly.length > 0 && /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-2", children: [
1531
+ /* @__PURE__ */ jsxs10("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
1532
+ "Nur auf Server (",
1533
+ syncDiff.serverOnly.length,
1534
+ ")"
1535
+ ] }),
1536
+ syncDiff.serverOnly.map((gen) => /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-3 px-3 py-2 rounded-xl bg-white/3 border border-white/5 opacity-40", children: [
1537
+ gen.base64 && /* @__PURE__ */ jsx12("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
1538
+ /* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0", children: [
1539
+ /* @__PURE__ */ jsx12("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
1540
+ /* @__PURE__ */ jsx12("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
1541
+ ] })
1542
+ ] }, gen.id))
1543
+ ] }),
1544
+ (syncDiff.localOnly.length > 0 || syncDiff.serverOnly.length > 0) && /* @__PURE__ */ jsxs10(
879
1545
  PillButton,
880
1546
  {
881
1547
  variant: "solid",
882
- icon: "auto_fix_high",
883
- loading: isGenerating,
884
- disabled: selectedLabels.size === 0 && !instructions.trim(),
885
- onClick: handleGenerate,
886
- children: "Prompt bauen"
1548
+ icon: "sync",
1549
+ loading: syncState === "executing",
1550
+ disabled: selectedLocalIds.size === 0,
1551
+ onClick: handleExecuteSync,
1552
+ children: [
1553
+ selectedLocalIds.size,
1554
+ " \xFCbertragen"
1555
+ ]
887
1556
  }
888
1557
  )
889
1558
  ] })
890
1559
  ] })
891
- ] });
1560
+ ] }) });
892
1561
  };
893
1562
 
894
1563
  // src/components/AvatarArchitectApp.tsx
895
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
896
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
1564
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1565
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
897
1566
  useEffect4(() => {
898
1567
  const id = "flow-styles";
899
1568
  if (!document.getElementById(id)) {
@@ -903,28 +1572,122 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
903
1572
  document.head.appendChild(style);
904
1573
  }
905
1574
  }, []);
906
- const [nodes, setNodes] = useState4([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
907
- const [edges, setEdges] = useState4([]);
908
- const [history, setHistory] = useState4([]);
909
- const [galleryItems, setGalleryItems] = useState4([]);
910
- const [activePrompt, setActivePrompt] = useState4("");
911
- const [isSynthesizing, setIsSynthesizing] = useState4(false);
912
- const [activeGenerationsCount, setActiveGenerationsCount] = useState4(0);
913
- const [currentResult, setCurrentResult] = useState4(null);
914
- const [focusedNodeId, setFocusedNodeId] = useState4(null);
915
- const [leftTab, setLeftTab] = useState4("hierarchy");
916
- const [promptFeedback, setPromptFeedback] = useState4(null);
917
- const [isPromptTabGenerating, setIsPromptTabGenerating] = useState4(false);
918
- const [activeTab, setActiveTab] = useState4("history");
919
- const [aspectRatio, setAspectRatio] = useState4("1:1");
920
- const [selectedModel, setSelectedModel] = useState4("\u{1F34C} Nano Banana Pro");
921
- const [seed, setSeed] = useState4(Math.floor(Math.random() * 1e6));
922
- const [seedMode, setSeedMode] = useState4("random");
923
- const [isLeftCollapsed, setIsLeftCollapsed] = useState4(false);
924
- const [isRightCollapsed, setIsRightCollapsed] = useState4(false);
925
- const [isPromptCollapsed, setIsPromptCollapsed] = useState4(false);
926
- const [projectActionState, setProjectActionState] = useState4("idle");
927
- const [workspaceTags, setWorkspaceTags] = useState4(null);
1575
+ const [showStart, setShowStart] = useState6(true);
1576
+ const [layoutChoice, setLayoutChoice] = useState6(() => {
1577
+ try {
1578
+ return localStorage.getItem("aa-layout") || null;
1579
+ } catch {
1580
+ return null;
1581
+ }
1582
+ });
1583
+ const [projectLoaded, setProjectLoaded] = useState6(false);
1584
+ const wsInputRef = useRef6(null);
1585
+ const startApp = (choice) => {
1586
+ try {
1587
+ localStorage.setItem("aa-layout", choice);
1588
+ } catch {
1589
+ }
1590
+ setLayoutChoice(choice);
1591
+ setShowStart(false);
1592
+ };
1593
+ const [nodes, setNodes] = useState6([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
1594
+ const [edges, setEdges] = useState6([]);
1595
+ const [history, setHistory] = useState6([]);
1596
+ const [galleryItems, setGalleryItems] = useState6([]);
1597
+ const [activePrompt, setActivePrompt] = useState6("");
1598
+ const [isSynthesizing, setIsSynthesizing] = useState6(false);
1599
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState6(0);
1600
+ const [currentResult, setCurrentResult] = useState6(null);
1601
+ const [focusedNodeId, setFocusedNodeId] = useState6(null);
1602
+ const [leftTab, setLeftTab] = useState6("prompt");
1603
+ const [promptFeedback, setPromptFeedback] = useState6(null);
1604
+ const [lastPromptPayload, setLastPromptPayload] = useState6(null);
1605
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState6(false);
1606
+ const [activeTab, setActiveTab] = useState6("history");
1607
+ const [mobileTab, setMobileTab] = useState6("stage");
1608
+ const [aspectRatio, setAspectRatio] = useState6("1:1");
1609
+ const [selectedModel, setSelectedModel] = useState6("\u{1F34C} Nano Banana Pro");
1610
+ const [seed, setSeed] = useState6(Math.floor(Math.random() * 1e6));
1611
+ const [seedMode, setSeedMode] = useState6("random");
1612
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState6(false);
1613
+ const [isRightCollapsed, setIsRightCollapsed] = useState6(false);
1614
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState6(false);
1615
+ const [projectActionState, setProjectActionState] = useState6("idle");
1616
+ const syncServerDataRef = useRef6(null);
1617
+ const [workspaceTags, setWorkspaceTags] = useState6(null);
1618
+ const [serverProjects, setServerProjects] = useState6([]);
1619
+ const [isLoadingFromServer, setIsLoadingFromServer] = useState6(false);
1620
+ const [highContrast, setHighContrast] = useState6(() => {
1621
+ try {
1622
+ return localStorage.getItem("aa-contrast") === "high";
1623
+ } catch {
1624
+ return false;
1625
+ }
1626
+ });
1627
+ const [touchStartX, setTouchStartX] = useState6(null);
1628
+ const [isFullscreen, setIsFullscreen] = useState6(false);
1629
+ const [zoomScale, setZoomScale] = useState6(1);
1630
+ const [zoomOffset, setZoomOffset] = useState6({ x: 0, y: 0 });
1631
+ const lastPinchDist = useRef6(null);
1632
+ const lastTapTime = useRef6(0);
1633
+ const dragStart = useRef6(null);
1634
+ const openFullscreen = () => {
1635
+ setIsFullscreen(true);
1636
+ setZoomScale(1);
1637
+ setZoomOffset({ x: 0, y: 0 });
1638
+ };
1639
+ const closeFullscreen = () => {
1640
+ setIsFullscreen(false);
1641
+ setZoomScale(1);
1642
+ setZoomOffset({ x: 0, y: 0 });
1643
+ };
1644
+ const handleFsTouchStart = (e) => {
1645
+ if (e.touches.length === 2) {
1646
+ lastPinchDist.current = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
1647
+ } else if (e.touches.length === 1) {
1648
+ const now = Date.now();
1649
+ if (now - lastTapTime.current < 280) {
1650
+ setZoomScale((s) => s > 1 ? 1 : 2.5);
1651
+ setZoomOffset({ x: 0, y: 0 });
1652
+ }
1653
+ lastTapTime.current = now;
1654
+ dragStart.current = { x: e.touches[0].clientX, y: e.touches[0].clientY, ox: zoomOffset.x, oy: zoomOffset.y };
1655
+ }
1656
+ };
1657
+ const handleFsTouchMove = (e) => {
1658
+ if (e.touches.length === 2 && lastPinchDist.current !== null) {
1659
+ const dist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
1660
+ setZoomScale((s) => Math.min(Math.max(s * (dist / lastPinchDist.current), 1), 5));
1661
+ lastPinchDist.current = dist;
1662
+ } else if (e.touches.length === 1 && dragStart.current && zoomScale > 1) {
1663
+ setZoomOffset({ x: dragStart.current.ox + e.touches[0].clientX - dragStart.current.x, y: dragStart.current.oy + e.touches[0].clientY - dragStart.current.y });
1664
+ }
1665
+ };
1666
+ const handleFsTouchEnd = (e) => {
1667
+ lastPinchDist.current = null;
1668
+ if (e.changedTouches.length === 1 && zoomScale <= 1 && dragStart.current) {
1669
+ const dx = e.changedTouches[0].clientX - dragStart.current.x;
1670
+ if (dx < -50) goToNext();
1671
+ else if (dx > 50) goToPrev();
1672
+ }
1673
+ dragStart.current = null;
1674
+ };
1675
+ const toggleContrast = () => {
1676
+ const next = !highContrast;
1677
+ setHighContrast(next);
1678
+ try {
1679
+ localStorage.setItem("aa-contrast", next ? "high" : "low");
1680
+ } catch {
1681
+ }
1682
+ };
1683
+ const currentIndex = useMemo2(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1684
+ const goToPrev = useCallback(() => {
1685
+ if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
1686
+ }, [currentIndex, history]);
1687
+ const goToNext = useCallback(() => {
1688
+ if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
1689
+ }, [currentIndex, history]);
1690
+ const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
928
1691
  const isGenerating = activeGenerationsCount > 0;
929
1692
  useKeyboardNavigation(history, currentResult, setCurrentResult);
930
1693
  const getSubtreeFormat = useCallback((nodeId, depth = 0) => {
@@ -935,7 +1698,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
935
1698
  return `${indent}- ${node.data.label || "(unbenannt)"}
936
1699
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
937
1700
  }, [nodes, edges]);
938
- const activePath = useMemo(() => {
1701
+ const activePath = useMemo2(() => {
939
1702
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
940
1703
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
941
1704
  let currId = focusedNodeId;
@@ -953,17 +1716,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
953
1716
  setActiveGenerationsCount((prev) => prev + 1);
954
1717
  const genId = crypto.randomUUID();
955
1718
  const activeSeed = seedMode === "random" ? Math.floor(Math.random() * 1e9) : seed;
956
- const newGen = {
957
- id: genId,
958
- nodeId: overrideNodeId || focusedNodeId || "1",
959
- status: "processing",
960
- timestamp: Date.now(),
961
- prompt: promptToUse,
962
- seed: activeSeed,
963
- model: selectedModel,
964
- tags: [],
965
- type: "generation"
966
- };
1719
+ const newGen = { id: genId, nodeId: overrideNodeId || focusedNodeId || "1", status: "processing", timestamp: Date.now(), prompt: promptToUse, seed: activeSeed, model: selectedModel, tags: [], type: "generation" };
967
1720
  setHistory((prev) => [newGen, ...prev]);
968
1721
  if (!options.silent) setCurrentResult(newGen);
969
1722
  if (seedMode === "random") setSeed(activeSeed);
@@ -1000,12 +1753,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1000
1753
  setIsSynthesizing(false);
1001
1754
  }
1002
1755
  };
1003
- const handlePromptTabGenerate = async (selectedValues, instructions) => {
1756
+ const handleTagCreate = (tag) => {
1757
+ setWorkspaceTags((prev) => {
1758
+ if (!prev) return prev;
1759
+ const newTagOption = { label: tag.label, value: tag.value, is_user_created: true };
1760
+ const updatedCat = [...prev.by_category[tag.category] || [], newTagOption];
1761
+ return {
1762
+ by_category: { ...prev.by_category, [tag.category]: updatedCat },
1763
+ all: [...prev.all, { ...newTagOption, category: tag.category }]
1764
+ };
1765
+ });
1766
+ };
1767
+ const handleTagUpdate = (originalLabel, originalCategory, updates) => {
1768
+ setWorkspaceTags((prev) => {
1769
+ if (!prev) return prev;
1770
+ const updatedCat = (prev.by_category[originalCategory] || []).map(
1771
+ (t) => t.label === originalLabel ? { ...t, ...updates } : t
1772
+ );
1773
+ const updatedAll = prev.all.map(
1774
+ (t) => t.label === originalLabel && t.category === originalCategory ? { ...t, ...updates } : t
1775
+ );
1776
+ return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
1777
+ });
1778
+ };
1779
+ const handleTagDelete = (label, category) => {
1780
+ setWorkspaceTags((prev) => {
1781
+ if (!prev) return prev;
1782
+ const updatedCat = (prev.by_category[category] || []).map(
1783
+ (t) => t.label === label ? { ...t, is_deleted: true } : t
1784
+ );
1785
+ const updatedAll = prev.all.map(
1786
+ (t) => t.label === label && t.category === category ? { ...t, is_deleted: true } : t
1787
+ );
1788
+ return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
1789
+ });
1790
+ };
1791
+ const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
1004
1792
  setIsPromptTabGenerating(true);
1005
1793
  setPromptFeedback(null);
1006
1794
  try {
1007
1795
  const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : void 0;
1008
- const payload = buildPromptTabPayload(selectedValues, instructions, treeText);
1796
+ const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
1797
+ setLastPromptPayload(payload);
1009
1798
  const raw = await onGeneratePrompt(payload);
1010
1799
  const { prompt, feedback } = parsePromptResponse(raw);
1011
1800
  setActivePrompt(prompt);
@@ -1022,9 +1811,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1022
1811
  const base64Data = currentResult.base64.split(",")[1];
1023
1812
  const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
1024
1813
  let finalBase64 = base64Data;
1025
- if (mimeType === "image/png") {
1026
- finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
1027
- }
1814
+ if (mimeType === "image/png") finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
1028
1815
  await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
1029
1816
  } catch (e) {
1030
1817
  console.error("Download Error", e);
@@ -1033,7 +1820,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1033
1820
  const handleProjectExport = async () => {
1034
1821
  setProjectActionState("working-full");
1035
1822
  try {
1036
- const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode });
1823
+ const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
1037
1824
  await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
1038
1825
  setProjectActionState("done");
1039
1826
  setTimeout(() => setProjectActionState("idle"), 3e3);
@@ -1049,7 +1836,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1049
1836
  const data = await importProjectFromZip(file);
1050
1837
  if (data.nodes) setNodes(data.nodes);
1051
1838
  if (data.edges) setEdges(data.edges);
1052
- if (data.history) setHistory(data.history);
1839
+ if (data.history) {
1840
+ setHistory(data.history);
1841
+ if (data.history.length > 0) setCurrentResult(data.history[0]);
1842
+ }
1053
1843
  if (data.galleryItems) setGalleryItems(data.galleryItems);
1054
1844
  if (data.settings) {
1055
1845
  setAspectRatio(data.settings.aspectRatio || "1:1");
@@ -1057,7 +1847,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1057
1847
  setSeed(data.settings.seed || 0);
1058
1848
  setSeedMode(data.settings.seedMode || "random");
1059
1849
  }
1850
+ if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
1060
1851
  setProjectActionState("done");
1852
+ setProjectLoaded(true);
1061
1853
  setTimeout(() => setProjectActionState("idle"), 2e3);
1062
1854
  } catch {
1063
1855
  setProjectActionState("error");
@@ -1072,12 +1864,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1072
1864
  const rawTags = root.tags || root;
1073
1865
  if (!rawTags?.by_category) return;
1074
1866
  const filtered = {};
1075
- const EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
1867
+ const EXCLUDED2 = ["pdf-export", "book.micro-edit-rules"];
1076
1868
  Object.entries(rawTags.by_category).forEach(([cat, items]) => {
1077
- if (EXCLUDED.includes(cat) || !Array.isArray(items)) return;
1869
+ if (EXCLUDED2.includes(cat) || !Array.isArray(items)) return;
1078
1870
  filtered[cat] = items.filter((t) => t && typeof t === "object" && t.label && t.value);
1079
1871
  });
1080
- const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED.includes(t.category));
1872
+ const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED2.includes(t.category));
1081
1873
  setWorkspaceTags({ by_category: filtered, all: filteredAll });
1082
1874
  } catch (err) {
1083
1875
  console.error("Workspace Load failed", err);
@@ -1085,37 +1877,571 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1085
1877
  };
1086
1878
  reader.readAsText(file);
1087
1879
  };
1088
- return /* @__PURE__ */ jsxs9("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", children: [
1089
- /* @__PURE__ */ jsxs9(motion6.div, { animate: { width: isLeftCollapsed ? 48 : 260 }, className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", children: [
1090
- /* @__PURE__ */ jsxs9("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
1091
- !isLeftCollapsed && /* @__PURE__ */ jsxs9("div", { className: "flex flex-1 gap-1", children: [
1092
- /* @__PURE__ */ jsxs9(
1093
- "button",
1880
+ const fetchServerProjects = async () => {
1881
+ if (!onFetchServerProjects) return;
1882
+ try {
1883
+ setServerProjects(await onFetchServerProjects());
1884
+ } catch {
1885
+ }
1886
+ };
1887
+ const handleServerSave = async (name) => {
1888
+ if (!onServerSave) return;
1889
+ setProjectActionState("working-full");
1890
+ try {
1891
+ const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
1892
+ await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
1893
+ await fetchServerProjects();
1894
+ setProjectActionState("done");
1895
+ setTimeout(() => setProjectActionState("idle"), 2e3);
1896
+ } catch {
1897
+ setProjectActionState("error");
1898
+ setTimeout(() => setProjectActionState("idle"), 4e3);
1899
+ }
1900
+ };
1901
+ const handleServerLoad = async (id) => {
1902
+ if (!onServerLoad) return;
1903
+ const file = await onServerLoad(id);
1904
+ await handleProjectImport(file);
1905
+ };
1906
+ const handleServerDelete = async (id) => {
1907
+ if (!onServerDelete) return;
1908
+ await onServerDelete(id);
1909
+ await fetchServerProjects();
1910
+ };
1911
+ const handleComputeSyncDiff = async () => {
1912
+ if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
1913
+ const projects = await onFetchServerProjects();
1914
+ if (projects.length === 0) return { localOnly: [...history], serverOnly: [] };
1915
+ const file = await onServerLoad(projects[0].id);
1916
+ const serverData = await importProjectFromZip(file);
1917
+ syncServerDataRef.current = serverData;
1918
+ const localOnly = history.filter((h) => !(serverData.history || []).find((s) => s.id === h.id));
1919
+ const serverOnly = (serverData.history || []).filter((s) => !history.find((h) => h.id === s.id));
1920
+ return { localOnly, serverOnly };
1921
+ };
1922
+ const handleExecuteSync = async (selectedLocalIds) => {
1923
+ const serverData = syncServerDataRef.current;
1924
+ if (!serverData || !onServerSave) return;
1925
+ setProjectActionState("working-full");
1926
+ try {
1927
+ const selectedLocal = history.filter((h) => selectedLocalIds.includes(h.id));
1928
+ const mergedHistory = [...serverData.history || [], ...selectedLocal];
1929
+ const { base64 } = await exportProjectToZip(
1930
+ serverData.nodes || nodes,
1931
+ serverData.edges || edges,
1932
+ mergedHistory,
1933
+ serverData.galleryItems || galleryItems,
1934
+ serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
1935
+ serverData.workspaceTags || workspaceTags
1936
+ );
1937
+ await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
1938
+ await fetchServerProjects();
1939
+ setProjectActionState("done");
1940
+ setTimeout(() => setProjectActionState("idle"), 2e3);
1941
+ } catch {
1942
+ setProjectActionState("error");
1943
+ setTimeout(() => setProjectActionState("idle"), 4e3);
1944
+ }
1945
+ };
1946
+ useEffect4(() => {
1947
+ if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
1948
+ }, [activeTab]);
1949
+ if (isFullscreen && currentResult?.base64) {
1950
+ const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
1951
+ return /* @__PURE__ */ jsxs11(
1952
+ "div",
1953
+ {
1954
+ className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
1955
+ onTouchStart: handleFsTouchStart,
1956
+ onTouchMove: handleFsTouchMove,
1957
+ onTouchEnd: handleFsTouchEnd,
1958
+ children: [
1959
+ /* @__PURE__ */ jsx13(
1960
+ "img",
1094
1961
  {
1095
- onClick: () => setLeftTab("hierarchy"),
1096
- 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"}`,
1097
- children: [
1098
- /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
1099
- "Hierarchie"
1100
- ]
1962
+ src: fsBase64,
1963
+ alt: "",
1964
+ draggable: false,
1965
+ style: {
1966
+ width: "100%",
1967
+ height: "100%",
1968
+ objectFit: "contain",
1969
+ transform: `scale(${zoomScale}) translate(${zoomOffset.x / zoomScale}px, ${zoomOffset.y / zoomScale}px)`,
1970
+ transformOrigin: "center center",
1971
+ userSelect: "none",
1972
+ pointerEvents: "none"
1973
+ }
1101
1974
  }
1102
1975
  ),
1103
- workspaceTags && /* @__PURE__ */ jsxs9(
1104
- "button",
1976
+ /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
1977
+ zoomScale > 1 && /* @__PURE__ */ jsx13("button", { onClick: () => {
1978
+ setZoomScale(1);
1979
+ setZoomOffset({ x: 0, y: 0 });
1980
+ }, 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__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
1981
+ history.length > 1 && /* @__PURE__ */ jsxs11(Fragment4, { children: [
1982
+ /* @__PURE__ */ jsx13("button", { onClick: () => {
1983
+ if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
1984
+ }, 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__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
1985
+ /* @__PURE__ */ jsx13("button", { onClick: () => {
1986
+ if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
1987
+ }, 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__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
1988
+ /* @__PURE__ */ jsxs11("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: [
1989
+ currentIndex + 1,
1990
+ " / ",
1991
+ history.length
1992
+ ] })
1993
+ ] }),
1994
+ zoomScale === 1 && /* @__PURE__ */ jsx13("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
1995
+ ]
1996
+ }
1997
+ );
1998
+ }
1999
+ if (showStart) {
2000
+ return /* @__PURE__ */ jsxs11("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
2001
+ /* @__PURE__ */ jsx13("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
2002
+ const f = e.target.files?.[0];
2003
+ if (f) handleProjectImport(f);
2004
+ e.target.value = "";
2005
+ } }),
2006
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-1", children: [
2007
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
2008
+ /* @__PURE__ */ jsx13("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" })
2009
+ ] }),
2010
+ /* @__PURE__ */ jsxs11(
2011
+ "button",
2012
+ {
2013
+ onClick: toggleContrast,
2014
+ className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
2015
+ style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
2016
+ children: [
2017
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
2018
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-start", children: [
2019
+ /* @__PURE__ */ jsx13("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
2020
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
2021
+ ] })
2022
+ ]
2023
+ }
2024
+ ),
2025
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2026
+ /* @__PURE__ */ jsxs11(
2027
+ "button",
2028
+ {
2029
+ onClick: () => wsInputRef.current?.click(),
2030
+ 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",
2031
+ style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
2032
+ children: [
2033
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
2034
+ projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
2035
+ ]
2036
+ }
2037
+ ),
2038
+ !projectLoaded && /* @__PURE__ */ jsx13("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
2039
+ ] }),
2040
+ onFetchServerProjects && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2041
+ /* @__PURE__ */ jsxs11(
2042
+ "button",
2043
+ {
2044
+ disabled: isLoadingFromServer,
2045
+ onClick: async () => {
2046
+ setIsLoadingFromServer(true);
2047
+ try {
2048
+ const projects = await onFetchServerProjects();
2049
+ setServerProjects(projects);
2050
+ if (projects.length > 0 && onServerLoad) {
2051
+ const file = await onServerLoad(projects[0].id);
2052
+ await handleProjectImport(file);
2053
+ }
2054
+ } catch {
2055
+ } finally {
2056
+ setIsLoadingFromServer(false);
2057
+ }
2058
+ },
2059
+ 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",
2060
+ style: { height: 56, background: "#7c3aed" },
2061
+ children: [
2062
+ /* @__PURE__ */ jsx13("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
2063
+ isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
2064
+ ]
2065
+ }
2066
+ ),
2067
+ /* @__PURE__ */ jsx13("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
2068
+ ] }),
2069
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
2070
+ /* @__PURE__ */ jsx13("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
2071
+ /* @__PURE__ */ jsx13("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
2072
+ { id: "mobile", icon: "smartphone", label: "Mobile" },
2073
+ { id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
2074
+ { id: "desktop", icon: "desktop_windows", label: "Desktop" },
2075
+ { id: "tablet-landscape", icon: "tablet", label: "Landscape" }
2076
+ ].map((opt) => /* @__PURE__ */ jsxs11(
2077
+ "button",
2078
+ {
2079
+ onClick: () => startApp(opt.id),
2080
+ className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
2081
+ 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" },
2082
+ children: [
2083
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
2084
+ /* @__PURE__ */ jsx13("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
2085
+ ]
2086
+ },
2087
+ opt.id
2088
+ )) }),
2089
+ layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx13("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
2090
+ layoutChoice === "tablet-landscape" && /* @__PURE__ */ jsx13("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
2091
+ ] })
2092
+ ] });
2093
+ }
2094
+ if (layoutChoice === "mobile" || layoutChoice === "mobile-desktop") {
2095
+ const mdMode = layoutChoice === "mobile-desktop";
2096
+ const mdScale = mdMode ? window.innerWidth / 430 : 1;
2097
+ const mdW = mdMode ? 430 : void 0;
2098
+ const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
2099
+ const mobileRoot = /* @__PURE__ */ jsxs11("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
2100
+ width: mdMode ? mdW : "100vw",
2101
+ height: mdMode ? mdH : "100dvh",
2102
+ transform: mdMode ? `scale(${mdScale})` : void 0,
2103
+ transformOrigin: mdMode ? "top left" : void 0,
2104
+ ...hcStyle || {}
2105
+ }, children: [
2106
+ mobileTab === "stage" && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col flex-1 min-h-0", children: [
2107
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
2108
+ /* @__PURE__ */ jsx13(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2109
+ /* @__PURE__ */ jsx13(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" }] }),
2110
+ /* @__PURE__ */ jsx13("div", { className: "flex-1" }),
2111
+ /* @__PURE__ */ jsx13("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
2112
+ /* @__PURE__ */ jsx13("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
2113
+ ] }),
2114
+ /* @__PURE__ */ jsx13("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ jsxs11("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
2115
+ /* @__PURE__ */ jsx13(
2116
+ "textarea",
1105
2117
  {
1106
- onClick: () => setLeftTab("prompt"),
1107
- 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"}`,
2118
+ value: activePrompt,
2119
+ onChange: (e) => setActivePrompt(e.target.value),
2120
+ 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",
2121
+ style: { height: 80 },
2122
+ placeholder: "Prompt eingeben..."
2123
+ }
2124
+ ),
2125
+ activePrompt && !isSynthesizing && /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
2126
+ ] }) }),
2127
+ /* @__PURE__ */ jsx13("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ jsx13(
2128
+ "button",
2129
+ {
2130
+ onClick: () => handleGenerateImage(),
2131
+ disabled: !activePrompt.trim() || isGenerating,
2132
+ 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",
2133
+ style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
2134
+ children: isGenerating ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
2135
+ /* @__PURE__ */ jsx13("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
2136
+ /* @__PURE__ */ jsx13("span", { children: "Generiere..." })
2137
+ ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
2138
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
2139
+ /* @__PURE__ */ jsx13("span", { children: "Generieren" })
2140
+ ] })
2141
+ }
2142
+ ) }),
2143
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
2144
+ /* @__PURE__ */ jsxs11(
2145
+ "div",
2146
+ {
2147
+ className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
2148
+ style: { aspectRatio: "1/1" },
2149
+ onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
2150
+ onTouchEnd: (e) => {
2151
+ if (touchStartX === null) return;
2152
+ const dx = e.changedTouches[0].clientX - touchStartX;
2153
+ if (dx < -50) goToNext();
2154
+ else if (dx > 50) goToPrev();
2155
+ setTouchStartX(null);
2156
+ },
1108
2157
  children: [
1109
- /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
1110
- "Prompt"
2158
+ currentResult?.status === "processing" && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-3", children: [
2159
+ /* @__PURE__ */ jsx13("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
2160
+ /* @__PURE__ */ jsx13("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
2161
+ ] }),
2162
+ currentResult?.status === "error" && /* @__PURE__ */ jsxs11("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
2163
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
2164
+ /* @__PURE__ */ jsx13("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
2165
+ /* @__PURE__ */ jsx13("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" })
2166
+ ] }),
2167
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx13("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
2168
+ !currentResult && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
2169
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
2170
+ /* @__PURE__ */ jsx13("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
2171
+ ] }),
2172
+ currentResult?.status === "done" && /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
2173
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs11(Fragment4, { children: [
2174
+ /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
2175
+ /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
2176
+ /* @__PURE__ */ jsxs11("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: [
2177
+ currentIndex + 1,
2178
+ " / ",
2179
+ history.length
2180
+ ] })
2181
+ ] })
1111
2182
  ]
1112
2183
  }
2184
+ ),
2185
+ currentResult?.status === "done" && /* @__PURE__ */ jsxs11("div", { className: "flex gap-2 mt-3", children: [
2186
+ /* @__PURE__ */ jsxs11("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: [
2187
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
2188
+ /* @__PURE__ */ jsx13("span", { className: "text-[12px] text-white/60", children: "Prompt" })
2189
+ ] }),
2190
+ /* @__PURE__ */ jsxs11("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: [
2191
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
2192
+ /* @__PURE__ */ jsx13("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
2193
+ ] }),
2194
+ /* @__PURE__ */ jsxs11("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: [
2195
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
2196
+ /* @__PURE__ */ jsx13("span", { className: "text-[12px] text-white/60", children: "Laden" })
2197
+ ] })
2198
+ ] })
2199
+ ] })
2200
+ ] }),
2201
+ mobileTab === "browse" && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col flex-1 min-h-0", children: [
2202
+ /* @__PURE__ */ jsx13("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)) }),
2203
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-hidden relative", children: [
2204
+ activeTab === "history" && /* @__PURE__ */ jsx13(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
2205
+ setCurrentResult(g);
2206
+ setMobileTab("stage");
2207
+ }, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
2208
+ activeTab === "gallery" && /* @__PURE__ */ jsx13(
2209
+ MediaLibrary,
2210
+ {
2211
+ items: galleryItems,
2212
+ onImport: async () => {
2213
+ const media = await onSelectMedia();
2214
+ if (!media?.length) return;
2215
+ 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]);
2216
+ },
2217
+ onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
2218
+ onSelect: (g) => {
2219
+ setCurrentResult(g);
2220
+ setMobileTab("stage");
2221
+ },
2222
+ onGenerateReference: (item) => {
2223
+ handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true });
2224
+ setMobileTab("stage");
2225
+ }
2226
+ }
2227
+ ),
2228
+ activeTab === "inspect" && /* @__PURE__ */ jsx13(InspectPanel, { currentResult, history, onSelect: (g) => {
2229
+ setCurrentResult(g);
2230
+ } })
2231
+ ] })
2232
+ ] }),
2233
+ mobileTab === "tools" && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col flex-1 min-h-0", children: [
2234
+ /* @__PURE__ */ jsxs11("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
2235
+ workspaceTags && /* @__PURE__ */ jsxs11("button", { onClick: () => {
2236
+ setLeftTab("prompt");
2237
+ if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
2238
+ }, 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: [
2239
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
2240
+ "Prompt"
2241
+ ] }),
2242
+ /* @__PURE__ */ jsxs11("button", { onClick: () => {
2243
+ setLeftTab("hierarchy");
2244
+ if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
2245
+ }, 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: [
2246
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
2247
+ "Hierarchie"
2248
+ ] }),
2249
+ /* @__PURE__ */ jsxs11("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: [
2250
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
2251
+ "Setup"
2252
+ ] }),
2253
+ /* @__PURE__ */ jsxs11("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: [
2254
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
2255
+ "Sync"
2256
+ ] })
2257
+ ] }),
2258
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-hidden relative", children: [
2259
+ leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx13("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx13(
2260
+ ListView,
2261
+ {
2262
+ nodes,
2263
+ edges,
2264
+ onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
2265
+ onAddChild: (pId) => {
2266
+ const newId = crypto.randomUUID();
2267
+ setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
2268
+ setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
2269
+ setFocusedNodeId(newId);
2270
+ return newId;
2271
+ },
2272
+ onDeleteNode: (id) => {
2273
+ setNodes((nds) => nds.filter((n) => n.id !== id));
2274
+ setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
2275
+ },
2276
+ onFocus: setFocusedNodeId,
2277
+ focusedNodeId,
2278
+ activePath,
2279
+ onGenerate: (id) => {
2280
+ handleSynthesizePrompt(id);
2281
+ setMobileTab("stage");
2282
+ },
2283
+ onGenerateBranch: (id) => {
2284
+ handleSynthesizePrompt(id, true);
2285
+ setMobileTab("stage");
2286
+ },
2287
+ isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2288
+ }
2289
+ ) }),
2290
+ leftTab === "prompt" && workspaceTags && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2291
+ handleGenerateImage(prompt);
2292
+ setMobileTab("stage");
2293
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2294
+ activeTab === "setup" && /* @__PURE__ */ jsx13(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2295
+ activeTab === "sync" && /* @__PURE__ */ jsx13(
2296
+ ProjectSyncTab,
2297
+ {
2298
+ onProjectExport: handleProjectExport,
2299
+ onProjectImport: (f) => handleProjectImport(f),
2300
+ onWorkspaceImport: handleWorkspaceImport,
2301
+ projectActionState,
2302
+ serverProjects: onFetchServerProjects ? serverProjects : void 0,
2303
+ onServerSave: onServerSave ? handleServerSave : void 0,
2304
+ onServerLoad: onServerLoad ? handleServerLoad : void 0,
2305
+ onServerDelete: onServerDelete ? handleServerDelete : void 0,
2306
+ onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
2307
+ onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
2308
+ onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
2309
+ }
1113
2310
  )
2311
+ ] })
2312
+ ] }),
2313
+ /* @__PURE__ */ jsx13("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
2314
+ { id: "tools", icon: "auto_fix_high", label: "Prompt" },
2315
+ { id: "stage", icon: "palette", label: "Stage" },
2316
+ { id: "browse", icon: "photo_library", label: "Galerie" }
2317
+ ].map((tab) => /* @__PURE__ */ jsxs11("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: [
2318
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
2319
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
2320
+ ] }, tab.id)) })
2321
+ ] });
2322
+ if (mdMode) {
2323
+ return /* @__PURE__ */ jsx13("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
2324
+ }
2325
+ return mobileRoot;
2326
+ }
2327
+ if (layoutChoice === "tablet-landscape") {
2328
+ const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
2329
+ const tlW = 920;
2330
+ const tlH = 520;
2331
+ return /* @__PURE__ */ jsx13("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ jsxs11("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
2332
+ /* @__PURE__ */ jsxs11("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
2333
+ /* @__PURE__ */ jsxs11("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: [
2334
+ /* @__PURE__ */ jsx13(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2335
+ /* @__PURE__ */ jsx13(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" }] }),
2336
+ /* @__PURE__ */ jsx13("div", { style: { flex: 1 } }),
2337
+ /* @__PURE__ */ jsx13("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
2338
+ /* @__PURE__ */ jsx13("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
1114
2339
  ] }),
1115
- /* @__PURE__ */ jsx11("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" })
2340
+ /* @__PURE__ */ jsx13("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs11("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: [
2341
+ /* @__PURE__ */ jsx13(
2342
+ "textarea",
2343
+ {
2344
+ value: activePrompt,
2345
+ onChange: (e) => setActivePrompt(e.target.value),
2346
+ 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" },
2347
+ placeholder: "Prompt eingeben..."
2348
+ }
2349
+ ),
2350
+ activePrompt && /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
2351
+ ] }) }),
2352
+ /* @__PURE__ */ jsx13("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ jsx13(
2353
+ "button",
2354
+ {
2355
+ onClick: () => handleGenerateImage(),
2356
+ disabled: !activePrompt.trim() || isGenerating,
2357
+ 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" },
2358
+ children: isGenerating ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
2359
+ /* @__PURE__ */ jsx13("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
2360
+ /* @__PURE__ */ jsx13("span", { children: "Generiere..." })
2361
+ ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
2362
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
2363
+ /* @__PURE__ */ jsx13("span", { children: "Generieren" })
2364
+ ] })
2365
+ }
2366
+ ) }),
2367
+ /* @__PURE__ */ jsx13("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx13(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
1116
2368
  ] }),
1117
- !isLeftCollapsed && /* @__PURE__ */ jsx11("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ jsxs9(AnimatePresence2, { mode: "wait", children: [
1118
- leftTab === "hierarchy" && /* @__PURE__ */ jsx11(motion6.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ jsx11(
2369
+ /* @__PURE__ */ jsxs11("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
2370
+ /* @__PURE__ */ jsx13(
2371
+ "div",
2372
+ {
2373
+ style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
2374
+ onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
2375
+ onTouchEnd: (e) => {
2376
+ if (touchStartX === null) return;
2377
+ const dx = e.changedTouches[0].clientX - touchStartX;
2378
+ if (dx < -50) goToNext();
2379
+ else if (dx > 50) goToPrev();
2380
+ setTouchStartX(null);
2381
+ },
2382
+ children: /* @__PURE__ */ jsxs11("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: [
2383
+ currentResult?.status === "processing" && /* @__PURE__ */ jsxs11("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
2384
+ /* @__PURE__ */ jsx13("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
2385
+ /* @__PURE__ */ jsx13("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
2386
+ ] }),
2387
+ currentResult?.status === "error" && /* @__PURE__ */ jsxs11("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
2388
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
2389
+ /* @__PURE__ */ jsx13("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
2390
+ /* @__PURE__ */ jsx13("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" })
2391
+ ] }),
2392
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx13("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
2393
+ !currentResult && /* @__PURE__ */ jsxs11("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
2394
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
2395
+ /* @__PURE__ */ jsx13("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
2396
+ ] }),
2397
+ currentResult?.status === "done" && /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
2398
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs11(Fragment4, { children: [
2399
+ /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
2400
+ /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
2401
+ /* @__PURE__ */ jsxs11("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: [
2402
+ currentIndex + 1,
2403
+ " / ",
2404
+ history.length
2405
+ ] })
2406
+ ] })
2407
+ ] })
2408
+ }
2409
+ ),
2410
+ currentResult?.status === "done" && /* @__PURE__ */ jsxs11("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
2411
+ /* @__PURE__ */ jsxs11("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: [
2412
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
2413
+ /* @__PURE__ */ jsx13("span", { children: "Prompt" })
2414
+ ] }),
2415
+ /* @__PURE__ */ jsxs11("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: [
2416
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
2417
+ /* @__PURE__ */ jsx13("span", { children: "Referenz" })
2418
+ ] }),
2419
+ /* @__PURE__ */ jsxs11("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: [
2420
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
2421
+ /* @__PURE__ */ jsx13("span", { children: "Laden" })
2422
+ ] })
2423
+ ] })
2424
+ ] })
2425
+ ] }) });
2426
+ }
2427
+ return /* @__PURE__ */ jsxs11("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
2428
+ /* @__PURE__ */ jsx13("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ jsx13("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
2429
+ /* @__PURE__ */ jsxs11("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: [
2430
+ /* @__PURE__ */ jsxs11("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
2431
+ !isLeftCollapsed && /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 gap-1", children: [
2432
+ workspaceTags && /* @__PURE__ */ jsxs11("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: [
2433
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
2434
+ "Prompt"
2435
+ ] }),
2436
+ /* @__PURE__ */ jsxs11("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: [
2437
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
2438
+ "Hierarchie"
2439
+ ] })
2440
+ ] }),
2441
+ /* @__PURE__ */ jsx13("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" })
2442
+ ] }),
2443
+ !isLeftCollapsed && /* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-hidden relative", children: [
2444
+ leftTab === "hierarchy" && /* @__PURE__ */ jsx13("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx13(
1119
2445
  ListView,
1120
2446
  {
1121
2447
  nodes,
@@ -1139,124 +2465,104 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1139
2465
  onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
1140
2466
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
1141
2467
  }
1142
- ) }, "hierarchy"),
1143
- leftTab === "prompt" && workspaceTags && /* @__PURE__ */ jsx11(motion6.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ jsx11(
1144
- PromptTab,
1145
- {
1146
- workspaceTags,
1147
- onGenerate: handlePromptTabGenerate,
1148
- isGenerating: isPromptTabGenerating,
1149
- feedback: promptFeedback
1150
- }
1151
- ) }, "prompt")
1152
- ] }) })
2468
+ ) }),
2469
+ leftTab === "prompt" && workspaceTags && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete })
2470
+ ] })
1153
2471
  ] }),
1154
- /* @__PURE__ */ jsxs9("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
1155
- /* @__PURE__ */ jsxs9("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
1156
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
1157
- /* @__PURE__ */ jsx11(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
1158
- /* @__PURE__ */ jsx11(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" }] })
2472
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
2473
+ /* @__PURE__ */ jsxs11("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
2474
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5", children: [
2475
+ /* @__PURE__ */ jsx13(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2476
+ /* @__PURE__ */ jsx13(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" }] })
1159
2477
  ] }),
1160
- /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1161
- /* @__PURE__ */ jsx11("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
1162
- /* @__PURE__ */ jsx11(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
2478
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
2479
+ /* @__PURE__ */ jsx13("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
2480
+ /* @__PURE__ */ jsx13(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
1163
2481
  ] })
1164
2482
  ] }),
1165
- /* @__PURE__ */ jsxs9("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
1166
- /* @__PURE__ */ jsx11(AnimatePresence2, { children: !isPromptCollapsed && /* @__PURE__ */ jsx11(motion6.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__ */ jsxs9("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
1167
- /* @__PURE__ */ jsx11(
1168
- "textarea",
1169
- {
1170
- value: activePrompt,
1171
- onChange: (e) => setActivePrompt(e.target.value),
1172
- className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar",
1173
- placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
1174
- }
1175
- ),
1176
- activePrompt && !isSynthesizing && /* @__PURE__ */ jsx11("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__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
1177
- ] }) }) }),
1178
- /* @__PURE__ */ jsx11("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs9("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: [
1179
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs9("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: [
1180
- /* @__PURE__ */ jsx11("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
1181
- /* @__PURE__ */ jsx11("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
2483
+ /* @__PURE__ */ jsxs11("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
2484
+ !isPromptCollapsed && /* @__PURE__ */ jsx13("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxs11("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
2485
+ /* @__PURE__ */ jsx13("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..." }),
2486
+ activePrompt && !isSynthesizing && /* @__PURE__ */ jsx13("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__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2487
+ ] }) }),
2488
+ /* @__PURE__ */ jsx13("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs11("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: [
2489
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs11("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: [
2490
+ /* @__PURE__ */ jsx13("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
2491
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
1182
2492
  ] }),
1183
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center gap-4", children: [
1184
- /* @__PURE__ */ jsx11("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
1185
- /* @__PURE__ */ jsx11("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
1186
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs9("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
1187
- /* @__PURE__ */ jsx11("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
1188
- /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
1189
- /* @__PURE__ */ jsx11("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
1190
- /* @__PURE__ */ jsx11("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
2493
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-4", children: [
2494
+ /* @__PURE__ */ jsx13("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
2495
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
2496
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs11("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
2497
+ /* @__PURE__ */ jsx13("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
2498
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2", children: [
2499
+ /* @__PURE__ */ jsx13("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
2500
+ /* @__PURE__ */ jsx13("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
1191
2501
  ] }),
1192
- /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
1193
- ] }) : /* @__PURE__ */ jsxs9("div", { className: "h-full w-full relative flex items-center justify-center", children: [
1194
- /* @__PURE__ */ jsx11("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
1195
- /* @__PURE__ */ jsxs9("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: [
1196
- /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
1197
- /* @__PURE__ */ jsx11(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
1198
- /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
2502
+ /* @__PURE__ */ jsx13(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
2503
+ ] }) : /* @__PURE__ */ jsxs11("div", { className: "h-full w-full relative flex items-center justify-center", children: [
2504
+ /* @__PURE__ */ jsx13("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
2505
+ /* @__PURE__ */ jsxs11("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: [
2506
+ /* @__PURE__ */ jsx13(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
2507
+ /* @__PURE__ */ jsx13(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
2508
+ /* @__PURE__ */ jsx13(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
1199
2509
  ] })
1200
- ] }) : /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
1201
- /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
1202
- /* @__PURE__ */ jsx11("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
2510
+ ] }) : /* @__PURE__ */ jsxs11("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
2511
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
2512
+ /* @__PURE__ */ jsx13("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
1203
2513
  ] })
1204
2514
  ] }) })
1205
2515
  ] })
1206
2516
  ] }),
1207
- /* @__PURE__ */ jsxs9(motion6.div, { animate: { width: isRightCollapsed ? 60 : 320 }, className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", children: [
1208
- /* @__PURE__ */ jsxs9("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
1209
- /* @__PURE__ */ jsx11("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup"].map((tab) => /* @__PURE__ */ jsx11("button", { onClick: () => {
2517
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : 320, transition: "width 0.2s" }, children: [
2518
+ /* @__PURE__ */ jsxs11("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
2519
+ /* @__PURE__ */ jsx13("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync"].map((tab) => /* @__PURE__ */ jsx13("button", { onClick: () => {
1210
2520
  setActiveTab(tab);
1211
2521
  setIsRightCollapsed(false);
1212
- }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : "settings" }) }, tab)) }),
1213
- /* @__PURE__ */ jsx11("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
2522
+ }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : "cloud_sync" }) }, tab)) }),
2523
+ /* @__PURE__ */ jsx13("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
1214
2524
  ] }),
1215
- !isRightCollapsed && /* @__PURE__ */ jsx11("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ jsxs9(AnimatePresence2, { mode: "wait", children: [
1216
- activeTab === "history" && /* @__PURE__ */ jsx11(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }, "history"),
1217
- activeTab === "gallery" && /* @__PURE__ */ jsx11(
2525
+ !isRightCollapsed && /* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-hidden relative", children: [
2526
+ activeTab === "history" && /* @__PURE__ */ jsx13(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
2527
+ activeTab === "gallery" && /* @__PURE__ */ jsx13(
1218
2528
  MediaLibrary,
1219
2529
  {
1220
2530
  items: galleryItems,
1221
2531
  onImport: async () => {
1222
2532
  const media = await onSelectMedia();
1223
2533
  if (!media?.length) return;
1224
- const newItems = media.map((m) => ({
1225
- id: crypto.randomUUID(),
1226
- nodeId: "1",
1227
- base64: `data:${m.mimeType};base64,${m.base64}`,
1228
- mediaId: m.mediaId,
1229
- prompt: m.name || "Importiert",
1230
- timestamp: Date.now(),
1231
- status: "done",
1232
- tags: [],
1233
- type: "import"
1234
- }));
1235
- setGalleryItems((prev) => [...newItems, ...prev]);
2534
+ 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]);
1236
2535
  },
1237
2536
  onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
1238
2537
  onSelect: setCurrentResult,
1239
2538
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
1240
- },
1241
- "gallery"
2539
+ }
1242
2540
  ),
1243
- activeTab === "inspect" && /* @__PURE__ */ jsx11(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1244
- activeTab === "setup" && /* @__PURE__ */ jsx11(
1245
- SetupPanel,
2541
+ activeTab === "inspect" && /* @__PURE__ */ jsx13(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
2542
+ activeTab === "setup" && /* @__PURE__ */ jsx13(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2543
+ activeTab === "sync" && /* @__PURE__ */ jsx13(
2544
+ ProjectSyncTab,
1246
2545
  {
1247
2546
  onProjectExport: handleProjectExport,
1248
- onProjectImport: handleProjectImport,
2547
+ onProjectImport: (f) => handleProjectImport(f),
1249
2548
  onWorkspaceImport: handleWorkspaceImport,
1250
- projectActionState
1251
- },
1252
- "setup"
2549
+ projectActionState,
2550
+ serverProjects: onFetchServerProjects ? serverProjects : void 0,
2551
+ onServerSave: onServerSave ? handleServerSave : void 0,
2552
+ onServerLoad: onServerLoad ? handleServerLoad : void 0,
2553
+ onServerDelete: onServerDelete ? handleServerDelete : void 0,
2554
+ onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
2555
+ onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
2556
+ onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
2557
+ }
1253
2558
  )
1254
- ] }) })
2559
+ ] })
1255
2560
  ] })
1256
2561
  ] });
1257
2562
  }
1258
2563
  export {
1259
2564
  AvatarArchitectApp,
2565
+ CollapsibleCard,
1260
2566
  CompactDropdown,
1261
2567
  FaToolsBadge,
1262
2568
  GLOBAL_STYLES,
@@ -1265,6 +2571,7 @@ export {
1265
2571
  ListView,
1266
2572
  MediaLibrary,
1267
2573
  PillButton,
2574
+ ProjectSyncTab,
1268
2575
  PromptTab,
1269
2576
  SectionLabel,
1270
2577
  SetupPanel,