@rslsp1/fa-app-tools 0.1.12 → 0.1.14
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 +27 -4
- package/dist/index.d.ts +27 -4
- package/dist/index.js +359 -0
- package/dist/index.mjs +353 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -65,6 +65,12 @@ declare function exportProjectToZip(nodes: any[], edges: any[], history: any[],
|
|
|
65
65
|
}>;
|
|
66
66
|
declare function importProjectFromZip(file: File): Promise<any>;
|
|
67
67
|
|
|
68
|
+
declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' | 'creative'): string;
|
|
69
|
+
declare function buildFallbackPrompt(hierarchyText: string): string;
|
|
70
|
+
declare function cleanAiResponse(text: string): string;
|
|
71
|
+
declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
|
|
72
|
+
declare function interpretSdkError(err: any): Error;
|
|
73
|
+
|
|
68
74
|
interface ExtractedCharacter {
|
|
69
75
|
name: string;
|
|
70
76
|
prompts: {
|
|
@@ -122,7 +128,7 @@ interface InspectPanelProps {
|
|
|
122
128
|
currentResult: Generation | null;
|
|
123
129
|
history: Generation[];
|
|
124
130
|
onSelect: (gen: Generation) => void;
|
|
125
|
-
workspaceTags
|
|
131
|
+
workspaceTags?: WorkspaceTags | null;
|
|
126
132
|
onTagToggle?: (tagName: string) => void;
|
|
127
133
|
}
|
|
128
134
|
declare const InspectPanel: React.FC<InspectPanelProps>;
|
|
@@ -160,10 +166,27 @@ interface ListViewProps {
|
|
|
160
166
|
onFocus: (id: string | null) => void;
|
|
161
167
|
activePath: Set<string>;
|
|
162
168
|
onGenerate: (id: string) => void;
|
|
163
|
-
onGenerateBranch
|
|
164
|
-
onGenerateSubtree
|
|
169
|
+
onGenerateBranch?: (id: string) => void;
|
|
170
|
+
onGenerateSubtree?: (id: string) => void;
|
|
165
171
|
isGeneratingNodeId: (id: string) => boolean;
|
|
166
172
|
}
|
|
167
173
|
declare function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }: ListViewProps): react_jsx_runtime.JSX.Element;
|
|
168
174
|
|
|
169
|
-
|
|
175
|
+
interface MediaItem {
|
|
176
|
+
base64: string;
|
|
177
|
+
mimeType: string;
|
|
178
|
+
mediaId?: string;
|
|
179
|
+
name?: string;
|
|
180
|
+
}
|
|
181
|
+
interface AvatarArchitectAppProps {
|
|
182
|
+
onGenerateImage: (prompt: string, aspectRatio: string, model: string, referenceId?: string) => Promise<{
|
|
183
|
+
base64: string;
|
|
184
|
+
mediaId?: string;
|
|
185
|
+
}>;
|
|
186
|
+
onGeneratePrompt: (treeText: string) => Promise<string>;
|
|
187
|
+
onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
|
|
188
|
+
onSelectMedia: () => Promise<MediaItem[]>;
|
|
189
|
+
}
|
|
190
|
+
declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
|
|
191
|
+
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -65,6 +65,12 @@ declare function exportProjectToZip(nodes: any[], edges: any[], history: any[],
|
|
|
65
65
|
}>;
|
|
66
66
|
declare function importProjectFromZip(file: File): Promise<any>;
|
|
67
67
|
|
|
68
|
+
declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' | 'creative'): string;
|
|
69
|
+
declare function buildFallbackPrompt(hierarchyText: string): string;
|
|
70
|
+
declare function cleanAiResponse(text: string): string;
|
|
71
|
+
declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
|
|
72
|
+
declare function interpretSdkError(err: any): Error;
|
|
73
|
+
|
|
68
74
|
interface ExtractedCharacter {
|
|
69
75
|
name: string;
|
|
70
76
|
prompts: {
|
|
@@ -122,7 +128,7 @@ interface InspectPanelProps {
|
|
|
122
128
|
currentResult: Generation | null;
|
|
123
129
|
history: Generation[];
|
|
124
130
|
onSelect: (gen: Generation) => void;
|
|
125
|
-
workspaceTags
|
|
131
|
+
workspaceTags?: WorkspaceTags | null;
|
|
126
132
|
onTagToggle?: (tagName: string) => void;
|
|
127
133
|
}
|
|
128
134
|
declare const InspectPanel: React.FC<InspectPanelProps>;
|
|
@@ -160,10 +166,27 @@ interface ListViewProps {
|
|
|
160
166
|
onFocus: (id: string | null) => void;
|
|
161
167
|
activePath: Set<string>;
|
|
162
168
|
onGenerate: (id: string) => void;
|
|
163
|
-
onGenerateBranch
|
|
164
|
-
onGenerateSubtree
|
|
169
|
+
onGenerateBranch?: (id: string) => void;
|
|
170
|
+
onGenerateSubtree?: (id: string) => void;
|
|
165
171
|
isGeneratingNodeId: (id: string) => boolean;
|
|
166
172
|
}
|
|
167
173
|
declare function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }: ListViewProps): react_jsx_runtime.JSX.Element;
|
|
168
174
|
|
|
169
|
-
|
|
175
|
+
interface MediaItem {
|
|
176
|
+
base64: string;
|
|
177
|
+
mimeType: string;
|
|
178
|
+
mediaId?: string;
|
|
179
|
+
name?: string;
|
|
180
|
+
}
|
|
181
|
+
interface AvatarArchitectAppProps {
|
|
182
|
+
onGenerateImage: (prompt: string, aspectRatio: string, model: string, referenceId?: string) => Promise<{
|
|
183
|
+
base64: string;
|
|
184
|
+
mediaId?: string;
|
|
185
|
+
}>;
|
|
186
|
+
onGeneratePrompt: (treeText: string) => Promise<string>;
|
|
187
|
+
onDownload: (base64: string, mimeType: string, filename: string) => Promise<void>;
|
|
188
|
+
onSelectMedia: () => Promise<MediaItem[]>;
|
|
189
|
+
}
|
|
190
|
+
declare function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }: AvatarArchitectAppProps): react_jsx_runtime.JSX.Element;
|
|
191
|
+
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AvatarArchitectApp: () => AvatarArchitectApp,
|
|
33
34
|
CompactDropdown: () => CompactDropdown,
|
|
34
35
|
FaToolsBadge: () => FaToolsBadge,
|
|
35
36
|
GLOBAL_STYLES: () => GLOBAL_STYLES,
|
|
@@ -40,11 +41,16 @@ __export(index_exports, {
|
|
|
40
41
|
PillButton: () => PillButton,
|
|
41
42
|
SectionLabel: () => SectionLabel,
|
|
42
43
|
SetupPanel: () => SetupPanel,
|
|
44
|
+
buildFallbackPrompt: () => buildFallbackPrompt,
|
|
45
|
+
buildGenerationPrompt: () => buildGenerationPrompt,
|
|
46
|
+
buildImageGenerationOptions: () => buildImageGenerationOptions,
|
|
47
|
+
cleanAiResponse: () => cleanAiResponse,
|
|
43
48
|
exportProjectToZip: () => exportProjectToZip,
|
|
44
49
|
formatTreeToMarkdown: () => formatTreeToMarkdown,
|
|
45
50
|
getFormattedTimestamp: () => getFormattedTimestamp,
|
|
46
51
|
importProjectFromZip: () => importProjectFromZip,
|
|
47
52
|
injectXMPMetadata: () => injectXMPMetadata,
|
|
53
|
+
interpretSdkError: () => interpretSdkError,
|
|
48
54
|
parsePromptFile: () => parsePromptFile,
|
|
49
55
|
useKeyboardNavigation: () => useKeyboardNavigation,
|
|
50
56
|
useOnClickOutside: () => useOnClickOutside
|
|
@@ -313,6 +319,50 @@ async function importProjectFromZip(file) {
|
|
|
313
319
|
return data;
|
|
314
320
|
}
|
|
315
321
|
|
|
322
|
+
// src/lib/aiHelpers.ts
|
|
323
|
+
function buildGenerationPrompt(hierarchyText, mode = "creative") {
|
|
324
|
+
const safe = hierarchyText.length > 2500 ? hierarchyText.slice(0, 2500) + "..." : hierarchyText;
|
|
325
|
+
if (mode === "literal") {
|
|
326
|
+
return `Translate this hierarchy into a literal image generation prompt. Output ONLY the raw prompt string. No chat.
|
|
327
|
+
|
|
328
|
+
HIERARCHY:
|
|
329
|
+
${safe}`;
|
|
330
|
+
}
|
|
331
|
+
return `Create a cinematic, high-quality image generation prompt based on this hierarchy. Translate everything to English. Output ONLY the prompt string. No chat.
|
|
332
|
+
|
|
333
|
+
HIERARCHY:
|
|
334
|
+
${safe}`;
|
|
335
|
+
}
|
|
336
|
+
function buildFallbackPrompt(hierarchyText) {
|
|
337
|
+
const lines = hierarchyText.split("\n");
|
|
338
|
+
const firstConcept = lines.find((l) => l.includes("-"))?.replace(/^[-\s]+/, "") || "character concept";
|
|
339
|
+
return `Cinematic professional photograph of ${firstConcept}, masterpiece, highly detailed, fine art style`;
|
|
340
|
+
}
|
|
341
|
+
function cleanAiResponse(text) {
|
|
342
|
+
return text.replace(/^["']|["']$/g, "").replace(/```/g, "").trim();
|
|
343
|
+
}
|
|
344
|
+
function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaId) {
|
|
345
|
+
const options = {
|
|
346
|
+
prompt: prompt.replace(/\n/g, " ").trim().slice(0, 1500),
|
|
347
|
+
aspectRatio,
|
|
348
|
+
modelDisplayName: model
|
|
349
|
+
};
|
|
350
|
+
if (referenceMediaId && referenceMediaId.length > 5) {
|
|
351
|
+
options.referenceImageMediaIds = [referenceMediaId];
|
|
352
|
+
}
|
|
353
|
+
return options;
|
|
354
|
+
}
|
|
355
|
+
function interpretSdkError(err) {
|
|
356
|
+
const msg = err?.message || "Generierung fehlgeschlagen";
|
|
357
|
+
if (msg.includes("PUBLIC_ERROR_SOMETHING_WENT_WRONG")) {
|
|
358
|
+
return new Error("Plattform-Fehler: Die Anfrage wurde abgelehnt. M\xF6gliche Ursachen: Sicherheitsfilter (Prompt-Inhalt) oder tempor\xE4re \xDCberlastung. Versuche einen neutraleren Prompt.");
|
|
359
|
+
}
|
|
360
|
+
if (msg.includes("PUBLIC_ERROR")) {
|
|
361
|
+
return new Error(`Plattform-Fehler: ${msg}`);
|
|
362
|
+
}
|
|
363
|
+
return new Error(msg);
|
|
364
|
+
}
|
|
365
|
+
|
|
316
366
|
// src/lib/parserService.ts
|
|
317
367
|
function parsePromptFile(text) {
|
|
318
368
|
const characters = [];
|
|
@@ -764,8 +814,312 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
764
814
|
if (e.target === e.currentTarget) onFocus(null);
|
|
765
815
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-full flex flex-col", children: roots.map((root) => renderNode(root, 0)) }) });
|
|
766
816
|
}
|
|
817
|
+
|
|
818
|
+
// src/components/AvatarArchitectApp.tsx
|
|
819
|
+
var import_react10 = require("react");
|
|
820
|
+
var import_react11 = require("motion/react");
|
|
821
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
822
|
+
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
|
|
823
|
+
(0, import_react10.useEffect)(() => {
|
|
824
|
+
const id = "flow-styles";
|
|
825
|
+
if (!document.getElementById(id)) {
|
|
826
|
+
const style = document.createElement("style");
|
|
827
|
+
style.id = id;
|
|
828
|
+
style.textContent = GLOBAL_STYLES;
|
|
829
|
+
document.head.appendChild(style);
|
|
830
|
+
}
|
|
831
|
+
}, []);
|
|
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");
|
|
850
|
+
const isGenerating = activeGenerationsCount > 0;
|
|
851
|
+
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
852
|
+
const getSubtreeFormat = (0, import_react10.useCallback)((nodeId, depth = 0) => {
|
|
853
|
+
const node = nodes.find((n) => n.id === nodeId);
|
|
854
|
+
if (!node) return "";
|
|
855
|
+
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
856
|
+
const indent = " ".repeat(depth);
|
|
857
|
+
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
858
|
+
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
859
|
+
}, [nodes, edges]);
|
|
860
|
+
const activePath = (0, import_react10.useMemo)(() => {
|
|
861
|
+
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
862
|
+
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
863
|
+
let currId = focusedNodeId;
|
|
864
|
+
const parentMap = /* @__PURE__ */ new Map();
|
|
865
|
+
edges.forEach((e) => parentMap.set(e.target, e.source));
|
|
866
|
+
while (parentMap.has(currId)) {
|
|
867
|
+
currId = parentMap.get(currId);
|
|
868
|
+
path.add(currId);
|
|
869
|
+
}
|
|
870
|
+
return path;
|
|
871
|
+
}, [focusedNodeId, edges]);
|
|
872
|
+
const handleGenerateImage = async (customPrompt, useReferenceId, overrideNodeId, options = { silent: false }) => {
|
|
873
|
+
const promptToUse = (customPrompt || activePrompt).trim();
|
|
874
|
+
if (!promptToUse) return;
|
|
875
|
+
setActiveGenerationsCount((prev) => prev + 1);
|
|
876
|
+
const genId = crypto.randomUUID();
|
|
877
|
+
const activeSeed = seedMode === "random" ? Math.floor(Math.random() * 1e9) : seed;
|
|
878
|
+
const newGen = {
|
|
879
|
+
id: genId,
|
|
880
|
+
nodeId: overrideNodeId || focusedNodeId || "1",
|
|
881
|
+
status: "processing",
|
|
882
|
+
timestamp: Date.now(),
|
|
883
|
+
prompt: promptToUse,
|
|
884
|
+
seed: activeSeed,
|
|
885
|
+
model: selectedModel,
|
|
886
|
+
tags: [],
|
|
887
|
+
type: "generation"
|
|
888
|
+
};
|
|
889
|
+
setHistory((prev) => [newGen, ...prev]);
|
|
890
|
+
if (!options.silent) setCurrentResult(newGen);
|
|
891
|
+
if (seedMode === "random") setSeed(activeSeed);
|
|
892
|
+
try {
|
|
893
|
+
const { base64, mediaId } = await onGenerateImage(promptToUse, aspectRatio, selectedModel, useReferenceId);
|
|
894
|
+
const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
|
|
895
|
+
setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
|
|
896
|
+
setGalleryItems((prev) => [finishedGen, ...prev]);
|
|
897
|
+
setCurrentResult((prev) => {
|
|
898
|
+
if (!prev) return finishedGen;
|
|
899
|
+
if (prev.id === genId || !options.silent) return finishedGen;
|
|
900
|
+
return prev;
|
|
901
|
+
});
|
|
902
|
+
} catch (err) {
|
|
903
|
+
const errorGen = { ...newGen, status: "error", error: { message: err.message || "Unbekannter Fehler", details: err.toString() } };
|
|
904
|
+
setHistory((prev) => prev.map((g) => g.id === genId ? errorGen : g));
|
|
905
|
+
if (!options.silent || currentResult?.id === genId) setCurrentResult(errorGen);
|
|
906
|
+
} finally {
|
|
907
|
+
setActiveGenerationsCount((prev) => Math.max(0, prev - 1));
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const handleSynthesizePrompt = async (nodeId, autoGenerate = false) => {
|
|
911
|
+
setIsSynthesizing(true);
|
|
912
|
+
try {
|
|
913
|
+
const prompt = await onGeneratePrompt(getSubtreeFormat(nodeId));
|
|
914
|
+
setActivePrompt(prompt);
|
|
915
|
+
setFocusedNodeId(nodeId);
|
|
916
|
+
if (autoGenerate) handleGenerateImage(prompt, void 0, nodeId);
|
|
917
|
+
} catch {
|
|
918
|
+
setActivePrompt("Synthese-Fehler");
|
|
919
|
+
} finally {
|
|
920
|
+
setIsSynthesizing(false);
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const handleDownloadSingle = async () => {
|
|
924
|
+
if (!currentResult?.base64) return;
|
|
925
|
+
try {
|
|
926
|
+
const base64Data = currentResult.base64.split(",")[1];
|
|
927
|
+
const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
|
|
928
|
+
let finalBase64 = base64Data;
|
|
929
|
+
if (mimeType === "image/png") {
|
|
930
|
+
finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
|
|
931
|
+
}
|
|
932
|
+
await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
|
|
933
|
+
} catch (e) {
|
|
934
|
+
console.error("Download Error", e);
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
const handleProjectExport = async () => {
|
|
938
|
+
setProjectActionState("working-full");
|
|
939
|
+
try {
|
|
940
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode });
|
|
941
|
+
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
942
|
+
setProjectActionState("done");
|
|
943
|
+
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
944
|
+
} catch {
|
|
945
|
+
setProjectActionState("error");
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
const handleProjectImport = async (input) => {
|
|
949
|
+
const file = input instanceof FileList ? input[0] : input;
|
|
950
|
+
if (!file) return;
|
|
951
|
+
setProjectActionState("working-full");
|
|
952
|
+
try {
|
|
953
|
+
const data = await importProjectFromZip(file);
|
|
954
|
+
if (data.nodes) setNodes(data.nodes);
|
|
955
|
+
if (data.edges) setEdges(data.edges);
|
|
956
|
+
if (data.history) setHistory(data.history);
|
|
957
|
+
if (data.galleryItems) setGalleryItems(data.galleryItems);
|
|
958
|
+
if (data.settings) {
|
|
959
|
+
setAspectRatio(data.settings.aspectRatio || "1:1");
|
|
960
|
+
setSelectedModel(data.settings.selectedModel || "\u{1F34C} Nano Banana Pro");
|
|
961
|
+
setSeed(data.settings.seed || 0);
|
|
962
|
+
setSeedMode(data.settings.seedMode || "random");
|
|
963
|
+
}
|
|
964
|
+
setProjectActionState("done");
|
|
965
|
+
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
966
|
+
} catch {
|
|
967
|
+
setProjectActionState("error");
|
|
968
|
+
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
const handleWorkspaceImport = (file) => {
|
|
972
|
+
const reader = new FileReader();
|
|
973
|
+
reader.onload = (ev) => {
|
|
974
|
+
try {
|
|
975
|
+
const root = JSON.parse(ev.target?.result);
|
|
976
|
+
const rawTags = root.tags || root;
|
|
977
|
+
if (!rawTags?.by_category) return;
|
|
978
|
+
} catch (err) {
|
|
979
|
+
console.error("Workspace Load failed", err);
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
reader.readAsText(file);
|
|
983
|
+
};
|
|
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" })
|
|
989
|
+
] }),
|
|
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
|
+
)
|
|
1015
|
+
] }),
|
|
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" }] })
|
|
1021
|
+
] }),
|
|
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" })
|
|
1025
|
+
] })
|
|
1026
|
+
] }),
|
|
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)(
|
|
1030
|
+
"textarea",
|
|
1031
|
+
{
|
|
1032
|
+
value: activePrompt,
|
|
1033
|
+
onChange: (e) => setActivePrompt(e.target.value),
|
|
1034
|
+
className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar",
|
|
1035
|
+
placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
|
|
1036
|
+
}
|
|
1037
|
+
),
|
|
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" }) })
|
|
1039
|
+
] }) }) }),
|
|
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..." })
|
|
1044
|
+
] }),
|
|
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 })
|
|
1053
|
+
] }),
|
|
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" })
|
|
1061
|
+
] })
|
|
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" })
|
|
1065
|
+
] })
|
|
1066
|
+
] }) })
|
|
1067
|
+
] })
|
|
1068
|
+
] }),
|
|
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: () => {
|
|
1072
|
+
setActiveTab(tab);
|
|
1073
|
+
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" }) })
|
|
1076
|
+
] }),
|
|
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)(
|
|
1080
|
+
MediaLibrary,
|
|
1081
|
+
{
|
|
1082
|
+
items: galleryItems,
|
|
1083
|
+
onImport: async () => {
|
|
1084
|
+
const media = await onSelectMedia();
|
|
1085
|
+
if (!media?.length) return;
|
|
1086
|
+
const newItems = media.map((m) => ({
|
|
1087
|
+
id: crypto.randomUUID(),
|
|
1088
|
+
nodeId: "1",
|
|
1089
|
+
base64: `data:${m.mimeType};base64,${m.base64}`,
|
|
1090
|
+
mediaId: m.mediaId,
|
|
1091
|
+
prompt: m.name || "Importiert",
|
|
1092
|
+
timestamp: Date.now(),
|
|
1093
|
+
status: "done",
|
|
1094
|
+
tags: [],
|
|
1095
|
+
type: "import"
|
|
1096
|
+
}));
|
|
1097
|
+
setGalleryItems((prev) => [...newItems, ...prev]);
|
|
1098
|
+
},
|
|
1099
|
+
onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
|
|
1100
|
+
onSelect: setCurrentResult,
|
|
1101
|
+
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
1102
|
+
},
|
|
1103
|
+
"gallery"
|
|
1104
|
+
),
|
|
1105
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
|
|
1106
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1107
|
+
SetupPanel,
|
|
1108
|
+
{
|
|
1109
|
+
onProjectExport: handleProjectExport,
|
|
1110
|
+
onProjectImport: handleProjectImport,
|
|
1111
|
+
onWorkspaceImport: handleWorkspaceImport,
|
|
1112
|
+
projectActionState
|
|
1113
|
+
},
|
|
1114
|
+
"setup"
|
|
1115
|
+
)
|
|
1116
|
+
] }) })
|
|
1117
|
+
] })
|
|
1118
|
+
] });
|
|
1119
|
+
}
|
|
767
1120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
768
1121
|
0 && (module.exports = {
|
|
1122
|
+
AvatarArchitectApp,
|
|
769
1123
|
CompactDropdown,
|
|
770
1124
|
FaToolsBadge,
|
|
771
1125
|
GLOBAL_STYLES,
|
|
@@ -776,11 +1130,16 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
776
1130
|
PillButton,
|
|
777
1131
|
SectionLabel,
|
|
778
1132
|
SetupPanel,
|
|
1133
|
+
buildFallbackPrompt,
|
|
1134
|
+
buildGenerationPrompt,
|
|
1135
|
+
buildImageGenerationOptions,
|
|
1136
|
+
cleanAiResponse,
|
|
779
1137
|
exportProjectToZip,
|
|
780
1138
|
formatTreeToMarkdown,
|
|
781
1139
|
getFormattedTimestamp,
|
|
782
1140
|
importProjectFromZip,
|
|
783
1141
|
injectXMPMetadata,
|
|
1142
|
+
interpretSdkError,
|
|
784
1143
|
parsePromptFile,
|
|
785
1144
|
useKeyboardNavigation,
|
|
786
1145
|
useOnClickOutside
|
package/dist/index.mjs
CHANGED
|
@@ -260,6 +260,50 @@ async function importProjectFromZip(file) {
|
|
|
260
260
|
return data;
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
// src/lib/aiHelpers.ts
|
|
264
|
+
function buildGenerationPrompt(hierarchyText, mode = "creative") {
|
|
265
|
+
const safe = hierarchyText.length > 2500 ? hierarchyText.slice(0, 2500) + "..." : hierarchyText;
|
|
266
|
+
if (mode === "literal") {
|
|
267
|
+
return `Translate this hierarchy into a literal image generation prompt. Output ONLY the raw prompt string. No chat.
|
|
268
|
+
|
|
269
|
+
HIERARCHY:
|
|
270
|
+
${safe}`;
|
|
271
|
+
}
|
|
272
|
+
return `Create a cinematic, high-quality image generation prompt based on this hierarchy. Translate everything to English. Output ONLY the prompt string. No chat.
|
|
273
|
+
|
|
274
|
+
HIERARCHY:
|
|
275
|
+
${safe}`;
|
|
276
|
+
}
|
|
277
|
+
function buildFallbackPrompt(hierarchyText) {
|
|
278
|
+
const lines = hierarchyText.split("\n");
|
|
279
|
+
const firstConcept = lines.find((l) => l.includes("-"))?.replace(/^[-\s]+/, "") || "character concept";
|
|
280
|
+
return `Cinematic professional photograph of ${firstConcept}, masterpiece, highly detailed, fine art style`;
|
|
281
|
+
}
|
|
282
|
+
function cleanAiResponse(text) {
|
|
283
|
+
return text.replace(/^["']|["']$/g, "").replace(/```/g, "").trim();
|
|
284
|
+
}
|
|
285
|
+
function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaId) {
|
|
286
|
+
const options = {
|
|
287
|
+
prompt: prompt.replace(/\n/g, " ").trim().slice(0, 1500),
|
|
288
|
+
aspectRatio,
|
|
289
|
+
modelDisplayName: model
|
|
290
|
+
};
|
|
291
|
+
if (referenceMediaId && referenceMediaId.length > 5) {
|
|
292
|
+
options.referenceImageMediaIds = [referenceMediaId];
|
|
293
|
+
}
|
|
294
|
+
return options;
|
|
295
|
+
}
|
|
296
|
+
function interpretSdkError(err) {
|
|
297
|
+
const msg = err?.message || "Generierung fehlgeschlagen";
|
|
298
|
+
if (msg.includes("PUBLIC_ERROR_SOMETHING_WENT_WRONG")) {
|
|
299
|
+
return new Error("Plattform-Fehler: Die Anfrage wurde abgelehnt. M\xF6gliche Ursachen: Sicherheitsfilter (Prompt-Inhalt) oder tempor\xE4re \xDCberlastung. Versuche einen neutraleren Prompt.");
|
|
300
|
+
}
|
|
301
|
+
if (msg.includes("PUBLIC_ERROR")) {
|
|
302
|
+
return new Error(`Plattform-Fehler: ${msg}`);
|
|
303
|
+
}
|
|
304
|
+
return new Error(msg);
|
|
305
|
+
}
|
|
306
|
+
|
|
263
307
|
// src/lib/parserService.ts
|
|
264
308
|
function parsePromptFile(text) {
|
|
265
309
|
const characters = [];
|
|
@@ -711,7 +755,311 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
711
755
|
if (e.target === e.currentTarget) onFocus(null);
|
|
712
756
|
}, children: /* @__PURE__ */ jsx9("div", { className: "w-full flex flex-col", children: roots.map((root) => renderNode(root, 0)) }) });
|
|
713
757
|
}
|
|
758
|
+
|
|
759
|
+
// src/components/AvatarArchitectApp.tsx
|
|
760
|
+
import { useState as useState3, useCallback, useMemo, useEffect as useEffect4 } from "react";
|
|
761
|
+
import { motion as motion5, AnimatePresence } from "motion/react";
|
|
762
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
763
|
+
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
|
|
764
|
+
useEffect4(() => {
|
|
765
|
+
const id = "flow-styles";
|
|
766
|
+
if (!document.getElementById(id)) {
|
|
767
|
+
const style = document.createElement("style");
|
|
768
|
+
style.id = id;
|
|
769
|
+
style.textContent = GLOBAL_STYLES;
|
|
770
|
+
document.head.appendChild(style);
|
|
771
|
+
}
|
|
772
|
+
}, []);
|
|
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");
|
|
791
|
+
const isGenerating = activeGenerationsCount > 0;
|
|
792
|
+
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
793
|
+
const getSubtreeFormat = useCallback((nodeId, depth = 0) => {
|
|
794
|
+
const node = nodes.find((n) => n.id === nodeId);
|
|
795
|
+
if (!node) return "";
|
|
796
|
+
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
797
|
+
const indent = " ".repeat(depth);
|
|
798
|
+
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
799
|
+
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
800
|
+
}, [nodes, edges]);
|
|
801
|
+
const activePath = useMemo(() => {
|
|
802
|
+
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
803
|
+
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
804
|
+
let currId = focusedNodeId;
|
|
805
|
+
const parentMap = /* @__PURE__ */ new Map();
|
|
806
|
+
edges.forEach((e) => parentMap.set(e.target, e.source));
|
|
807
|
+
while (parentMap.has(currId)) {
|
|
808
|
+
currId = parentMap.get(currId);
|
|
809
|
+
path.add(currId);
|
|
810
|
+
}
|
|
811
|
+
return path;
|
|
812
|
+
}, [focusedNodeId, edges]);
|
|
813
|
+
const handleGenerateImage = async (customPrompt, useReferenceId, overrideNodeId, options = { silent: false }) => {
|
|
814
|
+
const promptToUse = (customPrompt || activePrompt).trim();
|
|
815
|
+
if (!promptToUse) return;
|
|
816
|
+
setActiveGenerationsCount((prev) => prev + 1);
|
|
817
|
+
const genId = crypto.randomUUID();
|
|
818
|
+
const activeSeed = seedMode === "random" ? Math.floor(Math.random() * 1e9) : seed;
|
|
819
|
+
const newGen = {
|
|
820
|
+
id: genId,
|
|
821
|
+
nodeId: overrideNodeId || focusedNodeId || "1",
|
|
822
|
+
status: "processing",
|
|
823
|
+
timestamp: Date.now(),
|
|
824
|
+
prompt: promptToUse,
|
|
825
|
+
seed: activeSeed,
|
|
826
|
+
model: selectedModel,
|
|
827
|
+
tags: [],
|
|
828
|
+
type: "generation"
|
|
829
|
+
};
|
|
830
|
+
setHistory((prev) => [newGen, ...prev]);
|
|
831
|
+
if (!options.silent) setCurrentResult(newGen);
|
|
832
|
+
if (seedMode === "random") setSeed(activeSeed);
|
|
833
|
+
try {
|
|
834
|
+
const { base64, mediaId } = await onGenerateImage(promptToUse, aspectRatio, selectedModel, useReferenceId);
|
|
835
|
+
const finishedGen = { ...newGen, status: "done", base64: `data:image/png;base64,${base64}`, mediaId };
|
|
836
|
+
setHistory((prev) => prev.map((g) => g.id === genId ? finishedGen : g));
|
|
837
|
+
setGalleryItems((prev) => [finishedGen, ...prev]);
|
|
838
|
+
setCurrentResult((prev) => {
|
|
839
|
+
if (!prev) return finishedGen;
|
|
840
|
+
if (prev.id === genId || !options.silent) return finishedGen;
|
|
841
|
+
return prev;
|
|
842
|
+
});
|
|
843
|
+
} catch (err) {
|
|
844
|
+
const errorGen = { ...newGen, status: "error", error: { message: err.message || "Unbekannter Fehler", details: err.toString() } };
|
|
845
|
+
setHistory((prev) => prev.map((g) => g.id === genId ? errorGen : g));
|
|
846
|
+
if (!options.silent || currentResult?.id === genId) setCurrentResult(errorGen);
|
|
847
|
+
} finally {
|
|
848
|
+
setActiveGenerationsCount((prev) => Math.max(0, prev - 1));
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
const handleSynthesizePrompt = async (nodeId, autoGenerate = false) => {
|
|
852
|
+
setIsSynthesizing(true);
|
|
853
|
+
try {
|
|
854
|
+
const prompt = await onGeneratePrompt(getSubtreeFormat(nodeId));
|
|
855
|
+
setActivePrompt(prompt);
|
|
856
|
+
setFocusedNodeId(nodeId);
|
|
857
|
+
if (autoGenerate) handleGenerateImage(prompt, void 0, nodeId);
|
|
858
|
+
} catch {
|
|
859
|
+
setActivePrompt("Synthese-Fehler");
|
|
860
|
+
} finally {
|
|
861
|
+
setIsSynthesizing(false);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
const handleDownloadSingle = async () => {
|
|
865
|
+
if (!currentResult?.base64) return;
|
|
866
|
+
try {
|
|
867
|
+
const base64Data = currentResult.base64.split(",")[1];
|
|
868
|
+
const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
|
|
869
|
+
let finalBase64 = base64Data;
|
|
870
|
+
if (mimeType === "image/png") {
|
|
871
|
+
finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
|
|
872
|
+
}
|
|
873
|
+
await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
|
|
874
|
+
} catch (e) {
|
|
875
|
+
console.error("Download Error", e);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
const handleProjectExport = async () => {
|
|
879
|
+
setProjectActionState("working-full");
|
|
880
|
+
try {
|
|
881
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode });
|
|
882
|
+
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
883
|
+
setProjectActionState("done");
|
|
884
|
+
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
885
|
+
} catch {
|
|
886
|
+
setProjectActionState("error");
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
const handleProjectImport = async (input) => {
|
|
890
|
+
const file = input instanceof FileList ? input[0] : input;
|
|
891
|
+
if (!file) return;
|
|
892
|
+
setProjectActionState("working-full");
|
|
893
|
+
try {
|
|
894
|
+
const data = await importProjectFromZip(file);
|
|
895
|
+
if (data.nodes) setNodes(data.nodes);
|
|
896
|
+
if (data.edges) setEdges(data.edges);
|
|
897
|
+
if (data.history) setHistory(data.history);
|
|
898
|
+
if (data.galleryItems) setGalleryItems(data.galleryItems);
|
|
899
|
+
if (data.settings) {
|
|
900
|
+
setAspectRatio(data.settings.aspectRatio || "1:1");
|
|
901
|
+
setSelectedModel(data.settings.selectedModel || "\u{1F34C} Nano Banana Pro");
|
|
902
|
+
setSeed(data.settings.seed || 0);
|
|
903
|
+
setSeedMode(data.settings.seedMode || "random");
|
|
904
|
+
}
|
|
905
|
+
setProjectActionState("done");
|
|
906
|
+
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
907
|
+
} catch {
|
|
908
|
+
setProjectActionState("error");
|
|
909
|
+
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
910
|
+
}
|
|
911
|
+
};
|
|
912
|
+
const handleWorkspaceImport = (file) => {
|
|
913
|
+
const reader = new FileReader();
|
|
914
|
+
reader.onload = (ev) => {
|
|
915
|
+
try {
|
|
916
|
+
const root = JSON.parse(ev.target?.result);
|
|
917
|
+
const rawTags = root.tags || root;
|
|
918
|
+
if (!rawTags?.by_category) return;
|
|
919
|
+
} catch (err) {
|
|
920
|
+
console.error("Workspace Load failed", err);
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
reader.readAsText(file);
|
|
924
|
+
};
|
|
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" })
|
|
930
|
+
] }),
|
|
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
|
+
)
|
|
956
|
+
] }),
|
|
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" }] })
|
|
962
|
+
] }),
|
|
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" })
|
|
966
|
+
] })
|
|
967
|
+
] }),
|
|
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(
|
|
971
|
+
"textarea",
|
|
972
|
+
{
|
|
973
|
+
value: activePrompt,
|
|
974
|
+
onChange: (e) => setActivePrompt(e.target.value),
|
|
975
|
+
className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar",
|
|
976
|
+
placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..."
|
|
977
|
+
}
|
|
978
|
+
),
|
|
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" }) })
|
|
980
|
+
] }) }) }),
|
|
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..." })
|
|
985
|
+
] }),
|
|
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 })
|
|
994
|
+
] }),
|
|
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" })
|
|
1002
|
+
] })
|
|
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" })
|
|
1006
|
+
] })
|
|
1007
|
+
] }) })
|
|
1008
|
+
] })
|
|
1009
|
+
] }),
|
|
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: () => {
|
|
1013
|
+
setActiveTab(tab);
|
|
1014
|
+
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" }) })
|
|
1017
|
+
] }),
|
|
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(
|
|
1021
|
+
MediaLibrary,
|
|
1022
|
+
{
|
|
1023
|
+
items: galleryItems,
|
|
1024
|
+
onImport: async () => {
|
|
1025
|
+
const media = await onSelectMedia();
|
|
1026
|
+
if (!media?.length) return;
|
|
1027
|
+
const newItems = media.map((m) => ({
|
|
1028
|
+
id: crypto.randomUUID(),
|
|
1029
|
+
nodeId: "1",
|
|
1030
|
+
base64: `data:${m.mimeType};base64,${m.base64}`,
|
|
1031
|
+
mediaId: m.mediaId,
|
|
1032
|
+
prompt: m.name || "Importiert",
|
|
1033
|
+
timestamp: Date.now(),
|
|
1034
|
+
status: "done",
|
|
1035
|
+
tags: [],
|
|
1036
|
+
type: "import"
|
|
1037
|
+
}));
|
|
1038
|
+
setGalleryItems((prev) => [...newItems, ...prev]);
|
|
1039
|
+
},
|
|
1040
|
+
onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
|
|
1041
|
+
onSelect: setCurrentResult,
|
|
1042
|
+
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
1043
|
+
},
|
|
1044
|
+
"gallery"
|
|
1045
|
+
),
|
|
1046
|
+
activeTab === "inspect" && /* @__PURE__ */ jsx10(InspectPanel, { currentResult, history, onSelect: setCurrentResult }, "inspect"),
|
|
1047
|
+
activeTab === "setup" && /* @__PURE__ */ jsx10(
|
|
1048
|
+
SetupPanel,
|
|
1049
|
+
{
|
|
1050
|
+
onProjectExport: handleProjectExport,
|
|
1051
|
+
onProjectImport: handleProjectImport,
|
|
1052
|
+
onWorkspaceImport: handleWorkspaceImport,
|
|
1053
|
+
projectActionState
|
|
1054
|
+
},
|
|
1055
|
+
"setup"
|
|
1056
|
+
)
|
|
1057
|
+
] }) })
|
|
1058
|
+
] })
|
|
1059
|
+
] });
|
|
1060
|
+
}
|
|
714
1061
|
export {
|
|
1062
|
+
AvatarArchitectApp,
|
|
715
1063
|
CompactDropdown,
|
|
716
1064
|
FaToolsBadge,
|
|
717
1065
|
GLOBAL_STYLES,
|
|
@@ -722,11 +1070,16 @@ export {
|
|
|
722
1070
|
PillButton,
|
|
723
1071
|
SectionLabel,
|
|
724
1072
|
SetupPanel,
|
|
1073
|
+
buildFallbackPrompt,
|
|
1074
|
+
buildGenerationPrompt,
|
|
1075
|
+
buildImageGenerationOptions,
|
|
1076
|
+
cleanAiResponse,
|
|
725
1077
|
exportProjectToZip,
|
|
726
1078
|
formatTreeToMarkdown,
|
|
727
1079
|
getFormattedTimestamp,
|
|
728
1080
|
importProjectFromZip,
|
|
729
1081
|
injectXMPMetadata,
|
|
1082
|
+
interpretSdkError,
|
|
730
1083
|
parsePromptFile,
|
|
731
1084
|
useKeyboardNavigation,
|
|
732
1085
|
useOnClickOutside
|