@rslsp1/fa-app-tools 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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.2";
311
+ declare const LIB_VERSION = "1.1.4";
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.2";
311
+ declare const LIB_VERSION = "1.1.4";
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, {
@@ -1704,6 +1721,20 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1704
1721
  const [seedMode, setSeedMode] = (0, import_react14.useState)("random");
1705
1722
  const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react14.useState)(false);
1706
1723
  const [isRightCollapsed, setIsRightCollapsed] = (0, import_react14.useState)(false);
1724
+ const [leftPanelWidth, setLeftPanelWidth] = (0, import_react14.useState)(() => {
1725
+ try {
1726
+ return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
1727
+ } catch {
1728
+ return 260;
1729
+ }
1730
+ });
1731
+ const [rightPanelWidth, setRightPanelWidth] = (0, import_react14.useState)(() => {
1732
+ try {
1733
+ return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
1734
+ } catch {
1735
+ return 320;
1736
+ }
1737
+ });
1707
1738
  const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react14.useState)(false);
1708
1739
  const [projectActionState, setProjectActionState] = (0, import_react14.useState)("idle");
1709
1740
  const syncServerDataRef = (0, import_react14.useRef)(null);
@@ -1719,6 +1750,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1719
1750
  });
1720
1751
  const [activeReferenceId, setActiveReferenceId] = (0, import_react14.useState)(null);
1721
1752
  const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react14.useState)(null);
1753
+ const [isScanningImage, setIsScanningImage] = (0, import_react14.useState)(false);
1722
1754
  const [touchStartX, setTouchStartX] = (0, import_react14.useState)(null);
1723
1755
  const [isFullscreen, setIsFullscreen] = (0, import_react14.useState)(false);
1724
1756
  const [zoomScale, setZoomScale] = (0, import_react14.useState)(1);
@@ -1788,6 +1820,61 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1788
1820
  setActiveReferenceId(null);
1789
1821
  setActiveReferenceThumbnail(null);
1790
1822
  };
1823
+ const startLeftResize = (e) => {
1824
+ e.preventDefault();
1825
+ const startX = e.clientX;
1826
+ const startW = leftPanelWidth;
1827
+ const onMove = (ev) => {
1828
+ const w = Math.max(180, Math.min(520, startW + ev.clientX - startX));
1829
+ setLeftPanelWidth(w);
1830
+ try {
1831
+ localStorage.setItem("aa-left-width", String(w));
1832
+ } catch {
1833
+ }
1834
+ };
1835
+ const onUp = () => {
1836
+ document.removeEventListener("mousemove", onMove);
1837
+ document.removeEventListener("mouseup", onUp);
1838
+ };
1839
+ document.addEventListener("mousemove", onMove);
1840
+ document.addEventListener("mouseup", onUp);
1841
+ };
1842
+ const startRightResize = (e) => {
1843
+ e.preventDefault();
1844
+ const startX = e.clientX;
1845
+ const startW = rightPanelWidth;
1846
+ const onMove = (ev) => {
1847
+ const w = Math.max(180, Math.min(520, startW - (ev.clientX - startX)));
1848
+ setRightPanelWidth(w);
1849
+ try {
1850
+ localStorage.setItem("aa-right-width", String(w));
1851
+ } catch {
1852
+ }
1853
+ };
1854
+ const onUp = () => {
1855
+ document.removeEventListener("mousemove", onMove);
1856
+ document.removeEventListener("mouseup", onUp);
1857
+ };
1858
+ document.addEventListener("mousemove", onMove);
1859
+ document.addEventListener("mouseup", onUp);
1860
+ };
1861
+ const handleScanImage = async (file) => {
1862
+ setIsScanningImage(true);
1863
+ try {
1864
+ const base64 = await new Promise((resolve, reject) => {
1865
+ const reader = new FileReader();
1866
+ reader.onloadend = () => resolve(reader.result.split(",")[1]);
1867
+ reader.onerror = reject;
1868
+ reader.readAsDataURL(file);
1869
+ });
1870
+ const prompt = await onGeneratePrompt("", "scan", [{ base64, mimeType: file.type || "image/png" }]);
1871
+ setActivePrompt(prompt);
1872
+ } catch (err) {
1873
+ console.error("Scan fehlgeschlagen:", err);
1874
+ } finally {
1875
+ setIsScanningImage(false);
1876
+ }
1877
+ };
1791
1878
  const currentIndex = (0, import_react14.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1792
1879
  const goToPrev = (0, import_react14.useCallback)(() => {
1793
1880
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
@@ -2347,7 +2434,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2347
2434
  } })
2348
2435
  ] })
2349
2436
  ] }),
2350
- mobileTab === "tools" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
2437
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
2351
2438
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
2352
2439
  workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => {
2353
2440
  setLeftTab("prompt");
@@ -2404,10 +2491,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2404
2491
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2405
2492
  }
2406
2493
  ) }),
2407
- 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) => {
2494
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2408
2495
  handleGenerateImage(prompt);
2409
2496
  setMobileTab("stage");
2410
- }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2497
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
2411
2498
  activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2412
2499
  activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2413
2500
  ProjectSyncTab,
@@ -2543,7 +2630,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2543
2630
  }
2544
2631
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
2545
2632
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
2546
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : 260, transition: "width 0.2s" }, children: [
2633
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
2547
2634
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
2548
2635
  !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 gap-1", children: [
2549
2636
  workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
@@ -2583,9 +2670,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2583
2670
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2584
2671
  }
2585
2672
  ) }),
2586
- 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 })
2673
+ 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 })
2587
2674
  ] })
2588
2675
  ] }),
2676
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
2589
2677
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
2590
2678
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
2591
2679
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5", children: [
@@ -2639,7 +2727,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2639
2727
  ] }) })
2640
2728
  ] })
2641
2729
  ] }),
2642
- /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : 320, transition: "width 0.2s" }, children: [
2730
+ !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
2731
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
2643
2732
  /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
2644
2733
  /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
2645
2734
  setActiveTab(tab);
@@ -2687,7 +2776,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2687
2776
  }
2688
2777
 
2689
2778
  // src/index.ts
2690
- var LIB_VERSION = "1.1.2";
2779
+ var LIB_VERSION = "1.1.4";
2691
2780
  // Annotate the CommonJS export names for ESM import in node:
2692
2781
  0 && (module.exports = {
2693
2782
  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, {
@@ -1638,6 +1655,20 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1638
1655
  const [seedMode, setSeedMode] = useState6("random");
1639
1656
  const [isLeftCollapsed, setIsLeftCollapsed] = useState6(false);
1640
1657
  const [isRightCollapsed, setIsRightCollapsed] = useState6(false);
1658
+ const [leftPanelWidth, setLeftPanelWidth] = useState6(() => {
1659
+ try {
1660
+ return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
1661
+ } catch {
1662
+ return 260;
1663
+ }
1664
+ });
1665
+ const [rightPanelWidth, setRightPanelWidth] = useState6(() => {
1666
+ try {
1667
+ return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
1668
+ } catch {
1669
+ return 320;
1670
+ }
1671
+ });
1641
1672
  const [isPromptCollapsed, setIsPromptCollapsed] = useState6(false);
1642
1673
  const [projectActionState, setProjectActionState] = useState6("idle");
1643
1674
  const syncServerDataRef = useRef6(null);
@@ -1653,6 +1684,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1653
1684
  });
1654
1685
  const [activeReferenceId, setActiveReferenceId] = useState6(null);
1655
1686
  const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState6(null);
1687
+ const [isScanningImage, setIsScanningImage] = useState6(false);
1656
1688
  const [touchStartX, setTouchStartX] = useState6(null);
1657
1689
  const [isFullscreen, setIsFullscreen] = useState6(false);
1658
1690
  const [zoomScale, setZoomScale] = useState6(1);
@@ -1722,6 +1754,61 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
1722
1754
  setActiveReferenceId(null);
1723
1755
  setActiveReferenceThumbnail(null);
1724
1756
  };
1757
+ const startLeftResize = (e) => {
1758
+ e.preventDefault();
1759
+ const startX = e.clientX;
1760
+ const startW = leftPanelWidth;
1761
+ const onMove = (ev) => {
1762
+ const w = Math.max(180, Math.min(520, startW + ev.clientX - startX));
1763
+ setLeftPanelWidth(w);
1764
+ try {
1765
+ localStorage.setItem("aa-left-width", String(w));
1766
+ } catch {
1767
+ }
1768
+ };
1769
+ const onUp = () => {
1770
+ document.removeEventListener("mousemove", onMove);
1771
+ document.removeEventListener("mouseup", onUp);
1772
+ };
1773
+ document.addEventListener("mousemove", onMove);
1774
+ document.addEventListener("mouseup", onUp);
1775
+ };
1776
+ const startRightResize = (e) => {
1777
+ e.preventDefault();
1778
+ const startX = e.clientX;
1779
+ const startW = rightPanelWidth;
1780
+ const onMove = (ev) => {
1781
+ const w = Math.max(180, Math.min(520, startW - (ev.clientX - startX)));
1782
+ setRightPanelWidth(w);
1783
+ try {
1784
+ localStorage.setItem("aa-right-width", String(w));
1785
+ } catch {
1786
+ }
1787
+ };
1788
+ const onUp = () => {
1789
+ document.removeEventListener("mousemove", onMove);
1790
+ document.removeEventListener("mouseup", onUp);
1791
+ };
1792
+ document.addEventListener("mousemove", onMove);
1793
+ document.addEventListener("mouseup", onUp);
1794
+ };
1795
+ const handleScanImage = async (file) => {
1796
+ setIsScanningImage(true);
1797
+ try {
1798
+ const base64 = await new Promise((resolve, reject) => {
1799
+ const reader = new FileReader();
1800
+ reader.onloadend = () => resolve(reader.result.split(",")[1]);
1801
+ reader.onerror = reject;
1802
+ reader.readAsDataURL(file);
1803
+ });
1804
+ const prompt = await onGeneratePrompt("", "scan", [{ base64, mimeType: file.type || "image/png" }]);
1805
+ setActivePrompt(prompt);
1806
+ } catch (err) {
1807
+ console.error("Scan fehlgeschlagen:", err);
1808
+ } finally {
1809
+ setIsScanningImage(false);
1810
+ }
1811
+ };
1725
1812
  const currentIndex = useMemo2(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
1726
1813
  const goToPrev = useCallback(() => {
1727
1814
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
@@ -2281,7 +2368,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2281
2368
  } })
2282
2369
  ] })
2283
2370
  ] }),
2284
- mobileTab === "tools" && /* @__PURE__ */ jsxs11("div", { className: "flex flex-col flex-1 min-h-0", children: [
2371
+ /* @__PURE__ */ jsxs11("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
2285
2372
  /* @__PURE__ */ jsxs11("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
2286
2373
  workspaceTags && /* @__PURE__ */ jsxs11("button", { onClick: () => {
2287
2374
  setLeftTab("prompt");
@@ -2338,10 +2425,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2338
2425
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2339
2426
  }
2340
2427
  ) }),
2341
- leftTab === "prompt" && workspaceTags && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2428
+ workspaceTags && /* @__PURE__ */ jsx13("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ jsx13(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
2342
2429
  handleGenerateImage(prompt);
2343
2430
  setMobileTab("stage");
2344
- }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
2431
+ }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
2345
2432
  activeTab === "setup" && /* @__PURE__ */ jsx13(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
2346
2433
  activeTab === "sync" && /* @__PURE__ */ jsx13(
2347
2434
  ProjectSyncTab,
@@ -2477,7 +2564,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2477
2564
  }
2478
2565
  return /* @__PURE__ */ jsxs11("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
2479
2566
  /* @__PURE__ */ jsx13("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ jsx13("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
2480
- /* @__PURE__ */ jsxs11("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : 260, transition: "width 0.2s" }, children: [
2567
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
2481
2568
  /* @__PURE__ */ jsxs11("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
2482
2569
  !isLeftCollapsed && /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 gap-1", children: [
2483
2570
  workspaceTags && /* @__PURE__ */ jsxs11("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
@@ -2517,9 +2604,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2517
2604
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
2518
2605
  }
2519
2606
  ) }),
2520
- 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 })
2607
+ 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 })
2521
2608
  ] })
2522
2609
  ] }),
2610
+ !isLeftCollapsed && /* @__PURE__ */ jsx13("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
2523
2611
  /* @__PURE__ */ jsxs11("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
2524
2612
  /* @__PURE__ */ jsxs11("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
2525
2613
  /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1.5", children: [
@@ -2573,7 +2661,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2573
2661
  ] }) })
2574
2662
  ] })
2575
2663
  ] }),
2576
- /* @__PURE__ */ jsxs11("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : 320, transition: "width 0.2s" }, children: [
2664
+ !isRightCollapsed && /* @__PURE__ */ jsx13("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
2665
+ /* @__PURE__ */ jsxs11("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
2577
2666
  /* @__PURE__ */ jsxs11("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
2578
2667
  /* @__PURE__ */ jsx13("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync"].map((tab) => /* @__PURE__ */ jsx13("button", { onClick: () => {
2579
2668
  setActiveTab(tab);
@@ -2621,7 +2710,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
2621
2710
  }
2622
2711
 
2623
2712
  // src/index.ts
2624
- var LIB_VERSION = "1.1.2";
2713
+ var LIB_VERSION = "1.1.4";
2625
2714
  export {
2626
2715
  AvatarArchitectApp,
2627
2716
  CollapsibleCard,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rslsp1/fa-app-tools",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Shared tools and hooks for Fine Art flow apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",