@rslsp1/fa-app-tools 1.0.2 → 1.0.4

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