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