@rslsp1/fa-app-tools 1.0.0 → 1.0.2

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.d.mts CHANGED
@@ -4,11 +4,13 @@ import { Node, Edge } from '@xyflow/react';
4
4
 
5
5
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
6
6
 
7
+ interface TagOption {
8
+ label: string;
9
+ value: string;
10
+ }
7
11
  interface WorkspaceTags {
8
- by_category: Record<string, string[]>;
9
- all: Array<{
10
- label: string;
11
- value: string;
12
+ by_category: Record<string, TagOption[]>;
13
+ all: Array<TagOption & {
12
14
  category: string;
13
15
  }>;
14
16
  }
@@ -69,6 +71,11 @@ declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' |
69
71
  declare function buildFallbackPrompt(hierarchyText: string): string;
70
72
  declare function cleanAiResponse(text: string): string;
71
73
  declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
74
+ declare function buildPromptTabPayload(selectedValues: string[], instructions: string, treeText?: string): string;
75
+ declare function parsePromptResponse(raw: string): {
76
+ prompt: string;
77
+ feedback: string | null;
78
+ };
72
79
  declare function interpretSdkError(err: any): Error;
73
80
 
74
81
  interface ExtractedCharacter {
@@ -179,14 +186,22 @@ interface MediaItem {
179
186
  name?: string;
180
187
  }
181
188
  interface AvatarArchitectAppProps {
182
- onGenerateImage: (prompt: string, aspectRatio: string, model: string, referenceId?: string) => Promise<{
189
+ onGenerateImage: (options: Record<string, any>) => Promise<{
183
190
  base64: string;
184
191
  mediaId?: string;
185
192
  }>;
186
- onGeneratePrompt: (treeText: string) => Promise<string>;
193
+ onGeneratePrompt: (text: string) => Promise<string>;
187
194
  onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
188
195
  onSelectMedia: () => Promise<MediaItem[]>;
189
196
  }
190
197
  declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
191
198
 
192
- export { AvatarArchitectApp, type AvatarArchitectAppProps, CompactDropdown, type ExtractedCharacter, FaToolsBadge, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectSettings, SectionLabel, SetupPanel, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, cleanAiResponse, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, useKeyboardNavigation, useOnClickOutside };
199
+ interface PromptTabProps {
200
+ workspaceTags: WorkspaceTags;
201
+ onGenerate: (selectedValues: string[], instructions: string) => Promise<void>;
202
+ isGenerating: boolean;
203
+ feedback: string | null;
204
+ }
205
+ declare const PromptTab: React.FC<PromptTabProps>;
206
+
207
+ export { AvatarArchitectApp, type AvatarArchitectAppProps, CompactDropdown, type ExtractedCharacter, FaToolsBadge, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectSettings, PromptTab, SectionLabel, SetupPanel, type TagOption, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildPromptTabPayload, cleanAiResponse, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.d.ts CHANGED
@@ -4,11 +4,13 @@ import { Node, Edge } from '@xyflow/react';
4
4
 
5
5
  declare function useOnClickOutside(ref: RefObject<HTMLElement | null>, handler: (e: MouseEvent | TouchEvent) => void): void;
6
6
 
7
+ interface TagOption {
8
+ label: string;
9
+ value: string;
10
+ }
7
11
  interface WorkspaceTags {
8
- by_category: Record<string, string[]>;
9
- all: Array<{
10
- label: string;
11
- value: string;
12
+ by_category: Record<string, TagOption[]>;
13
+ all: Array<TagOption & {
12
14
  category: string;
13
15
  }>;
14
16
  }
@@ -69,6 +71,11 @@ declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' |
69
71
  declare function buildFallbackPrompt(hierarchyText: string): string;
70
72
  declare function cleanAiResponse(text: string): string;
71
73
  declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
74
+ declare function buildPromptTabPayload(selectedValues: string[], instructions: string, treeText?: string): string;
75
+ declare function parsePromptResponse(raw: string): {
76
+ prompt: string;
77
+ feedback: string | null;
78
+ };
72
79
  declare function interpretSdkError(err: any): Error;
73
80
 
74
81
  interface ExtractedCharacter {
@@ -179,14 +186,22 @@ interface MediaItem {
179
186
  name?: string;
180
187
  }
181
188
  interface AvatarArchitectAppProps {
182
- onGenerateImage: (prompt: string, aspectRatio: string, model: string, referenceId?: string) => Promise<{
189
+ onGenerateImage: (options: Record<string, any>) => Promise<{
183
190
  base64: string;
184
191
  mediaId?: string;
185
192
  }>;
186
- onGeneratePrompt: (treeText: string) => Promise<string>;
193
+ onGeneratePrompt: (text: string) => Promise<string>;
187
194
  onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
188
195
  onSelectMedia: () => Promise<MediaItem[]>;
189
196
  }
190
197
  declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
191
198
 
192
- export { AvatarArchitectApp, type AvatarArchitectAppProps, CompactDropdown, type ExtractedCharacter, FaToolsBadge, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectSettings, SectionLabel, SetupPanel, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, cleanAiResponse, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, useKeyboardNavigation, useOnClickOutside };
199
+ interface PromptTabProps {
200
+ workspaceTags: WorkspaceTags;
201
+ onGenerate: (selectedValues: string[], instructions: string) => Promise<void>;
202
+ isGenerating: boolean;
203
+ feedback: string | null;
204
+ }
205
+ declare const PromptTab: React.FC<PromptTabProps>;
206
+
207
+ export { AvatarArchitectApp, type AvatarArchitectAppProps, CompactDropdown, type ExtractedCharacter, FaToolsBadge, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectSettings, PromptTab, SectionLabel, SetupPanel, type TagOption, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildPromptTabPayload, cleanAiResponse, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.js CHANGED
@@ -39,11 +39,13 @@ __export(index_exports, {
39
39
  ListView: () => ListView,
40
40
  MediaLibrary: () => MediaLibrary,
41
41
  PillButton: () => PillButton,
42
+ PromptTab: () => PromptTab,
42
43
  SectionLabel: () => SectionLabel,
43
44
  SetupPanel: () => SetupPanel,
44
45
  buildFallbackPrompt: () => buildFallbackPrompt,
45
46
  buildGenerationPrompt: () => buildGenerationPrompt,
46
47
  buildImageGenerationOptions: () => buildImageGenerationOptions,
48
+ buildPromptTabPayload: () => buildPromptTabPayload,
47
49
  cleanAiResponse: () => cleanAiResponse,
48
50
  exportProjectToZip: () => exportProjectToZip,
49
51
  formatTreeToMarkdown: () => formatTreeToMarkdown,
@@ -52,6 +54,7 @@ __export(index_exports, {
52
54
  injectXMPMetadata: () => injectXMPMetadata,
53
55
  interpretSdkError: () => interpretSdkError,
54
56
  parsePromptFile: () => parsePromptFile,
57
+ parsePromptResponse: () => parsePromptResponse,
55
58
  useKeyboardNavigation: () => useKeyboardNavigation,
56
59
  useOnClickOutside: () => useOnClickOutside
57
60
  });
@@ -352,6 +355,33 @@ function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaI
352
355
  }
353
356
  return options;
354
357
  }
358
+ function buildPromptTabPayload(selectedValues, instructions, treeText) {
359
+ const parts = [];
360
+ if (treeText) parts.push(`HIERARCHY:
361
+ ${treeText}`);
362
+ if (selectedValues.length > 0) parts.push(`SELECTED TAGS:
363
+ ${selectedValues.join(", ")}`);
364
+ if (instructions.trim()) parts.push(`ADDITIONAL INSTRUCTIONS:
365
+ ${instructions.trim()}`);
366
+ return [
367
+ "Create a high-quality image generation prompt based on the following inputs. Translate everything to English.",
368
+ `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).`,
369
+ "Output ONLY the JSON object, no markdown, no wrapping.",
370
+ "",
371
+ ...parts
372
+ ].join("\n");
373
+ }
374
+ function parsePromptResponse(raw) {
375
+ const cleaned = raw.replace(/^```json\s*/i, "").replace(/^```\s*/i, "").replace(/```$/i, "").trim();
376
+ try {
377
+ const parsed = JSON.parse(cleaned);
378
+ if (typeof parsed?.prompt === "string") {
379
+ return { prompt: parsed.prompt.trim(), feedback: parsed.feedback ?? null };
380
+ }
381
+ } catch {
382
+ }
383
+ return { prompt: cleaned, feedback: null };
384
+ }
355
385
  function interpretSdkError(err) {
356
386
  const msg = err?.message || "Generierung fehlgeschlagen";
357
387
  if (msg.includes("PUBLIC_ERROR_SOMETHING_WENT_WRONG")) {
@@ -603,8 +633,8 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
603
633
  Object.entries(workspaceTags.by_category).map(([cat, tags]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-2", children: [
604
634
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[8px] font-bold text-white/30 uppercase tracking-widest px-1", children: cat }),
605
635
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1.5", children: tags.map((t) => {
606
- const isActive = (currentResult.tags || []).includes(t);
607
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onTagToggle?.(t), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t }, t);
636
+ const isActive = (currentResult.tags || []).includes(t.label);
637
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onTagToggle?.(t.label), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t.label }, t.label);
608
638
  }) })
609
639
  ] }, cat))
610
640
  ] })
@@ -816,11 +846,117 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
816
846
  }
817
847
 
818
848
  // src/components/AvatarArchitectApp.tsx
849
+ var import_react12 = require("react");
850
+ var import_react13 = require("motion/react");
851
+
852
+ // src/components/PromptTab.tsx
819
853
  var import_react10 = require("react");
820
854
  var import_react11 = require("motion/react");
821
855
  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);
860
+ const toggleTag = (label) => {
861
+ setSelectedLabels((prev) => {
862
+ const next = new Set(prev);
863
+ if (next.has(label)) next.delete(label);
864
+ else next.add(label);
865
+ return next;
866
+ });
867
+ };
868
+ const handleGenerate = () => {
869
+ const selectedValues = workspaceTags.all.filter((t) => selectedLabels.has(t.label)).map((t) => t.value);
870
+ onGenerate(selectedValues, instructions);
871
+ };
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: [
878
+ "Keine Tags geladen.",
879
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("br", {}),
880
+ "Importiere ein Workspace-JSON im Setup-Tab."
881
+ ] })
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",
888
+ {
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",
901
+ {
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"
906
+ }
907
+ )
908
+ ] })
909
+ ] }),
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",
914
+ {
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",
917
+ 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" })
921
+ ]
922
+ }
923
+ ),
924
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.AnimatePresence, { children: showFeedback && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
925
+ import_react11.motion.div,
926
+ {
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 })
932
+ }
933
+ ) })
934
+ ] }),
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)(
941
+ PillButton,
942
+ {
943
+ variant: "solid",
944
+ icon: "auto_fix_high",
945
+ loading: isGenerating,
946
+ disabled: selectedLabels.size === 0 && !instructions.trim(),
947
+ onClick: handleGenerate,
948
+ children: "Prompt bauen"
949
+ }
950
+ )
951
+ ] })
952
+ ] })
953
+ ] });
954
+ };
955
+
956
+ // src/components/AvatarArchitectApp.tsx
957
+ var import_jsx_runtime11 = require("react/jsx-runtime");
822
958
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
823
- (0, import_react10.useEffect)(() => {
959
+ (0, import_react12.useEffect)(() => {
824
960
  const id = "flow-styles";
825
961
  if (!document.getElementById(id)) {
826
962
  const style = document.createElement("style");
@@ -829,27 +965,31 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
829
965
  document.head.appendChild(style);
830
966
  }
831
967
  }, []);
832
- const [nodes, setNodes] = (0, import_react10.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
833
- const [edges, setEdges] = (0, import_react10.useState)([]);
834
- const [history, setHistory] = (0, import_react10.useState)([]);
835
- const [galleryItems, setGalleryItems] = (0, import_react10.useState)([]);
836
- const [activePrompt, setActivePrompt] = (0, import_react10.useState)("");
837
- const [isSynthesizing, setIsSynthesizing] = (0, import_react10.useState)(false);
838
- const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react10.useState)(0);
839
- const [currentResult, setCurrentResult] = (0, import_react10.useState)(null);
840
- const [focusedNodeId, setFocusedNodeId] = (0, import_react10.useState)(null);
841
- const [activeTab, setActiveTab] = (0, import_react10.useState)("history");
842
- const [aspectRatio, setAspectRatio] = (0, import_react10.useState)("1:1");
843
- const [selectedModel, setSelectedModel] = (0, import_react10.useState)("\u{1F34C} Nano Banana Pro");
844
- const [seed, setSeed] = (0, import_react10.useState)(Math.floor(Math.random() * 1e6));
845
- const [seedMode, setSeedMode] = (0, import_react10.useState)("random");
846
- const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react10.useState)(false);
847
- const [isRightCollapsed, setIsRightCollapsed] = (0, import_react10.useState)(false);
848
- const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react10.useState)(false);
849
- const [projectActionState, setProjectActionState] = (0, import_react10.useState)("idle");
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);
850
990
  const isGenerating = activeGenerationsCount > 0;
851
991
  useKeyboardNavigation(history, currentResult, setCurrentResult);
852
- const getSubtreeFormat = (0, import_react10.useCallback)((nodeId, depth = 0) => {
992
+ const getSubtreeFormat = (0, import_react12.useCallback)((nodeId, depth = 0) => {
853
993
  const node = nodes.find((n) => n.id === nodeId);
854
994
  if (!node) return "";
855
995
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -857,7 +997,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
857
997
  return `${indent}- ${node.data.label || "(unbenannt)"}
858
998
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
859
999
  }, [nodes, edges]);
860
- const activePath = (0, import_react10.useMemo)(() => {
1000
+ const activePath = (0, import_react12.useMemo)(() => {
861
1001
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
862
1002
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
863
1003
  let currId = focusedNodeId;
@@ -890,7 +1030,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
890
1030
  if (!options.silent) setCurrentResult(newGen);
891
1031
  if (seedMode === "random") setSeed(activeSeed);
892
1032
  try {
893
- const { base64, mediaId } = await onGenerateImage(promptToUse, aspectRatio, selectedModel, useReferenceId);
1033
+ const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId));
894
1034
  const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
895
1035
  setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
896
1036
  setGalleryItems((prev) => [finishedGen, ...prev]);
@@ -910,7 +1050,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
910
1050
  const handleSynthesizePrompt = async (nodeId, autoGenerate = false) => {
911
1051
  setIsSynthesizing(true);
912
1052
  try {
913
- const prompt = await onGeneratePrompt(getSubtreeFormat(nodeId));
1053
+ const payload = buildGenerationPrompt(getSubtreeFormat(nodeId));
1054
+ const raw = await onGeneratePrompt(payload);
1055
+ const prompt = cleanAiResponse(raw);
914
1056
  setActivePrompt(prompt);
915
1057
  setFocusedNodeId(nodeId);
916
1058
  if (autoGenerate) handleGenerateImage(prompt, void 0, nodeId);
@@ -920,6 +1062,22 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
920
1062
  setIsSynthesizing(false);
921
1063
  }
922
1064
  };
1065
+ const handlePromptTabGenerate = async (selectedValues, instructions) => {
1066
+ setIsPromptTabGenerating(true);
1067
+ setPromptFeedback(null);
1068
+ try {
1069
+ const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : void 0;
1070
+ const payload = buildPromptTabPayload(selectedValues, instructions, treeText);
1071
+ const raw = await onGeneratePrompt(payload);
1072
+ const { prompt, feedback } = parsePromptResponse(raw);
1073
+ setActivePrompt(prompt);
1074
+ setPromptFeedback(feedback);
1075
+ } catch {
1076
+ setActivePrompt("Synthese-Fehler");
1077
+ } finally {
1078
+ setIsPromptTabGenerating(false);
1079
+ }
1080
+ };
923
1081
  const handleDownloadSingle = async () => {
924
1082
  if (!currentResult?.base64) return;
925
1083
  try {
@@ -975,58 +1133,100 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
975
1133
  const root = JSON.parse(ev.target?.result);
976
1134
  const rawTags = root.tags || root;
977
1135
  if (!rawTags?.by_category) return;
1136
+ const filtered = {};
1137
+ const EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
1138
+ Object.entries(rawTags.by_category).forEach(([cat, items]) => {
1139
+ if (EXCLUDED.includes(cat) || !Array.isArray(items)) return;
1140
+ filtered[cat] = items.filter((t) => t && typeof t === "object" && t.label && t.value);
1141
+ });
1142
+ const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED.includes(t.category));
1143
+ setWorkspaceTags({ by_category: filtered, all: filteredAll });
978
1144
  } catch (err) {
979
1145
  console.error("Workspace Load failed", err);
980
1146
  }
981
1147
  };
982
1148
  reader.readAsText(file);
983
1149
  };
984
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", children: [
985
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react11.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: [
986
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "h-14 px-4 border-b border-white/5 flex items-center justify-between shrink-0", children: [
987
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[10px] font-bold uppercase text-white/40", children: "Hierarchie" }),
988
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
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",
1156
+ {
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
+ ]
1163
+ }
1164
+ ),
1165
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1166
+ "button",
1167
+ {
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"}`,
1170
+ children: [
1171
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
1172
+ "Prompt"
1173
+ ]
1174
+ }
1175
+ )
1176
+ ] }),
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" })
989
1178
  ] }),
990
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
991
- ListView,
992
- {
993
- nodes,
994
- edges,
995
- onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
996
- onAddChild: (pId) => {
997
- const newId = crypto.randomUUID();
998
- setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
999
- setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
1000
- setFocusedNodeId(newId);
1001
- return newId;
1002
- },
1003
- onDeleteNode: (id) => {
1004
- setNodes((nds) => nds.filter((n) => n.id !== id));
1005
- setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
1006
- },
1007
- onFocus: setFocusedNodeId,
1008
- focusedNodeId,
1009
- activePath,
1010
- onGenerate: handleSynthesizePrompt,
1011
- onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
1012
- isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
1013
- }
1014
- )
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)(
1181
+ ListView,
1182
+ {
1183
+ nodes,
1184
+ edges,
1185
+ onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
1186
+ onAddChild: (pId) => {
1187
+ const newId = crypto.randomUUID();
1188
+ setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
1189
+ setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
1190
+ setFocusedNodeId(newId);
1191
+ return newId;
1192
+ },
1193
+ onDeleteNode: (id) => {
1194
+ setNodes((nds) => nds.filter((n) => n.id !== id));
1195
+ setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
1196
+ },
1197
+ onFocus: setFocusedNodeId,
1198
+ focusedNodeId,
1199
+ activePath,
1200
+ onGenerate: handleSynthesizePrompt,
1201
+ onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
1202
+ isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
1203
+ }
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
+ ] }) })
1015
1215
  ] }),
1016
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
1017
- /* @__PURE__ */ (0, import_jsx_runtime10.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: [
1018
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1.5", children: [
1019
- /* @__PURE__ */ (0, import_jsx_runtime10.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" }] }),
1020
- /* @__PURE__ */ (0, import_jsx_runtime10.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" }] })
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" }] })
1021
1221
  ] }),
1022
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
1024
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
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" })
1025
1225
  ] })
1026
1226
  ] }),
1027
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
1028
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.AnimatePresence, { children: !isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.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_runtime10.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
1029
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
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)(
1030
1230
  "textarea",
1031
1231
  {
1032
1232
  value: activePrompt,
@@ -1035,48 +1235,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1035
1235
  placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
1036
1236
  }
1037
1237
  ),
1038
- activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime10.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_runtime10.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
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" }) })
1039
1239
  ] }) }) }),
1040
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.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: [
1041
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime10.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: [
1042
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
1043
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
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..." })
1044
1244
  ] }),
1045
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
1046
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
1047
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
1048
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
1049
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
1050
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
1051
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
1052
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
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 })
1053
1253
  ] }),
1054
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
1055
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
1056
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
1057
- /* @__PURE__ */ (0, import_jsx_runtime10.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: [
1058
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
1059
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
1060
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
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" })
1061
1261
  ] })
1062
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
1063
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
1064
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
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" })
1065
1265
  ] })
1066
1266
  ] }) })
1067
1267
  ] })
1068
1268
  ] }),
1069
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react11.motion.div, { animate: { width: isRightCollapsed ? 60 : 320 }, className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", children: [
1070
- /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
1071
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { onClick: () => {
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: () => {
1072
1272
  setActiveTab(tab);
1073
1273
  setIsRightCollapsed(false);
1074
- }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : "settings" }) }, tab)) }),
1075
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
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" }) })
1076
1276
  ] }),
1077
- !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react11.AnimatePresence, { mode: "wait", children: [
1078
- activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }, "history"),
1079
- activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
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)(
1080
1280
  MediaLibrary,
1081
1281
  {
1082
1282
  items: galleryItems,
@@ -1102,8 +1302,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1102
1302
  },
1103
1303
  "gallery"
1104
1304
  ),
1105
- activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1106
- activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1305
+ activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1306
+ activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1107
1307
  SetupPanel,
1108
1308
  {
1109
1309
  onProjectExport: handleProjectExport,
@@ -1128,11 +1328,13 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1128
1328
  ListView,
1129
1329
  MediaLibrary,
1130
1330
  PillButton,
1331
+ PromptTab,
1131
1332
  SectionLabel,
1132
1333
  SetupPanel,
1133
1334
  buildFallbackPrompt,
1134
1335
  buildGenerationPrompt,
1135
1336
  buildImageGenerationOptions,
1337
+ buildPromptTabPayload,
1136
1338
  cleanAiResponse,
1137
1339
  exportProjectToZip,
1138
1340
  formatTreeToMarkdown,
@@ -1141,6 +1343,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1141
1343
  injectXMPMetadata,
1142
1344
  interpretSdkError,
1143
1345
  parsePromptFile,
1346
+ parsePromptResponse,
1144
1347
  useKeyboardNavigation,
1145
1348
  useOnClickOutside
1146
1349
  });
package/dist/index.mjs CHANGED
@@ -293,6 +293,33 @@ function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaI
293
293
  }
294
294
  return options;
295
295
  }
296
+ function buildPromptTabPayload(selectedValues, instructions, treeText) {
297
+ const parts = [];
298
+ if (treeText) parts.push(`HIERARCHY:
299
+ ${treeText}`);
300
+ if (selectedValues.length > 0) parts.push(`SELECTED TAGS:
301
+ ${selectedValues.join(", ")}`);
302
+ if (instructions.trim()) parts.push(`ADDITIONAL INSTRUCTIONS:
303
+ ${instructions.trim()}`);
304
+ return [
305
+ "Create a high-quality image generation prompt based on the following inputs. Translate everything to English.",
306
+ `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).`,
307
+ "Output ONLY the JSON object, no markdown, no wrapping.",
308
+ "",
309
+ ...parts
310
+ ].join("\n");
311
+ }
312
+ function parsePromptResponse(raw) {
313
+ const cleaned = raw.replace(/^```json\s*/i, "").replace(/^```\s*/i, "").replace(/```$/i, "").trim();
314
+ try {
315
+ const parsed = JSON.parse(cleaned);
316
+ if (typeof parsed?.prompt === "string") {
317
+ return { prompt: parsed.prompt.trim(), feedback: parsed.feedback ?? null };
318
+ }
319
+ } catch {
320
+ }
321
+ return { prompt: cleaned, feedback: null };
322
+ }
296
323
  function interpretSdkError(err) {
297
324
  const msg = err?.message || "Generierung fehlgeschlagen";
298
325
  if (msg.includes("PUBLIC_ERROR_SOMETHING_WENT_WRONG")) {
@@ -544,8 +571,8 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
544
571
  Object.entries(workspaceTags.by_category).map(([cat, tags]) => /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
545
572
  /* @__PURE__ */ jsx6("span", { className: "text-[8px] font-bold text-white/30 uppercase tracking-widest px-1", children: cat }),
546
573
  /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1.5", children: tags.map((t) => {
547
- const isActive = (currentResult.tags || []).includes(t);
548
- return /* @__PURE__ */ jsx6("button", { onClick: () => onTagToggle?.(t), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t }, t);
574
+ const isActive = (currentResult.tags || []).includes(t.label);
575
+ return /* @__PURE__ */ jsx6("button", { onClick: () => onTagToggle?.(t.label), className: `px-2.5 py-1 rounded-lg text-[8px] font-bold uppercase transition-all border ${isActive ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_10px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/20 hover:text-white/50 hover:bg-white/10"}`, children: t.label }, t.label);
549
576
  }) })
550
577
  ] }, cat))
551
578
  ] })
@@ -757,9 +784,115 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
757
784
  }
758
785
 
759
786
  // src/components/AvatarArchitectApp.tsx
760
- import { useState as useState3, useCallback, useMemo, useEffect as useEffect4 } from "react";
787
+ import { useState as useState4, useCallback, useMemo, useEffect as useEffect4 } from "react";
788
+ import { motion as motion6, AnimatePresence as AnimatePresence2 } from "motion/react";
789
+
790
+ // src/components/PromptTab.tsx
791
+ import { useState as useState3 } from "react";
761
792
  import { motion as motion5, AnimatePresence } from "motion/react";
762
793
  import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
794
+ var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
795
+ const [selectedLabels, setSelectedLabels] = useState3(/* @__PURE__ */ new Set());
796
+ const [instructions, setInstructions] = useState3("");
797
+ const [showFeedback, setShowFeedback] = useState3(true);
798
+ const toggleTag = (label) => {
799
+ setSelectedLabels((prev) => {
800
+ const next = new Set(prev);
801
+ if (next.has(label)) next.delete(label);
802
+ else next.add(label);
803
+ return next;
804
+ });
805
+ };
806
+ const handleGenerate = () => {
807
+ const selectedValues = workspaceTags.all.filter((t) => selectedLabels.has(t.label)).map((t) => t.value);
808
+ onGenerate(selectedValues, instructions);
809
+ };
810
+ const categories = Object.entries(workspaceTags.by_category).filter(([, tags]) => tags.length > 0);
811
+ return /* @__PURE__ */ jsxs8("div", { className: "absolute inset-0 flex flex-col overflow-hidden", children: [
812
+ /* @__PURE__ */ jsxs8("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 py-3 flex flex-col gap-4", children: [
813
+ categories.length === 0 ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
814
+ /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
815
+ /* @__PURE__ */ jsxs8("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
816
+ "Keine Tags geladen.",
817
+ /* @__PURE__ */ jsx10("br", {}),
818
+ "Importiere ein Workspace-JSON im Setup-Tab."
819
+ ] })
820
+ ] }) : categories.map(([cat, tags]) => /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
821
+ /* @__PURE__ */ jsx10(SectionLabel, { children: cat }),
822
+ /* @__PURE__ */ jsx10("div", { className: "flex flex-wrap gap-1", children: tags.map((t) => {
823
+ const active = selectedLabels.has(t.label);
824
+ return /* @__PURE__ */ jsx10(
825
+ "button",
826
+ {
827
+ onClick: () => toggleTag(t.label),
828
+ className: `px-2 py-1 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-all border ${active ? "bg-blue-500 border-blue-400 text-white shadow-[0_0_8px_rgba(59,130,246,0.3)]" : "bg-white/5 border-white/5 text-white/30 hover:text-white/60 hover:bg-white/10"}`,
829
+ children: t.label
830
+ },
831
+ t.label
832
+ );
833
+ }) })
834
+ ] }, cat)),
835
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2 mt-2", children: [
836
+ /* @__PURE__ */ jsx10(SectionLabel, { children: "Zus\xE4tzliche Anweisungen" }),
837
+ /* @__PURE__ */ jsx10(
838
+ "textarea",
839
+ {
840
+ value: instructions,
841
+ onChange: (e) => setInstructions(e.target.value),
842
+ placeholder: "Weitere Vorgaben f\xFCr den AI-Prompt...",
843
+ className: "w-full bg-white/5 border border-white/10 rounded-xl p-3 text-[10px] text-white/70 placeholder-white/20 resize-none h-20 outline-none focus:border-white/20 dark-scrollbar"
844
+ }
845
+ )
846
+ ] })
847
+ ] }),
848
+ /* @__PURE__ */ jsxs8("div", { className: "shrink-0 border-t border-white/5 p-3 flex flex-col gap-2", children: [
849
+ feedback && /* @__PURE__ */ jsxs8("div", { className: "flex flex-col", children: [
850
+ /* @__PURE__ */ jsxs8(
851
+ "button",
852
+ {
853
+ onClick: () => setShowFeedback((p) => !p),
854
+ className: "flex items-center gap-1.5 text-[8px] font-bold uppercase text-amber-400/60 hover:text-amber-400 transition-colors mb-1",
855
+ children: [
856
+ /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[12px]", children: "psychology" }),
857
+ "KI-Analyse",
858
+ /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[12px] ml-auto", children: showFeedback ? "expand_less" : "expand_more" })
859
+ ]
860
+ }
861
+ ),
862
+ /* @__PURE__ */ jsx10(AnimatePresence, { children: showFeedback && /* @__PURE__ */ jsx10(
863
+ motion5.div,
864
+ {
865
+ initial: { height: 0, opacity: 0 },
866
+ animate: { height: "auto", opacity: 1 },
867
+ exit: { height: 0, opacity: 0 },
868
+ className: "overflow-hidden",
869
+ children: /* @__PURE__ */ jsx10("p", { className: "text-[9px] text-amber-400/50 leading-relaxed bg-amber-500/5 border border-amber-500/10 rounded-xl p-2 italic", children: feedback })
870
+ }
871
+ ) })
872
+ ] }),
873
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-center justify-between", children: [
874
+ selectedLabels.size > 0 ? /* @__PURE__ */ jsxs8("span", { className: "text-[8px] text-white/30 font-bold uppercase", children: [
875
+ selectedLabels.size,
876
+ " Tags gew\xE4hlt"
877
+ ] }) : /* @__PURE__ */ jsx10("span", { className: "text-[8px] text-white/20 italic", children: "Keine Tags gew\xE4hlt" }),
878
+ /* @__PURE__ */ jsx10(
879
+ PillButton,
880
+ {
881
+ variant: "solid",
882
+ icon: "auto_fix_high",
883
+ loading: isGenerating,
884
+ disabled: selectedLabels.size === 0 && !instructions.trim(),
885
+ onClick: handleGenerate,
886
+ children: "Prompt bauen"
887
+ }
888
+ )
889
+ ] })
890
+ ] })
891
+ ] });
892
+ };
893
+
894
+ // src/components/AvatarArchitectApp.tsx
895
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
763
896
  function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
764
897
  useEffect4(() => {
765
898
  const id = "flow-styles";
@@ -770,24 +903,28 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
770
903
  document.head.appendChild(style);
771
904
  }
772
905
  }, []);
773
- const [nodes, setNodes] = useState3([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
774
- const [edges, setEdges] = useState3([]);
775
- const [history, setHistory] = useState3([]);
776
- const [galleryItems, setGalleryItems] = useState3([]);
777
- const [activePrompt, setActivePrompt] = useState3("");
778
- const [isSynthesizing, setIsSynthesizing] = useState3(false);
779
- const [activeGenerationsCount, setActiveGenerationsCount] = useState3(0);
780
- const [currentResult, setCurrentResult] = useState3(null);
781
- const [focusedNodeId, setFocusedNodeId] = useState3(null);
782
- const [activeTab, setActiveTab] = useState3("history");
783
- const [aspectRatio, setAspectRatio] = useState3("1:1");
784
- const [selectedModel, setSelectedModel] = useState3("\u{1F34C} Nano Banana Pro");
785
- const [seed, setSeed] = useState3(Math.floor(Math.random() * 1e6));
786
- const [seedMode, setSeedMode] = useState3("random");
787
- const [isLeftCollapsed, setIsLeftCollapsed] = useState3(false);
788
- const [isRightCollapsed, setIsRightCollapsed] = useState3(false);
789
- const [isPromptCollapsed, setIsPromptCollapsed] = useState3(false);
790
- const [projectActionState, setProjectActionState] = useState3("idle");
906
+ const [nodes, setNodes] = useState4([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
907
+ const [edges, setEdges] = useState4([]);
908
+ const [history, setHistory] = useState4([]);
909
+ const [galleryItems, setGalleryItems] = useState4([]);
910
+ const [activePrompt, setActivePrompt] = useState4("");
911
+ const [isSynthesizing, setIsSynthesizing] = useState4(false);
912
+ const [activeGenerationsCount, setActiveGenerationsCount] = useState4(0);
913
+ const [currentResult, setCurrentResult] = useState4(null);
914
+ const [focusedNodeId, setFocusedNodeId] = useState4(null);
915
+ const [leftTab, setLeftTab] = useState4("hierarchy");
916
+ const [promptFeedback, setPromptFeedback] = useState4(null);
917
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = useState4(false);
918
+ const [activeTab, setActiveTab] = useState4("history");
919
+ const [aspectRatio, setAspectRatio] = useState4("1:1");
920
+ const [selectedModel, setSelectedModel] = useState4("\u{1F34C} Nano Banana Pro");
921
+ const [seed, setSeed] = useState4(Math.floor(Math.random() * 1e6));
922
+ const [seedMode, setSeedMode] = useState4("random");
923
+ const [isLeftCollapsed, setIsLeftCollapsed] = useState4(false);
924
+ const [isRightCollapsed, setIsRightCollapsed] = useState4(false);
925
+ const [isPromptCollapsed, setIsPromptCollapsed] = useState4(false);
926
+ const [projectActionState, setProjectActionState] = useState4("idle");
927
+ const [workspaceTags, setWorkspaceTags] = useState4(null);
791
928
  const isGenerating = activeGenerationsCount > 0;
792
929
  useKeyboardNavigation(history, currentResult, setCurrentResult);
793
930
  const getSubtreeFormat = useCallback((nodeId, depth = 0) => {
@@ -831,7 +968,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
831
968
  if (!options.silent) setCurrentResult(newGen);
832
969
  if (seedMode === "random") setSeed(activeSeed);
833
970
  try {
834
- const { base64, mediaId } = await onGenerateImage(promptToUse, aspectRatio, selectedModel, useReferenceId);
971
+ const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId));
835
972
  const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
836
973
  setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
837
974
  setGalleryItems((prev) => [finishedGen, ...prev]);
@@ -851,7 +988,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
851
988
  const handleSynthesizePrompt = async (nodeId, autoGenerate = false) => {
852
989
  setIsSynthesizing(true);
853
990
  try {
854
- const prompt = await onGeneratePrompt(getSubtreeFormat(nodeId));
991
+ const payload = buildGenerationPrompt(getSubtreeFormat(nodeId));
992
+ const raw = await onGeneratePrompt(payload);
993
+ const prompt = cleanAiResponse(raw);
855
994
  setActivePrompt(prompt);
856
995
  setFocusedNodeId(nodeId);
857
996
  if (autoGenerate) handleGenerateImage(prompt, void 0, nodeId);
@@ -861,6 +1000,22 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
861
1000
  setIsSynthesizing(false);
862
1001
  }
863
1002
  };
1003
+ const handlePromptTabGenerate = async (selectedValues, instructions) => {
1004
+ setIsPromptTabGenerating(true);
1005
+ setPromptFeedback(null);
1006
+ try {
1007
+ const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : void 0;
1008
+ const payload = buildPromptTabPayload(selectedValues, instructions, treeText);
1009
+ const raw = await onGeneratePrompt(payload);
1010
+ const { prompt, feedback } = parsePromptResponse(raw);
1011
+ setActivePrompt(prompt);
1012
+ setPromptFeedback(feedback);
1013
+ } catch {
1014
+ setActivePrompt("Synthese-Fehler");
1015
+ } finally {
1016
+ setIsPromptTabGenerating(false);
1017
+ }
1018
+ };
864
1019
  const handleDownloadSingle = async () => {
865
1020
  if (!currentResult?.base64) return;
866
1021
  try {
@@ -916,58 +1071,100 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
916
1071
  const root = JSON.parse(ev.target?.result);
917
1072
  const rawTags = root.tags || root;
918
1073
  if (!rawTags?.by_category) return;
1074
+ const filtered = {};
1075
+ const EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
1076
+ Object.entries(rawTags.by_category).forEach(([cat, items]) => {
1077
+ if (EXCLUDED.includes(cat) || !Array.isArray(items)) return;
1078
+ filtered[cat] = items.filter((t) => t && typeof t === "object" && t.label && t.value);
1079
+ });
1080
+ const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED.includes(t.category));
1081
+ setWorkspaceTags({ by_category: filtered, all: filteredAll });
919
1082
  } catch (err) {
920
1083
  console.error("Workspace Load failed", err);
921
1084
  }
922
1085
  };
923
1086
  reader.readAsText(file);
924
1087
  };
925
- return /* @__PURE__ */ jsxs8("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", children: [
926
- /* @__PURE__ */ jsxs8(motion5.div, { animate: { width: isLeftCollapsed ? 48 : 260 }, className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", children: [
927
- /* @__PURE__ */ jsxs8("div", { className: "h-14 px-4 border-b border-white/5 flex items-center justify-between shrink-0", children: [
928
- !isLeftCollapsed && /* @__PURE__ */ jsx10("span", { className: "text-[10px] font-bold uppercase text-white/40", children: "Hierarchie" }),
929
- /* @__PURE__ */ jsx10("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
1088
+ return /* @__PURE__ */ jsxs9("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", children: [
1089
+ /* @__PURE__ */ jsxs9(motion6.div, { animate: { width: isLeftCollapsed ? 48 : 260 }, className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", children: [
1090
+ /* @__PURE__ */ jsxs9("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
1091
+ !isLeftCollapsed && /* @__PURE__ */ jsxs9("div", { className: "flex flex-1 gap-1", children: [
1092
+ /* @__PURE__ */ jsxs9(
1093
+ "button",
1094
+ {
1095
+ onClick: () => setLeftTab("hierarchy"),
1096
+ className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`,
1097
+ children: [
1098
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
1099
+ "Hierarchie"
1100
+ ]
1101
+ }
1102
+ ),
1103
+ workspaceTags && /* @__PURE__ */ jsxs9(
1104
+ "button",
1105
+ {
1106
+ onClick: () => setLeftTab("prompt"),
1107
+ className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`,
1108
+ children: [
1109
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
1110
+ "Prompt"
1111
+ ]
1112
+ }
1113
+ )
1114
+ ] }),
1115
+ /* @__PURE__ */ jsx11("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
930
1116
  ] }),
931
- !isLeftCollapsed && /* @__PURE__ */ jsx10(
932
- ListView,
933
- {
934
- nodes,
935
- edges,
936
- onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
937
- onAddChild: (pId) => {
938
- const newId = crypto.randomUUID();
939
- setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
940
- setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
941
- setFocusedNodeId(newId);
942
- return newId;
943
- },
944
- onDeleteNode: (id) => {
945
- setNodes((nds) => nds.filter((n) => n.id !== id));
946
- setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
947
- },
948
- onFocus: setFocusedNodeId,
949
- focusedNodeId,
950
- activePath,
951
- onGenerate: handleSynthesizePrompt,
952
- onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
953
- isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
954
- }
955
- )
1117
+ !isLeftCollapsed && /* @__PURE__ */ jsx11("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ jsxs9(AnimatePresence2, { mode: "wait", children: [
1118
+ leftTab === "hierarchy" && /* @__PURE__ */ jsx11(motion6.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ jsx11(
1119
+ ListView,
1120
+ {
1121
+ nodes,
1122
+ edges,
1123
+ onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
1124
+ onAddChild: (pId) => {
1125
+ const newId = crypto.randomUUID();
1126
+ setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
1127
+ setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
1128
+ setFocusedNodeId(newId);
1129
+ return newId;
1130
+ },
1131
+ onDeleteNode: (id) => {
1132
+ setNodes((nds) => nds.filter((n) => n.id !== id));
1133
+ setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
1134
+ },
1135
+ onFocus: setFocusedNodeId,
1136
+ focusedNodeId,
1137
+ activePath,
1138
+ onGenerate: handleSynthesizePrompt,
1139
+ onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
1140
+ isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
1141
+ }
1142
+ ) }, "hierarchy"),
1143
+ leftTab === "prompt" && workspaceTags && /* @__PURE__ */ jsx11(motion6.div, { className: "absolute inset-0", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, children: /* @__PURE__ */ jsx11(
1144
+ PromptTab,
1145
+ {
1146
+ workspaceTags,
1147
+ onGenerate: handlePromptTabGenerate,
1148
+ isGenerating: isPromptTabGenerating,
1149
+ feedback: promptFeedback
1150
+ }
1151
+ ) }, "prompt")
1152
+ ] }) })
956
1153
  ] }),
957
- /* @__PURE__ */ jsxs8("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
958
- /* @__PURE__ */ jsxs8("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
959
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1.5", children: [
960
- /* @__PURE__ */ jsx10(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
961
- /* @__PURE__ */ jsx10(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" }] })
1154
+ /* @__PURE__ */ jsxs9("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
1155
+ /* @__PURE__ */ jsxs9("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
1156
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1.5", children: [
1157
+ /* @__PURE__ */ jsx11(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
1158
+ /* @__PURE__ */ jsx11(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
962
1159
  ] }),
963
- /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
964
- /* @__PURE__ */ jsx10("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
965
- /* @__PURE__ */ jsx10(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
1160
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-2", children: [
1161
+ /* @__PURE__ */ jsx11("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
1162
+ /* @__PURE__ */ jsx11(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
966
1163
  ] })
967
1164
  ] }),
968
- /* @__PURE__ */ jsxs8("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
969
- /* @__PURE__ */ jsx10(AnimatePresence, { children: !isPromptCollapsed && /* @__PURE__ */ jsx10(motion5.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__ */ jsxs8("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
970
- /* @__PURE__ */ jsx10(
1165
+ /* @__PURE__ */ jsxs9("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
1166
+ /* @__PURE__ */ jsx11(AnimatePresence2, { children: !isPromptCollapsed && /* @__PURE__ */ jsx11(motion6.div, { initial: { height: 0 }, animate: { height: "auto" }, exit: { height: 0 }, className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxs9("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
1167
+ /* @__PURE__ */ jsx11(
971
1168
  "textarea",
972
1169
  {
973
1170
  value: activePrompt,
@@ -976,48 +1173,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
976
1173
  placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
977
1174
  }
978
1175
  ),
979
- activePrompt && !isSynthesizing && /* @__PURE__ */ jsx10("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__ */ jsx10("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
1176
+ activePrompt && !isSynthesizing && /* @__PURE__ */ jsx11("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
980
1177
  ] }) }) }),
981
- /* @__PURE__ */ jsx10("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs8("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: [
982
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs8("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: [
983
- /* @__PURE__ */ jsx10("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
984
- /* @__PURE__ */ jsx10("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
1178
+ /* @__PURE__ */ jsx11("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs9("div", { className: "h-full w-full max-w-4xl aspect-square rounded-3xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center group shadow-2xl", children: [
1179
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs9("div", { className: "absolute top-6 right-6 z-30 bg-black/60 backdrop-blur-md px-4 py-2 rounded-full border border-white/10 flex items-center gap-3", children: [
1180
+ /* @__PURE__ */ jsx11("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
1181
+ /* @__PURE__ */ jsx11("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
985
1182
  ] }),
986
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center gap-4", children: [
987
- /* @__PURE__ */ jsx10("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
988
- /* @__PURE__ */ jsx10("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
989
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs8("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
990
- /* @__PURE__ */ jsx10("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
991
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
992
- /* @__PURE__ */ jsx10("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
993
- /* @__PURE__ */ jsx10("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
1183
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center gap-4", children: [
1184
+ /* @__PURE__ */ jsx11("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
1185
+ /* @__PURE__ */ jsx11("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
1186
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs9("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
1187
+ /* @__PURE__ */ jsx11("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
1188
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
1189
+ /* @__PURE__ */ jsx11("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
1190
+ /* @__PURE__ */ jsx11("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
994
1191
  ] }),
995
- /* @__PURE__ */ jsx10(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
996
- ] }) : /* @__PURE__ */ jsxs8("div", { className: "h-full w-full relative flex items-center justify-center", children: [
997
- /* @__PURE__ */ jsx10("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
998
- /* @__PURE__ */ jsxs8("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: [
999
- /* @__PURE__ */ jsx10(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
1000
- /* @__PURE__ */ jsx10(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
1001
- /* @__PURE__ */ jsx10(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
1192
+ /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
1193
+ ] }) : /* @__PURE__ */ jsxs9("div", { className: "h-full w-full relative flex items-center justify-center", children: [
1194
+ /* @__PURE__ */ jsx11("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
1195
+ /* @__PURE__ */ jsxs9("div", { className: "absolute bottom-6 flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-4 group-hover:translate-y-0 z-20", children: [
1196
+ /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
1197
+ /* @__PURE__ */ jsx11(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
1198
+ /* @__PURE__ */ jsx11(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
1002
1199
  ] })
1003
- ] }) : /* @__PURE__ */ jsxs8("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
1004
- /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
1005
- /* @__PURE__ */ jsx10("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
1200
+ ] }) : /* @__PURE__ */ jsxs9("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
1201
+ /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
1202
+ /* @__PURE__ */ jsx11("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
1006
1203
  ] })
1007
1204
  ] }) })
1008
1205
  ] })
1009
1206
  ] }),
1010
- /* @__PURE__ */ jsxs8(motion5.div, { animate: { width: isRightCollapsed ? 60 : 320 }, className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", children: [
1011
- /* @__PURE__ */ jsxs8("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
1012
- /* @__PURE__ */ jsx10("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup"].map((tab) => /* @__PURE__ */ jsx10("button", { onClick: () => {
1207
+ /* @__PURE__ */ jsxs9(motion6.div, { animate: { width: isRightCollapsed ? 60 : 320 }, className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", children: [
1208
+ /* @__PURE__ */ jsxs9("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
1209
+ /* @__PURE__ */ jsx11("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup"].map((tab) => /* @__PURE__ */ jsx11("button", { onClick: () => {
1013
1210
  setActiveTab(tab);
1014
1211
  setIsRightCollapsed(false);
1015
- }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : "settings" }) }, tab)) }),
1016
- /* @__PURE__ */ jsx10("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx10("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
1212
+ }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : "settings" }) }, tab)) }),
1213
+ /* @__PURE__ */ jsx11("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx11("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
1017
1214
  ] }),
1018
- !isRightCollapsed && /* @__PURE__ */ jsx10("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ jsxs8(AnimatePresence, { mode: "wait", children: [
1019
- activeTab === "history" && /* @__PURE__ */ jsx10(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }, "history"),
1020
- activeTab === "gallery" && /* @__PURE__ */ jsx10(
1215
+ !isRightCollapsed && /* @__PURE__ */ jsx11("div", { className: "flex-1 overflow-hidden relative", children: /* @__PURE__ */ jsxs9(AnimatePresence2, { mode: "wait", children: [
1216
+ activeTab === "history" && /* @__PURE__ */ jsx11(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }, "history"),
1217
+ activeTab === "gallery" && /* @__PURE__ */ jsx11(
1021
1218
  MediaLibrary,
1022
1219
  {
1023
1220
  items: galleryItems,
@@ -1043,8 +1240,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1043
1240
  },
1044
1241
  "gallery"
1045
1242
  ),
1046
- activeTab === "inspect" && /* @__PURE__ */ jsx10(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1047
- activeTab === "setup" && /* @__PURE__ */ jsx10(
1243
+ activeTab === "inspect" && /* @__PURE__ */ jsx11(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
1244
+ activeTab === "setup" && /* @__PURE__ */ jsx11(
1048
1245
  SetupPanel,
1049
1246
  {
1050
1247
  onProjectExport: handleProjectExport,
@@ -1068,11 +1265,13 @@ export {
1068
1265
  ListView,
1069
1266
  MediaLibrary,
1070
1267
  PillButton,
1268
+ PromptTab,
1071
1269
  SectionLabel,
1072
1270
  SetupPanel,
1073
1271
  buildFallbackPrompt,
1074
1272
  buildGenerationPrompt,
1075
1273
  buildImageGenerationOptions,
1274
+ buildPromptTabPayload,
1076
1275
  cleanAiResponse,
1077
1276
  exportProjectToZip,
1078
1277
  formatTreeToMarkdown,
@@ -1081,6 +1280,7 @@ export {
1081
1280
  injectXMPMetadata,
1082
1281
  interpretSdkError,
1083
1282
  parsePromptFile,
1283
+ parsePromptResponse,
1084
1284
  useKeyboardNavigation,
1085
1285
  useOnClickOutside
1086
1286
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",