@rslsp1/fa-app-tools 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -93,13 +93,21 @@ declare function parsePromptResponse(raw: string): {
93
93
  prompt: string;
94
94
  feedback: string | null;
95
95
  };
96
+ interface FlowSdkImage {
97
+ base64: string;
98
+ mimeType: string;
99
+ }
96
100
  interface FlowSdk {
97
101
  generate: {
98
102
  image: (options: Record<string, any>) => Promise<{
99
103
  base64: string;
100
104
  mediaId: string;
101
105
  }>;
102
- text: (prompt: string, options?: Record<string, any>) => Promise<{
106
+ text: (prompt: string, options?: {
107
+ systemInstruction?: string;
108
+ thinkingLevel?: string;
109
+ images?: FlowSdkImage[];
110
+ }) => Promise<{
103
111
  text: string;
104
112
  }>;
105
113
  };
@@ -119,7 +127,7 @@ declare function createFlowServices(Flow: FlowSdk): {
119
127
  base64: string;
120
128
  mediaId: string;
121
129
  }>;
122
- generateText: (hierarchyText: string, mode?: "literal" | "creative") => Promise<string>;
130
+ generateText: (hierarchyText: string, mode?: "literal" | "creative" | "scan", images?: FlowSdkImage[]) => Promise<string>;
123
131
  };
124
132
  declare function interpretSdkError(err: any): Error;
125
133
 
@@ -233,7 +241,10 @@ interface AvatarArchitectAppProps {
233
241
  base64: string;
234
242
  mediaId?: string;
235
243
  }>;
236
- onGeneratePrompt: (text: string) => Promise<string>;
244
+ onGeneratePrompt: (text: string, mode?: string, images?: {
245
+ base64: string;
246
+ mimeType: string;
247
+ }[]) => Promise<string>;
237
248
  onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
238
249
  onSelectMedia: () => Promise<MediaItem[]>;
239
250
  buildInfo?: string;
@@ -297,6 +308,6 @@ interface ProjectSyncTabProps {
297
308
  }
298
309
  declare const ProjectSyncTab: React.FC<ProjectSyncTabProps>;
299
310
 
300
- declare const LIB_VERSION = "1.1.1";
311
+ declare const LIB_VERSION = "1.1.3";
301
312
 
302
313
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, LIB_VERSION, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedTag, SetupPanel, type SyncDiff, type TagOption, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildPromptTabPayload, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.d.ts CHANGED
@@ -93,13 +93,21 @@ declare function parsePromptResponse(raw: string): {
93
93
  prompt: string;
94
94
  feedback: string | null;
95
95
  };
96
+ interface FlowSdkImage {
97
+ base64: string;
98
+ mimeType: string;
99
+ }
96
100
  interface FlowSdk {
97
101
  generate: {
98
102
  image: (options: Record<string, any>) => Promise<{
99
103
  base64: string;
100
104
  mediaId: string;
101
105
  }>;
102
- text: (prompt: string, options?: Record<string, any>) => Promise<{
106
+ text: (prompt: string, options?: {
107
+ systemInstruction?: string;
108
+ thinkingLevel?: string;
109
+ images?: FlowSdkImage[];
110
+ }) => Promise<{
103
111
  text: string;
104
112
  }>;
105
113
  };
@@ -119,7 +127,7 @@ declare function createFlowServices(Flow: FlowSdk): {
119
127
  base64: string;
120
128
  mediaId: string;
121
129
  }>;
122
- generateText: (hierarchyText: string, mode?: "literal" | "creative") => Promise<string>;
130
+ generateText: (hierarchyText: string, mode?: "literal" | "creative" | "scan", images?: FlowSdkImage[]) => Promise<string>;
123
131
  };
124
132
  declare function interpretSdkError(err: any): Error;
125
133
 
@@ -233,7 +241,10 @@ interface AvatarArchitectAppProps {
233
241
  base64: string;
234
242
  mediaId?: string;
235
243
  }>;
236
- onGeneratePrompt: (text: string) => Promise<string>;
244
+ onGeneratePrompt: (text: string, mode?: string, images?: {
245
+ base64: string;
246
+ mimeType: string;
247
+ }[]) => Promise<string>;
237
248
  onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
238
249
  onSelectMedia: () => Promise<MediaItem[]>;
239
250
  buildInfo?: string;
@@ -297,6 +308,6 @@ interface ProjectSyncTabProps {
297
308
  }
298
309
  declare const ProjectSyncTab: React.FC<ProjectSyncTabProps>;
299
310
 
300
- declare const LIB_VERSION = "1.1.1";
311
+ declare const LIB_VERSION = "1.1.3";
301
312
 
302
313
  export { AvatarArchitectApp, type AvatarArchitectAppProps, CollapsibleCard, CompactDropdown, type ExtractedCharacter, FaToolsBadge, type FlowSdk, GLOBAL_STYLES, type Generation, HistoryPanel, InspectPanel, LIB_VERSION, ListView, type MediaItem, MediaLibrary, PillButton, type ProjectMeta, type ProjectSettings, ProjectSyncTab, PromptTab, SectionLabel, type SelectedTag, SetupPanel, type SyncDiff, type TagOption, type WorkspaceTags, buildFallbackPrompt, buildGenerationPrompt, buildImageGenerationOptions, buildPromptTabPayload, cleanAiResponse, createFlowServices, exportProjectToZip, formatTreeToMarkdown, getFormattedTimestamp, importProjectFromZip, injectXMPMetadata, interpretSdkError, parsePromptFile, parsePromptResponse, useKeyboardNavigation, useOnClickOutside };
package/dist/index.js CHANGED
@@ -428,7 +428,24 @@ function createFlowServices(Flow) {
428
428
  throw interpretSdkError(err);
429
429
  }
430
430
  },
431
- generateText: async (hierarchyText, mode = "creative") => {
431
+ generateText: async (hierarchyText, mode = "creative", images) => {
432
+ if (mode === "scan" && images?.length) {
433
+ try {
434
+ const response = await Flow.generate.text(
435
+ "Analyze this image and create a detailed image generation prompt that would recreate it. Focus on: subject, style, lighting, composition, color palette, mood, and technical quality. Output ONLY the prompt string, no explanations.",
436
+ {
437
+ thinkingLevel: "low",
438
+ systemInstruction: "You are an expert image analyst and prompt engineer. Always return ONLY the generation prompt, no headers, no quotes, no conversational filler.",
439
+ images
440
+ }
441
+ );
442
+ const result = response?.text?.trim() || "";
443
+ if (!result) throw new Error("Empty AI response");
444
+ return cleanAiResponse(result);
445
+ } catch (err) {
446
+ throw interpretSdkError(err);
447
+ }
448
+ }
432
449
  const prompt = buildGenerationPrompt(hierarchyText, mode);
433
450
  try {
434
451
  const response = await Flow.generate.text(prompt, {
@@ -1717,6 +1734,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1717
1734
  return false;
1718
1735
  }
1719
1736
  });
1737
+ const [activeReferenceId, setActiveReferenceId] = (0, import_react14.useState)(null);
1738
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react14.useState)(null);
1739
+ const [isScanningImage, setIsScanningImage] = (0, import_react14.useState)(false);
1720
1740
  const [touchStartX, setTouchStartX] = (0, import_react14.useState)(null);
1721
1741
  const [isFullscreen, setIsFullscreen] = (0, import_react14.useState)(false);
1722
1742
  const [zoomScale, setZoomScale] = (0, import_react14.useState)(1);
@@ -1773,6 +1793,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1773
1793
  } catch {
1774
1794
  }
1775
1795
  };
1796
+ const handleSelectReference = async () => {
1797
+ const media = await onSelectMedia();
1798
+ if (!media?.length) return;
1799
+ const first = media[0];
1800
+ if (first.mediaId) {
1801
+ setActiveReferenceId(first.mediaId);
1802
+ setActiveReferenceThumbnail(`data:${first.mimeType};base64,${first.base64}`);
1803
+ }
1804
+ };
1805
+ const clearReference = () => {
1806
+ setActiveReferenceId(null);
1807
+ setActiveReferenceThumbnail(null);
1808
+ };
1809
+ const handleScanImage = async (file) => {
1810
+ setIsScanningImage(true);
1811
+ try {
1812
+ const base64 = await new Promise((resolve, reject) => {
1813
+ const reader = new FileReader();
1814
+ reader.onloadend = () => resolve(reader.result.split(",")[1]);
1815
+ reader.onerror = reject;
1816
+ reader.readAsDataURL(file);
1817
+ });
1818
+ const prompt = await onGeneratePrompt("", "scan", [{ base64, mimeType: file.type || "image/png" }]);
1819
+ setActivePrompt(prompt);
1820
+ } catch (err) {
1821
+ console.error("Scan fehlgeschlagen:", err);
1822
+ } finally {
1823
+ setIsScanningImage(false);
1824
+ }
1825
+ };
1776
1826
  const currentIndex = (0, import_react14.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1777
1827
  const goToPrev = (0, import_react14.useCallback)(() => {
1778
1828
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
@@ -1814,7 +1864,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1814
1864
  if (!options.silent) setCurrentResult(newGen);
1815
1865
  if (seedMode === "random") setSeed(activeSeed);
1816
1866
  try {
1817
- const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId));
1867
+ const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId ?? activeReferenceId ?? void 0));
1818
1868
  const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
1819
1869
  setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
1820
1870
  setGalleryItems((prev) => [finishedGen, ...prev]);
@@ -2205,6 +2255,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2205
2255
  /* @__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" }] }),
2206
2256
  /* @__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" }] }),
2207
2257
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1" }),
2258
+ activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
2259
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
2260
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
2261
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2262
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: handleSelectReference, 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: "add_photo_alternate" }) }),
2208
2263
  /* @__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" }) }),
2209
2264
  /* @__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" }) })
2210
2265
  ] }),
@@ -2387,7 +2442,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2387
2442
  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) => {
2388
2443
  handleGenerateImage(prompt);
2389
2444
  setMobileTab("stage");
2390
- }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2445
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }),
2391
2446
  activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2392
2447
  activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2393
2448
  ProjectSyncTab,
@@ -2563,7 +2618,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2563
2618
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2564
2619
  }
2565
2620
  ) }),
2566
- 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 })
2621
+ 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, onScanImage: handleScanImage, isScanning: isScanningImage })
2567
2622
  ] })
2568
2623
  ] }),
2569
2624
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
@@ -2573,6 +2628,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2573
2628
  /* @__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" }] })
2574
2629
  ] }),
2575
2630
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
2631
+ activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
2632
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
2633
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
2634
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2635
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
2636
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
2637
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Ref" })
2638
+ ] }),
2576
2639
  /* @__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" }) }),
2577
2640
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
2578
2641
  ] })
@@ -2659,7 +2722,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2659
2722
  }
2660
2723
 
2661
2724
  // src/index.ts
2662
- var LIB_VERSION = "1.1.1";
2725
+ var LIB_VERSION = "1.1.3";
2663
2726
  // Annotate the CommonJS export names for ESM import in node:
2664
2727
  0 && (module.exports = {
2665
2728
  AvatarArchitectApp,
package/dist/index.mjs CHANGED
@@ -362,7 +362,24 @@ function createFlowServices(Flow) {
362
362
  throw interpretSdkError(err);
363
363
  }
364
364
  },
365
- generateText: async (hierarchyText, mode = "creative") => {
365
+ generateText: async (hierarchyText, mode = "creative", images) => {
366
+ if (mode === "scan" && images?.length) {
367
+ try {
368
+ const response = await Flow.generate.text(
369
+ "Analyze this image and create a detailed image generation prompt that would recreate it. Focus on: subject, style, lighting, composition, color palette, mood, and technical quality. Output ONLY the prompt string, no explanations.",
370
+ {
371
+ thinkingLevel: "low",
372
+ systemInstruction: "You are an expert image analyst and prompt engineer. Always return ONLY the generation prompt, no headers, no quotes, no conversational filler.",
373
+ images
374
+ }
375
+ );
376
+ const result = response?.text?.trim() || "";
377
+ if (!result) throw new Error("Empty AI response");
378
+ return cleanAiResponse(result);
379
+ } catch (err) {
380
+ throw interpretSdkError(err);
381
+ }
382
+ }
366
383
  const prompt = buildGenerationPrompt(hierarchyText, mode);
367
384
  try {
368
385
  const response = await Flow.generate.text(prompt, {
@@ -1651,6 +1668,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1651
1668
  return false;
1652
1669
  }
1653
1670
  });
1671
+ const [activeReferenceId, setActiveReferenceId] = useState6(null);
1672
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState6(null);
1673
+ const [isScanningImage, setIsScanningImage] = useState6(false);
1654
1674
  const [touchStartX, setTouchStartX] = useState6(null);
1655
1675
  const [isFullscreen, setIsFullscreen] = useState6(false);
1656
1676
  const [zoomScale, setZoomScale] = useState6(1);
@@ -1707,6 +1727,36 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1707
1727
  } catch {
1708
1728
  }
1709
1729
  };
1730
+ const handleSelectReference = async () => {
1731
+ const media = await onSelectMedia();
1732
+ if (!media?.length) return;
1733
+ const first = media[0];
1734
+ if (first.mediaId) {
1735
+ setActiveReferenceId(first.mediaId);
1736
+ setActiveReferenceThumbnail(`data:${first.mimeType};base64,${first.base64}`);
1737
+ }
1738
+ };
1739
+ const clearReference = () => {
1740
+ setActiveReferenceId(null);
1741
+ setActiveReferenceThumbnail(null);
1742
+ };
1743
+ const handleScanImage = async (file) => {
1744
+ setIsScanningImage(true);
1745
+ try {
1746
+ const base64 = await new Promise((resolve, reject) => {
1747
+ const reader = new FileReader();
1748
+ reader.onloadend = () => resolve(reader.result.split(",")[1]);
1749
+ reader.onerror = reject;
1750
+ reader.readAsDataURL(file);
1751
+ });
1752
+ const prompt = await onGeneratePrompt("", "scan", [{ base64, mimeType: file.type || "image/png" }]);
1753
+ setActivePrompt(prompt);
1754
+ } catch (err) {
1755
+ console.error("Scan fehlgeschlagen:", err);
1756
+ } finally {
1757
+ setIsScanningImage(false);
1758
+ }
1759
+ };
1710
1760
  const currentIndex = useMemo2(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1711
1761
  const goToPrev = useCallback(() => {
1712
1762
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
@@ -1748,7 +1798,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1748
1798
  if (!options.silent) setCurrentResult(newGen);
1749
1799
  if (seedMode === "random") setSeed(activeSeed);
1750
1800
  try {
1751
- const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId));
1801
+ const { base64, mediaId } = await onGenerateImage(buildImageGenerationOptions(promptToUse, aspectRatio, selectedModel, useReferenceId ?? activeReferenceId ?? void 0));
1752
1802
  const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
1753
1803
  setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
1754
1804
  setGalleryItems((prev) => [finishedGen, ...prev]);
@@ -2139,6 +2189,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2139
2189
  /* @__PURE__ */ jsx13(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
2140
2190
  /* @__PURE__ */ jsx13(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
2141
2191
  /* @__PURE__ */ jsx13("div", { className: "flex-1" }),
2192
+ activeReferenceThumbnail ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
2193
+ /* @__PURE__ */ jsx13("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
2194
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
2195
+ /* @__PURE__ */ jsx13("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2196
+ ] }) : /* @__PURE__ */ jsx13("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
2142
2197
  /* @__PURE__ */ jsx13("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
2143
2198
  /* @__PURE__ */ jsx13("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
2144
2199
  ] }),
@@ -2321,7 +2376,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2321
2376
  leftTab === "prompt" && workspaceTags && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2322
2377
  handleGenerateImage(prompt);
2323
2378
  setMobileTab("stage");
2324
- }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2379
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }),
2325
2380
  activeTab === "setup" && /* @__PURE__ */ jsx13(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2326
2381
  activeTab === "sync" && /* @__PURE__ */ jsx13(
2327
2382
  ProjectSyncTab,
@@ -2497,7 +2552,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2497
2552
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2498
2553
  }
2499
2554
  ) }),
2500
- leftTab === "prompt" && workspaceTags && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete })
2555
+ leftTab === "prompt" && workspaceTags && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
2501
2556
  ] })
2502
2557
  ] }),
2503
2558
  /* @__PURE__ */ jsxs11("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
@@ -2507,6 +2562,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2507
2562
  /* @__PURE__ */ jsx13(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
2508
2563
  ] }),
2509
2564
  /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
2565
+ activeReferenceThumbnail ? /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
2566
+ /* @__PURE__ */ jsx13("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
2567
+ /* @__PURE__ */ jsx13("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
2568
+ /* @__PURE__ */ jsx13("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
2569
+ ] }) : /* @__PURE__ */ jsxs11("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
2570
+ /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
2571
+ /* @__PURE__ */ jsx13("span", { children: "Ref" })
2572
+ ] }),
2510
2573
  /* @__PURE__ */ jsx13("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
2511
2574
  /* @__PURE__ */ jsx13(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
2512
2575
  ] })
@@ -2593,7 +2656,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2593
2656
  }
2594
2657
 
2595
2658
  // src/index.ts
2596
- var LIB_VERSION = "1.1.1";
2659
+ var LIB_VERSION = "1.1.3";
2597
2660
  export {
2598
2661
  AvatarArchitectApp,
2599
2662
  CollapsibleCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",