@rslsp1/fa-app-tools 1.1.4 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +145 -8
- package/dist/index.d.ts +145 -8
- package/dist/index.js +1738 -318
- package/dist/index.mjs +1734 -330
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -197,7 +197,7 @@ function generateMarkdownReport(nodes, history) {
|
|
|
197
197
|
});
|
|
198
198
|
return md;
|
|
199
199
|
}
|
|
200
|
-
async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags) {
|
|
200
|
+
async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags, recentLabItemIds) {
|
|
201
201
|
const zip = new JSZip();
|
|
202
202
|
const projectData = {
|
|
203
203
|
nodes,
|
|
@@ -206,7 +206,8 @@ async function exportProjectToZip(nodes, edges, history, galleryItems, settings,
|
|
|
206
206
|
galleryItems: galleryItems.map((g) => ({ ...g, base64: void 0 })),
|
|
207
207
|
settings,
|
|
208
208
|
workspaceTags: workspaceTags || null,
|
|
209
|
-
|
|
209
|
+
recentLabItemIds: recentLabItemIds || [],
|
|
210
|
+
version: "1.3.0"
|
|
210
211
|
};
|
|
211
212
|
zip.file("project.json", JSON.stringify(projectData, null, 2));
|
|
212
213
|
zip.file("projekt_bericht.md", generateMarkdownReport(nodes, history));
|
|
@@ -362,35 +363,18 @@ function createFlowServices(Flow) {
|
|
|
362
363
|
throw interpretSdkError(err);
|
|
363
364
|
}
|
|
364
365
|
},
|
|
365
|
-
generateText: async (
|
|
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
|
-
}
|
|
383
|
-
const prompt = buildGenerationPrompt(hierarchyText, mode);
|
|
366
|
+
generateText: async (text, options) => {
|
|
384
367
|
try {
|
|
385
|
-
const response = await Flow.generate.text(
|
|
368
|
+
const response = await Flow.generate.text(text, {
|
|
386
369
|
thinkingLevel: "low",
|
|
387
|
-
systemInstruction: "You are an expert prompt engineer. Always return ONLY the generated prompt text, no headers, no quotes, no conversational filler."
|
|
370
|
+
systemInstruction: options?.systemInstruction || "You are an expert prompt engineer. Always return ONLY the generated prompt text, no headers, no quotes, no conversational filler.",
|
|
371
|
+
images: options?.images
|
|
388
372
|
});
|
|
389
373
|
const result = response?.text?.trim() || "";
|
|
390
374
|
if (!result) throw new Error("Empty AI response");
|
|
391
375
|
return cleanAiResponse(result);
|
|
392
|
-
} catch {
|
|
393
|
-
|
|
376
|
+
} catch (err) {
|
|
377
|
+
throw interpretSdkError(err);
|
|
394
378
|
}
|
|
395
379
|
}
|
|
396
380
|
};
|
|
@@ -669,12 +653,40 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
|
|
|
669
653
|
};
|
|
670
654
|
|
|
671
655
|
// src/components/SetupPanel.tsx
|
|
672
|
-
import { useRef as useRef2 } from "react";
|
|
656
|
+
import { useRef as useRef2, useState as useState2 } from "react";
|
|
673
657
|
import { motion as motion3 } from "motion/react";
|
|
674
658
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
675
659
|
var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
|
|
676
660
|
const workspaceInputRef = useRef2(null);
|
|
677
|
-
|
|
661
|
+
const blobTestInputRef = useRef2(null);
|
|
662
|
+
const [blobStatus, setBlobStatus] = useState2("idle");
|
|
663
|
+
const [blobThumb, setBlobThumb] = useState2(null);
|
|
664
|
+
const [blobError, setBlobError] = useState2(null);
|
|
665
|
+
const handleBlobTest = async (file) => {
|
|
666
|
+
setBlobStatus("loading");
|
|
667
|
+
setBlobThumb(null);
|
|
668
|
+
setBlobError(null);
|
|
669
|
+
const blobUrl = URL.createObjectURL(file);
|
|
670
|
+
try {
|
|
671
|
+
const res = await fetch(blobUrl);
|
|
672
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
673
|
+
const blob = await res.blob();
|
|
674
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
675
|
+
const reader = new FileReader();
|
|
676
|
+
reader.onload = () => resolve(reader.result);
|
|
677
|
+
reader.onerror = () => reject(new Error("FileReader failed"));
|
|
678
|
+
reader.readAsDataURL(blob);
|
|
679
|
+
});
|
|
680
|
+
setBlobThumb(dataUrl);
|
|
681
|
+
setBlobStatus("ok");
|
|
682
|
+
} catch (e) {
|
|
683
|
+
setBlobError(e.message || String(e));
|
|
684
|
+
setBlobStatus("error");
|
|
685
|
+
} finally {
|
|
686
|
+
URL.revokeObjectURL(blobUrl);
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
return /* @__PURE__ */ jsxs5(motion3.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10 overflow-y-auto", children: [
|
|
678
690
|
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
|
|
679
691
|
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
680
692
|
/* @__PURE__ */ jsx7(SectionLabel, { children: "Workspace Management" }),
|
|
@@ -687,6 +699,46 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
|
|
|
687
699
|
e.target.value = "";
|
|
688
700
|
} })
|
|
689
701
|
] }),
|
|
702
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3", children: [
|
|
703
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1", children: [
|
|
704
|
+
/* @__PURE__ */ jsx7(SectionLabel, { children: "blob: URL Test" }),
|
|
705
|
+
/* @__PURE__ */ jsx7("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Testet ob fetch() auf blob: URLs funktioniert \u2014 lokale Datei \u2192 createObjectURL \u2192 fetch \u2192 base64." })
|
|
706
|
+
] }),
|
|
707
|
+
/* @__PURE__ */ jsx7(
|
|
708
|
+
PillButton,
|
|
709
|
+
{
|
|
710
|
+
variant: "outline",
|
|
711
|
+
icon: blobStatus === "loading" ? "sync" : "image_search",
|
|
712
|
+
onClick: () => blobTestInputRef.current?.click(),
|
|
713
|
+
children: "Bild w\xE4hlen & testen"
|
|
714
|
+
}
|
|
715
|
+
),
|
|
716
|
+
/* @__PURE__ */ jsx7(
|
|
717
|
+
"input",
|
|
718
|
+
{
|
|
719
|
+
ref: blobTestInputRef,
|
|
720
|
+
type: "file",
|
|
721
|
+
accept: "image/*",
|
|
722
|
+
className: "hidden",
|
|
723
|
+
onChange: (e) => {
|
|
724
|
+
const f = e.target.files?.[0];
|
|
725
|
+
if (f) handleBlobTest(f);
|
|
726
|
+
e.target.value = "";
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
),
|
|
730
|
+
blobStatus === "ok" && blobThumb && /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-2", children: [
|
|
731
|
+
/* @__PURE__ */ jsx7("img", { src: blobThumb, alt: "blob test", className: "w-full max-h-48 object-contain rounded-xl border border-white/10 bg-black/40" }),
|
|
732
|
+
/* @__PURE__ */ jsxs5("p", { className: "text-[10px] text-green-400 font-mono flex items-center gap-1", children: [
|
|
733
|
+
/* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[14px]", children: "check_circle" }),
|
|
734
|
+
"blob: fetch \u2713 \u2014 base64 erfolgreich"
|
|
735
|
+
] })
|
|
736
|
+
] }),
|
|
737
|
+
blobStatus === "error" && blobError && /* @__PURE__ */ jsxs5("p", { className: "text-[10px] text-red-400 font-mono flex items-start gap-1", children: [
|
|
738
|
+
/* @__PURE__ */ jsx7("span", { className: "material-symbols-outlined text-[14px] shrink-0", children: "error" }),
|
|
739
|
+
blobError
|
|
740
|
+
] })
|
|
741
|
+
] }),
|
|
690
742
|
/* @__PURE__ */ jsxs5("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
|
|
691
743
|
/* @__PURE__ */ jsx7("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
|
|
692
744
|
/* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
|
|
@@ -763,7 +815,7 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
|
|
|
763
815
|
};
|
|
764
816
|
|
|
765
817
|
// src/components/ListView.tsx
|
|
766
|
-
import { useEffect as useEffect3, useRef as useRef3, useState as
|
|
818
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
767
819
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
768
820
|
var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
|
|
769
821
|
const inputRef = useRef3(null);
|
|
@@ -828,7 +880,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
|
|
|
828
880
|
] });
|
|
829
881
|
};
|
|
830
882
|
function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
|
|
831
|
-
const [collapsed, setCollapsed] =
|
|
883
|
+
const [collapsed, setCollapsed] = useState3(/* @__PURE__ */ new Set());
|
|
832
884
|
const toggleCollapse = (id) => {
|
|
833
885
|
setCollapsed((prev) => {
|
|
834
886
|
const next = new Set(prev);
|
|
@@ -856,13 +908,13 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
856
908
|
}
|
|
857
909
|
|
|
858
910
|
// src/components/AvatarArchitectApp.tsx
|
|
859
|
-
import { useState as
|
|
911
|
+
import { useState as useState14, useCallback, useMemo as useMemo2, useEffect as useEffect4, useRef as useRef6 } from "react";
|
|
860
912
|
|
|
861
913
|
// src/components/PromptTab.tsx
|
|
862
|
-
import { useRef as useRef4, useState as
|
|
914
|
+
import { useRef as useRef4, useState as useState5 } from "react";
|
|
863
915
|
|
|
864
916
|
// src/components/CollapsibleCard.tsx
|
|
865
|
-
import { useState as
|
|
917
|
+
import { useState as useState4 } from "react";
|
|
866
918
|
import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
867
919
|
var CollapsibleCard = ({
|
|
868
920
|
title,
|
|
@@ -873,7 +925,7 @@ var CollapsibleCard = ({
|
|
|
873
925
|
collapsible = true,
|
|
874
926
|
className = ""
|
|
875
927
|
}) => {
|
|
876
|
-
const [isOpen, setIsOpen] =
|
|
928
|
+
const [isOpen, setIsOpen] = useState4(defaultOpen);
|
|
877
929
|
return /* @__PURE__ */ jsxs8("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
|
|
878
930
|
/* @__PURE__ */ jsxs8(
|
|
879
931
|
"div",
|
|
@@ -920,18 +972,18 @@ var PromptTab = ({
|
|
|
920
972
|
onTagUpdate,
|
|
921
973
|
onTagDelete
|
|
922
974
|
}) => {
|
|
923
|
-
const [selectedLabels, setSelectedLabels] =
|
|
924
|
-
const [instructions, setInstructions] =
|
|
925
|
-
const [rules, setRules] =
|
|
926
|
-
const [activeCategory, setActiveCategory] =
|
|
927
|
-
const [copied, setCopied] =
|
|
975
|
+
const [selectedLabels, setSelectedLabels] = useState5(/* @__PURE__ */ new Set());
|
|
976
|
+
const [instructions, setInstructions] = useState5("");
|
|
977
|
+
const [rules, setRules] = useState5("");
|
|
978
|
+
const [activeCategory, setActiveCategory] = useState5(null);
|
|
979
|
+
const [copied, setCopied] = useState5(false);
|
|
928
980
|
const imgInputRef = useRef4(null);
|
|
929
|
-
const [addingInCat, setAddingInCat] =
|
|
930
|
-
const [newLabel, setNewLabel] =
|
|
931
|
-
const [newValue, setNewValue] =
|
|
932
|
-
const [editingTag, setEditingTag] =
|
|
933
|
-
const [editLabel, setEditLabel] =
|
|
934
|
-
const [editValue, setEditValue] =
|
|
981
|
+
const [addingInCat, setAddingInCat] = useState5(null);
|
|
982
|
+
const [newLabel, setNewLabel] = useState5("");
|
|
983
|
+
const [newValue, setNewValue] = useState5("");
|
|
984
|
+
const [editingTag, setEditingTag] = useState5(null);
|
|
985
|
+
const [editLabel, setEditLabel] = useState5("");
|
|
986
|
+
const [editValue, setEditValue] = useState5("");
|
|
935
987
|
const longPressTimer = useRef4(null);
|
|
936
988
|
const longPressActivated = useRef4(false);
|
|
937
989
|
const toggleTag = (label) => {
|
|
@@ -1380,7 +1432,7 @@ var PromptTab = ({
|
|
|
1380
1432
|
};
|
|
1381
1433
|
|
|
1382
1434
|
// src/components/ProjectSyncTab.tsx
|
|
1383
|
-
import { useRef as useRef5, useState as
|
|
1435
|
+
import { useRef as useRef5, useState as useState6 } from "react";
|
|
1384
1436
|
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1385
1437
|
var ProjectSyncTab = ({
|
|
1386
1438
|
onProjectExport,
|
|
@@ -1397,12 +1449,12 @@ var ProjectSyncTab = ({
|
|
|
1397
1449
|
}) => {
|
|
1398
1450
|
const projectInputRef = useRef5(null);
|
|
1399
1451
|
const workspaceInputRef = useRef5(null);
|
|
1400
|
-
const [saveName, setSaveName] =
|
|
1401
|
-
const [isSaving, setIsSaving] =
|
|
1402
|
-
const [isExporting, setIsExporting] =
|
|
1403
|
-
const [syncState, setSyncState] =
|
|
1404
|
-
const [syncDiff, setSyncDiff] =
|
|
1405
|
-
const [selectedLocalIds, setSelectedLocalIds] =
|
|
1452
|
+
const [saveName, setSaveName] = useState6("");
|
|
1453
|
+
const [isSaving, setIsSaving] = useState6(false);
|
|
1454
|
+
const [isExporting, setIsExporting] = useState6(false);
|
|
1455
|
+
const [syncState, setSyncState] = useState6("idle");
|
|
1456
|
+
const [syncDiff, setSyncDiff] = useState6(null);
|
|
1457
|
+
const [selectedLocalIds, setSelectedLocalIds] = useState6(/* @__PURE__ */ new Set());
|
|
1406
1458
|
const handleExport = async () => {
|
|
1407
1459
|
if (!onProjectExport) return;
|
|
1408
1460
|
setIsExporting(true);
|
|
@@ -1604,8 +1656,1205 @@ var ProjectSyncTab = ({
|
|
|
1604
1656
|
] }) });
|
|
1605
1657
|
};
|
|
1606
1658
|
|
|
1607
|
-
// src/
|
|
1659
|
+
// src/lib/labHelpers.ts
|
|
1660
|
+
function groupGenerationsToLabItems(generations) {
|
|
1661
|
+
const map = /* @__PURE__ */ new Map();
|
|
1662
|
+
for (const gen of generations) {
|
|
1663
|
+
if (gen.status !== "done" || !gen.base64) continue;
|
|
1664
|
+
const prompt = gen.prompt || "";
|
|
1665
|
+
const existing = map.get(prompt);
|
|
1666
|
+
const frame = {
|
|
1667
|
+
id: gen.id,
|
|
1668
|
+
base64: gen.base64,
|
|
1669
|
+
mediaId: gen.mediaId,
|
|
1670
|
+
seed: gen.seed,
|
|
1671
|
+
model: gen.model,
|
|
1672
|
+
source: gen.type === "import" ? "zip-import" : "generated"
|
|
1673
|
+
};
|
|
1674
|
+
if (existing) {
|
|
1675
|
+
existing.frames.push(frame);
|
|
1676
|
+
} else {
|
|
1677
|
+
map.set(prompt, {
|
|
1678
|
+
id: gen.id,
|
|
1679
|
+
prompt,
|
|
1680
|
+
tags: gen.tags,
|
|
1681
|
+
frames: [frame]
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return Array.from(map.values());
|
|
1686
|
+
}
|
|
1687
|
+
function frameToGeneration(frame, item) {
|
|
1688
|
+
return {
|
|
1689
|
+
id: frame.id,
|
|
1690
|
+
nodeId: "1",
|
|
1691
|
+
base64: frame.base64,
|
|
1692
|
+
mediaId: frame.mediaId,
|
|
1693
|
+
prompt: item.prompt,
|
|
1694
|
+
seed: frame.seed,
|
|
1695
|
+
model: frame.model,
|
|
1696
|
+
timestamp: Date.now(),
|
|
1697
|
+
status: "done",
|
|
1698
|
+
tags: item.tags || [],
|
|
1699
|
+
type: frame.source === "zip-import" ? "import" : "generation"
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
function buildImageContext(images) {
|
|
1703
|
+
const active = images.filter((i) => i.sendToPromptAI);
|
|
1704
|
+
if (!active.length) return "";
|
|
1705
|
+
const lines = active.map((i) => {
|
|
1706
|
+
const role = i.roleForPrompt ? ` (${i.roleForPrompt})` : "";
|
|
1707
|
+
const promptSnippet = i.item.prompt ? `: "${i.item.prompt.slice(0, 120)}"` : "";
|
|
1708
|
+
return `- ${i.label}${role}${promptSnippet}`;
|
|
1709
|
+
});
|
|
1710
|
+
return `
|
|
1711
|
+
|
|
1712
|
+
Reference images:
|
|
1713
|
+
${lines.join("\n")}`;
|
|
1714
|
+
}
|
|
1715
|
+
function buildScanInstruction() {
|
|
1716
|
+
return {
|
|
1717
|
+
text: "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.",
|
|
1718
|
+
systemInstruction: "You are an expert image analyst and prompt engineer. Return ONLY the generation prompt, no headers, no quotes, no explanations."
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function buildRemixInstruction(images, userInstruction, currentPrompt) {
|
|
1722
|
+
const imgContext = buildImageContext(images);
|
|
1723
|
+
return {
|
|
1724
|
+
text: `Current prompt: "${currentPrompt}"
|
|
1725
|
+
|
|
1726
|
+
Instruction: ${userInstruction}${imgContext}
|
|
1727
|
+
|
|
1728
|
+
Create a creative variation of this prompt based on the instruction. Output ONLY the new prompt string.`,
|
|
1729
|
+
systemInstruction: "You are an expert prompt engineer. Return ONLY the new generation prompt, no headers, no quotes, no explanations."
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
function buildBlendInstruction(images, userInstruction) {
|
|
1733
|
+
const imgContext = buildImageContext(images);
|
|
1734
|
+
const promptList = images.map((i) => `${i.label}: "${i.item.prompt}"`).join("\n");
|
|
1735
|
+
return {
|
|
1736
|
+
text: `Blend these prompts into one new prompt:
|
|
1737
|
+
${promptList}
|
|
1738
|
+
|
|
1739
|
+
Instruction: ${userInstruction}${imgContext}
|
|
1740
|
+
|
|
1741
|
+
Output ONLY the blended prompt string.`,
|
|
1742
|
+
systemInstruction: "You are an expert prompt engineer. Blend the given prompts into one coherent generation prompt. Return ONLY the prompt string."
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1745
|
+
function buildCompareInstruction(images, userInstruction) {
|
|
1746
|
+
const imgContext = buildImageContext(images);
|
|
1747
|
+
return {
|
|
1748
|
+
text: `${userInstruction}${imgContext}
|
|
1749
|
+
|
|
1750
|
+
Analyze the provided images according to the instruction above.`,
|
|
1751
|
+
systemInstruction: "You are an expert image analyst. Provide a clear, structured analysis. Be concise and actionable."
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
function buildLoopInstruction(rounds, newInstruction) {
|
|
1755
|
+
const history = rounds.map(
|
|
1756
|
+
(r, i) => `Round ${i + 1} prompt: "${r.prompt}"
|
|
1757
|
+
Instruction given: "${r.instruction}"`
|
|
1758
|
+
).join("\n\n");
|
|
1759
|
+
const activeImages = rounds.flatMap((r) => r.images.filter((i) => i.sendToPromptAI));
|
|
1760
|
+
const imgContext = activeImages.length ? buildImageContext(activeImages) : "";
|
|
1761
|
+
return {
|
|
1762
|
+
text: `Previous rounds:
|
|
1763
|
+
${history}
|
|
1764
|
+
|
|
1765
|
+
New instruction: ${newInstruction}${imgContext}
|
|
1766
|
+
|
|
1767
|
+
Improve the prompt cumulatively based on all previous rounds and the new instruction. Output ONLY the new prompt string.`,
|
|
1768
|
+
systemInstruction: "You are an expert prompt engineer refining prompts iteratively. Each round the prompt should improve. Return ONLY the new prompt string."
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
function autoLabel(index) {
|
|
1772
|
+
return `Img${String.fromCharCode(65 + index)}`;
|
|
1773
|
+
}
|
|
1774
|
+
function buildReferenceImageMediaIds(images) {
|
|
1775
|
+
return images.filter((i) => i.sendToImageGen && i.frame.mediaId).map((i) => i.frame.mediaId);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/components/labs/LabsTab.tsx
|
|
1779
|
+
import { useState as useState12 } from "react";
|
|
1780
|
+
|
|
1781
|
+
// src/components/labs/LabRemix.tsx
|
|
1782
|
+
import { useState as useState8 } from "react";
|
|
1783
|
+
|
|
1784
|
+
// src/components/labs/LabImagePicker.tsx
|
|
1785
|
+
import { useState as useState7 } from "react";
|
|
1608
1786
|
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1787
|
+
var LabImagePicker = ({
|
|
1788
|
+
availableItems,
|
|
1789
|
+
recentItems,
|
|
1790
|
+
onSelect,
|
|
1791
|
+
onClose,
|
|
1792
|
+
title = "Bild w\xE4hlen"
|
|
1793
|
+
}) => {
|
|
1794
|
+
const [search, setSearch] = useState7("");
|
|
1795
|
+
const [drillItem, setDrillItem] = useState7(null);
|
|
1796
|
+
const filtered = availableItems.filter(
|
|
1797
|
+
(item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
|
|
1798
|
+
);
|
|
1799
|
+
const handleItemClick = (item) => {
|
|
1800
|
+
if (item.frames.length === 1) {
|
|
1801
|
+
onSelect(item, item.frames[0]);
|
|
1802
|
+
onClose();
|
|
1803
|
+
} else {
|
|
1804
|
+
setDrillItem(item);
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
const renderItemCard = (item) => {
|
|
1808
|
+
const firstFrame = item.frames[0];
|
|
1809
|
+
if (!firstFrame) return null;
|
|
1810
|
+
return /* @__PURE__ */ jsxs11(
|
|
1811
|
+
"button",
|
|
1812
|
+
{
|
|
1813
|
+
onClick: () => handleItemClick(item),
|
|
1814
|
+
className: "flex flex-col gap-1 p-2 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors text-left",
|
|
1815
|
+
children: [
|
|
1816
|
+
/* @__PURE__ */ jsxs11("div", { className: "relative w-full aspect-square rounded-lg overflow-hidden bg-black/40", children: [
|
|
1817
|
+
/* @__PURE__ */ jsx13("img", { src: firstFrame.base64, className: "w-full h-full object-cover" }),
|
|
1818
|
+
item.frames.length > 1 && /* @__PURE__ */ jsxs11("span", { className: "absolute bottom-1 right-1 text-[9px] font-bold bg-black/70 text-white/70 px-1.5 py-0.5 rounded", children: [
|
|
1819
|
+
item.frames.length,
|
|
1820
|
+
"x"
|
|
1821
|
+
] })
|
|
1822
|
+
] }),
|
|
1823
|
+
/* @__PURE__ */ jsx13("p", { className: "text-[10px] text-white/50 leading-tight line-clamp-2", children: item.prompt || "(kein Prompt)" })
|
|
1824
|
+
]
|
|
1825
|
+
},
|
|
1826
|
+
item.id
|
|
1827
|
+
);
|
|
1828
|
+
};
|
|
1829
|
+
if (drillItem) {
|
|
1830
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col h-full", children: [
|
|
1831
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 px-4 py-3 border-b border-white/5 shrink-0", children: [
|
|
1832
|
+
/* @__PURE__ */ jsx13("button", { onClick: () => setDrillItem(null), className: "text-white/40 active:text-white shrink-0", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "arrow_back" }) }),
|
|
1833
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60 truncate flex-1", children: drillItem.prompt || "(kein Prompt)" }),
|
|
1834
|
+
/* @__PURE__ */ jsx13("button", { onClick: onClose, className: "text-white/30 active:text-white shrink-0", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
|
|
1835
|
+
] }),
|
|
1836
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 py-3", children: [
|
|
1837
|
+
/* @__PURE__ */ jsxs11("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2", children: [
|
|
1838
|
+
drillItem.frames.length,
|
|
1839
|
+
" Varianten"
|
|
1840
|
+
] }),
|
|
1841
|
+
/* @__PURE__ */ jsx13("div", { className: "grid grid-cols-3 gap-2", children: drillItem.frames.map((frame, i) => /* @__PURE__ */ jsxs11(
|
|
1842
|
+
"button",
|
|
1843
|
+
{
|
|
1844
|
+
onClick: () => {
|
|
1845
|
+
onSelect(drillItem, frame);
|
|
1846
|
+
onClose();
|
|
1847
|
+
},
|
|
1848
|
+
className: "flex flex-col gap-1 p-2 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors text-left",
|
|
1849
|
+
children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs11("div", { className: "relative w-full aspect-square rounded-lg overflow-hidden bg-black/40", children: [
|
|
1851
|
+
/* @__PURE__ */ jsx13("img", { src: frame.base64, className: "w-full h-full object-cover" }),
|
|
1852
|
+
/* @__PURE__ */ jsxs11("span", { className: "absolute bottom-1 left-1 text-[9px] font-bold bg-black/70 text-white/50 px-1.5 py-0.5 rounded", children: [
|
|
1853
|
+
"#",
|
|
1854
|
+
i + 1
|
|
1855
|
+
] })
|
|
1856
|
+
] }),
|
|
1857
|
+
frame.seed != null && /* @__PURE__ */ jsxs11("p", { className: "text-[9px] text-white/30 font-mono truncate", children: [
|
|
1858
|
+
"seed ",
|
|
1859
|
+
frame.seed
|
|
1860
|
+
] })
|
|
1861
|
+
]
|
|
1862
|
+
},
|
|
1863
|
+
frame.id
|
|
1864
|
+
)) })
|
|
1865
|
+
] })
|
|
1866
|
+
] });
|
|
1867
|
+
}
|
|
1868
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-col h-full", children: [
|
|
1869
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between px-4 py-3 border-b border-white/5 shrink-0", children: [
|
|
1870
|
+
/* @__PURE__ */ jsx13("span", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60", children: title }),
|
|
1871
|
+
/* @__PURE__ */ jsx13("button", { onClick: onClose, className: "text-white/30 active:text-white", children: /* @__PURE__ */ jsx13("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
|
|
1872
|
+
] }),
|
|
1873
|
+
/* @__PURE__ */ jsx13("div", { className: "px-3 py-2 shrink-0", children: /* @__PURE__ */ jsx13(
|
|
1874
|
+
"input",
|
|
1875
|
+
{
|
|
1876
|
+
value: search,
|
|
1877
|
+
onChange: (e) => setSearch(e.target.value),
|
|
1878
|
+
placeholder: "Suchen...",
|
|
1879
|
+
className: "w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-[12px] text-white/80 outline-none focus:border-white/20"
|
|
1880
|
+
}
|
|
1881
|
+
) }),
|
|
1882
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 pb-3", children: [
|
|
1883
|
+
recentItems.length > 0 && !search && /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
1884
|
+
/* @__PURE__ */ jsx13("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2 mt-1", children: "Zuletzt verwendet" }),
|
|
1885
|
+
/* @__PURE__ */ jsx13("div", { className: "grid grid-cols-3 gap-2 mb-4", children: recentItems.slice(0, 6).map(renderItemCard) }),
|
|
1886
|
+
/* @__PURE__ */ jsx13("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2", children: "Alle Bilder" })
|
|
1887
|
+
] }),
|
|
1888
|
+
/* @__PURE__ */ jsx13("div", { className: "grid grid-cols-3 gap-2", children: filtered.map(renderItemCard) }),
|
|
1889
|
+
filtered.length === 0 && /* @__PURE__ */ jsx13("p", { className: "text-center text-[11px] text-white/20 py-8", children: "Keine Bilder vorhanden" })
|
|
1890
|
+
] })
|
|
1891
|
+
] });
|
|
1892
|
+
};
|
|
1893
|
+
|
|
1894
|
+
// src/components/labs/LabRemix.tsx
|
|
1895
|
+
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1896
|
+
var LabRemix = ({ services, onResult }) => {
|
|
1897
|
+
const [showPicker, setShowPicker] = useState8(false);
|
|
1898
|
+
const [selected, setSelected] = useState8(null);
|
|
1899
|
+
const [instruction, setInstruction] = useState8("");
|
|
1900
|
+
const [generatedPrompt, setGeneratedPrompt] = useState8("");
|
|
1901
|
+
const [resultImage, setResultImage] = useState8(null);
|
|
1902
|
+
const [isGeneratingPrompt, setIsGeneratingPrompt] = useState8(false);
|
|
1903
|
+
const [isGeneratingImage, setIsGeneratingImage] = useState8(false);
|
|
1904
|
+
const handleSelectImage = (item, frame) => {
|
|
1905
|
+
services.onItemUsed(item);
|
|
1906
|
+
setSelected({
|
|
1907
|
+
item,
|
|
1908
|
+
frame,
|
|
1909
|
+
label: autoLabel(0),
|
|
1910
|
+
sendToPromptAI: true,
|
|
1911
|
+
sendToImageGen: false,
|
|
1912
|
+
roleForPrompt: "",
|
|
1913
|
+
roleForImage: ""
|
|
1914
|
+
});
|
|
1915
|
+
setGeneratedPrompt("");
|
|
1916
|
+
setResultImage(null);
|
|
1917
|
+
};
|
|
1918
|
+
const handleGeneratePrompt = async () => {
|
|
1919
|
+
if (!selected || !instruction.trim()) return;
|
|
1920
|
+
setIsGeneratingPrompt(true);
|
|
1921
|
+
try {
|
|
1922
|
+
const { text, systemInstruction } = buildRemixInstruction(
|
|
1923
|
+
[selected],
|
|
1924
|
+
instruction,
|
|
1925
|
+
selected.item.prompt
|
|
1926
|
+
);
|
|
1927
|
+
const images = selected.sendToPromptAI ? [{ base64: selected.frame.base64, mimeType: "image/png" }] : void 0;
|
|
1928
|
+
const result = await services.generateText(text, { systemInstruction, images });
|
|
1929
|
+
setGeneratedPrompt(result);
|
|
1930
|
+
} finally {
|
|
1931
|
+
setIsGeneratingPrompt(false);
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
const handleGenerateImage = async () => {
|
|
1935
|
+
if (!generatedPrompt) return;
|
|
1936
|
+
setIsGeneratingImage(true);
|
|
1937
|
+
try {
|
|
1938
|
+
const refIds = buildReferenceImageMediaIds(selected ? [selected] : []);
|
|
1939
|
+
const { base64, mediaId } = await services.generateImage({
|
|
1940
|
+
prompt: generatedPrompt,
|
|
1941
|
+
aspectRatio: selected?.frame.aspectRatio || "1:1",
|
|
1942
|
+
modelDisplayName: selected?.frame.model || "\u{1F34C} Nano Banana Pro",
|
|
1943
|
+
...refIds.length ? { referenceImageMediaIds: refIds } : {}
|
|
1944
|
+
});
|
|
1945
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
1946
|
+
setResultImage(newBase64);
|
|
1947
|
+
const frameId = crypto.randomUUID();
|
|
1948
|
+
const newItem = {
|
|
1949
|
+
id: frameId,
|
|
1950
|
+
prompt: generatedPrompt,
|
|
1951
|
+
tags: selected?.item.tags || [],
|
|
1952
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
1953
|
+
};
|
|
1954
|
+
services.saveResult?.(newItem);
|
|
1955
|
+
onResult?.(newItem);
|
|
1956
|
+
} finally {
|
|
1957
|
+
setIsGeneratingImage(false);
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
if (showPicker) {
|
|
1961
|
+
return /* @__PURE__ */ jsx14(
|
|
1962
|
+
LabImagePicker,
|
|
1963
|
+
{
|
|
1964
|
+
availableItems: services.availableItems,
|
|
1965
|
+
recentItems: services.recentItems,
|
|
1966
|
+
onSelect: handleSelectImage,
|
|
1967
|
+
onClose: () => setShowPicker(false),
|
|
1968
|
+
title: "Basis-Bild w\xE4hlen"
|
|
1969
|
+
}
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
return /* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
1973
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
1974
|
+
/* @__PURE__ */ jsx14("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Basis-Bild" }),
|
|
1975
|
+
selected ? /* @__PURE__ */ jsxs12("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
1976
|
+
/* @__PURE__ */ jsx14("img", { src: selected.frame.base64, className: "w-16 h-16 object-cover rounded-lg shrink-0" }),
|
|
1977
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex-1 min-w-0 flex flex-col gap-1.5", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx14("p", { className: "text-[10px] text-white/60 leading-tight line-clamp-2", children: selected.item.prompt }),
|
|
1979
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-[10px] text-white/50", children: [
|
|
1980
|
+
/* @__PURE__ */ jsx14(
|
|
1981
|
+
"input",
|
|
1982
|
+
{
|
|
1983
|
+
type: "checkbox",
|
|
1984
|
+
checked: selected.sendToPromptAI,
|
|
1985
|
+
onChange: (e) => setSelected((s) => s ? { ...s, sendToPromptAI: e.target.checked } : s)
|
|
1986
|
+
}
|
|
1987
|
+
),
|
|
1988
|
+
"Bild an KI (Prompt)"
|
|
1989
|
+
] }),
|
|
1990
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-2 text-[10px] text-white/50", children: [
|
|
1991
|
+
/* @__PURE__ */ jsx14(
|
|
1992
|
+
"input",
|
|
1993
|
+
{
|
|
1994
|
+
type: "checkbox",
|
|
1995
|
+
checked: selected.sendToImageGen,
|
|
1996
|
+
onChange: (e) => setSelected((s) => s ? { ...s, sendToImageGen: e.target.checked } : s)
|
|
1997
|
+
}
|
|
1998
|
+
),
|
|
1999
|
+
"Bild als Referenz (Generierung)"
|
|
2000
|
+
] }),
|
|
2001
|
+
/* @__PURE__ */ jsx14(
|
|
2002
|
+
"input",
|
|
2003
|
+
{
|
|
2004
|
+
value: selected.roleForPrompt || "",
|
|
2005
|
+
onChange: (e) => setSelected((s) => s ? { ...s, roleForPrompt: e.target.value } : s),
|
|
2006
|
+
placeholder: "Rolle z.B. Subjekt, Farbreferenz...",
|
|
2007
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-1 text-[10px] text-white/70 outline-none"
|
|
2008
|
+
}
|
|
2009
|
+
)
|
|
2010
|
+
] }),
|
|
2011
|
+
/* @__PURE__ */ jsx14("button", { onClick: () => setShowPicker(true), className: "text-white/30 active:text-white self-start", children: /* @__PURE__ */ jsx14("span", { className: "material-symbols-outlined text-[18px]", children: "swap_horiz" }) })
|
|
2012
|
+
] }) : /* @__PURE__ */ jsxs12(
|
|
2013
|
+
"button",
|
|
2014
|
+
{
|
|
2015
|
+
onClick: () => setShowPicker(true),
|
|
2016
|
+
className: "w-full py-6 rounded-xl border border-dashed border-white/20 text-[11px] text-white/30 active:bg-white/5 flex items-center justify-center gap-2",
|
|
2017
|
+
children: [
|
|
2018
|
+
/* @__PURE__ */ jsx14("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }),
|
|
2019
|
+
"Bild w\xE4hlen"
|
|
2020
|
+
]
|
|
2021
|
+
}
|
|
2022
|
+
)
|
|
2023
|
+
] }),
|
|
2024
|
+
/* @__PURE__ */ jsxs12("div", { children: [
|
|
2025
|
+
/* @__PURE__ */ jsx14("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Anweisung" }),
|
|
2026
|
+
/* @__PURE__ */ jsx14(
|
|
2027
|
+
"textarea",
|
|
2028
|
+
{
|
|
2029
|
+
value: instruction,
|
|
2030
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2031
|
+
placeholder: "z.B. Mache es dramatischer, andere Lichtstimmung...",
|
|
2032
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2033
|
+
rows: 3
|
|
2034
|
+
}
|
|
2035
|
+
)
|
|
2036
|
+
] }),
|
|
2037
|
+
services.workspaceTags && /* @__PURE__ */ jsx14("p", { className: "text-[9px] text-white/20 italic", children: "Tags: Workspace-Tags k\xF6nnen unter der Anweisung erg\xE4nzt werden (folgt in n\xE4chstem Update)" }),
|
|
2038
|
+
/* @__PURE__ */ jsx14(
|
|
2039
|
+
"button",
|
|
2040
|
+
{
|
|
2041
|
+
onClick: handleGeneratePrompt,
|
|
2042
|
+
disabled: !selected || !instruction.trim() || isGeneratingPrompt,
|
|
2043
|
+
className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
|
|
2044
|
+
children: isGeneratingPrompt ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
2045
|
+
/* @__PURE__ */ jsx14("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2046
|
+
/* @__PURE__ */ jsx14("span", { children: "Generiere Prompt..." })
|
|
2047
|
+
] }) : /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
2048
|
+
/* @__PURE__ */ jsx14("span", { className: "material-symbols-outlined text-[16px]", children: "auto_fix_high" }),
|
|
2049
|
+
/* @__PURE__ */ jsx14("span", { children: "Prompt generieren" })
|
|
2050
|
+
] })
|
|
2051
|
+
}
|
|
2052
|
+
),
|
|
2053
|
+
generatedPrompt && /* @__PURE__ */ jsxs12("div", { children: [
|
|
2054
|
+
/* @__PURE__ */ jsx14("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Generierter Prompt" }),
|
|
2055
|
+
/* @__PURE__ */ jsx14(
|
|
2056
|
+
"textarea",
|
|
2057
|
+
{
|
|
2058
|
+
value: generatedPrompt,
|
|
2059
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2060
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2061
|
+
rows: 4
|
|
2062
|
+
}
|
|
2063
|
+
),
|
|
2064
|
+
/* @__PURE__ */ jsx14(
|
|
2065
|
+
"button",
|
|
2066
|
+
{
|
|
2067
|
+
onClick: handleGenerateImage,
|
|
2068
|
+
disabled: isGeneratingImage,
|
|
2069
|
+
className: "w-full mt-2 py-3 rounded-xl bg-blue-600/80 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-blue-600 flex items-center justify-center gap-2",
|
|
2070
|
+
children: isGeneratingImage ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
2071
|
+
/* @__PURE__ */ jsx14("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2072
|
+
/* @__PURE__ */ jsx14("span", { children: "Generiere Bild..." })
|
|
2073
|
+
] }) : /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
2074
|
+
/* @__PURE__ */ jsx14("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2075
|
+
/* @__PURE__ */ jsx14("span", { children: "Bild generieren" })
|
|
2076
|
+
] })
|
|
2077
|
+
}
|
|
2078
|
+
)
|
|
2079
|
+
] }),
|
|
2080
|
+
resultImage && /* @__PURE__ */ jsx14("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ jsx14("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2081
|
+
] });
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
// src/components/labs/LabBlend.tsx
|
|
2085
|
+
import { useState as useState9 } from "react";
|
|
2086
|
+
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2087
|
+
var LabBlend = ({ services, onResult }) => {
|
|
2088
|
+
const [showPickerFor, setShowPickerFor] = useState9(null);
|
|
2089
|
+
const [selectedImages, setSelectedImages] = useState9([]);
|
|
2090
|
+
const [instruction, setInstruction] = useState9("");
|
|
2091
|
+
const [generatedPrompt, setGeneratedPrompt] = useState9("");
|
|
2092
|
+
const [resultImage, setResultImage] = useState9(null);
|
|
2093
|
+
const [isGeneratingPrompt, setIsGeneratingPrompt] = useState9(false);
|
|
2094
|
+
const [isGeneratingImage, setIsGeneratingImage] = useState9(false);
|
|
2095
|
+
const handleSelectImage = (index, item, frame) => {
|
|
2096
|
+
services.onItemUsed(item);
|
|
2097
|
+
const newImg = {
|
|
2098
|
+
item,
|
|
2099
|
+
frame,
|
|
2100
|
+
label: autoLabel(index),
|
|
2101
|
+
sendToPromptAI: true,
|
|
2102
|
+
sendToImageGen: false,
|
|
2103
|
+
roleForPrompt: "",
|
|
2104
|
+
roleForImage: ""
|
|
2105
|
+
};
|
|
2106
|
+
setSelectedImages((prev) => {
|
|
2107
|
+
const next = [...prev];
|
|
2108
|
+
next[index] = newImg;
|
|
2109
|
+
return next;
|
|
2110
|
+
});
|
|
2111
|
+
setShowPickerFor(null);
|
|
2112
|
+
setGeneratedPrompt("");
|
|
2113
|
+
setResultImage(null);
|
|
2114
|
+
};
|
|
2115
|
+
const addSlot = () => setSelectedImages((prev) => [...prev, null]);
|
|
2116
|
+
const removeSlot = (i) => setSelectedImages((prev) => prev.filter((_, idx) => idx !== i));
|
|
2117
|
+
const updateImg = (i, updates) => setSelectedImages((prev) => prev.map((img, idx) => idx === i && img ? { ...img, ...updates } : img));
|
|
2118
|
+
const handleGeneratePrompt = async () => {
|
|
2119
|
+
const filled = selectedImages.filter(Boolean);
|
|
2120
|
+
if (!filled.length || !instruction.trim()) return;
|
|
2121
|
+
setIsGeneratingPrompt(true);
|
|
2122
|
+
try {
|
|
2123
|
+
const { text, systemInstruction } = buildBlendInstruction(filled, instruction);
|
|
2124
|
+
const images = filled.filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2125
|
+
const result = await services.generateText(text, { systemInstruction, images: images.length ? images : void 0 });
|
|
2126
|
+
setGeneratedPrompt(result);
|
|
2127
|
+
} finally {
|
|
2128
|
+
setIsGeneratingPrompt(false);
|
|
2129
|
+
}
|
|
2130
|
+
};
|
|
2131
|
+
const handleGenerateImage = async () => {
|
|
2132
|
+
if (!generatedPrompt) return;
|
|
2133
|
+
setIsGeneratingImage(true);
|
|
2134
|
+
try {
|
|
2135
|
+
const filled = selectedImages.filter(Boolean);
|
|
2136
|
+
const refIds = buildReferenceImageMediaIds(filled);
|
|
2137
|
+
const { base64, mediaId } = await services.generateImage({
|
|
2138
|
+
prompt: generatedPrompt,
|
|
2139
|
+
aspectRatio: "1:1",
|
|
2140
|
+
modelDisplayName: "\u{1F34C} Nano Banana Pro",
|
|
2141
|
+
...refIds.length ? { referenceImageMediaIds: refIds } : {}
|
|
2142
|
+
});
|
|
2143
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2144
|
+
setResultImage(newBase64);
|
|
2145
|
+
const frameId = crypto.randomUUID();
|
|
2146
|
+
const newItem = {
|
|
2147
|
+
id: frameId,
|
|
2148
|
+
prompt: generatedPrompt,
|
|
2149
|
+
tags: [],
|
|
2150
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
2151
|
+
};
|
|
2152
|
+
services.saveResult?.(newItem);
|
|
2153
|
+
onResult?.(newItem);
|
|
2154
|
+
} finally {
|
|
2155
|
+
setIsGeneratingImage(false);
|
|
2156
|
+
}
|
|
2157
|
+
};
|
|
2158
|
+
if (showPickerFor !== null) {
|
|
2159
|
+
return /* @__PURE__ */ jsx15(
|
|
2160
|
+
LabImagePicker,
|
|
2161
|
+
{
|
|
2162
|
+
availableItems: services.availableItems,
|
|
2163
|
+
recentItems: services.recentItems,
|
|
2164
|
+
onSelect: (item, frame) => handleSelectImage(showPickerFor, item, frame),
|
|
2165
|
+
onClose: () => setShowPickerFor(null),
|
|
2166
|
+
title: `${autoLabel(showPickerFor)} w\xE4hlen`
|
|
2167
|
+
}
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2171
|
+
/* @__PURE__ */ jsx15("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder" }),
|
|
2172
|
+
selectedImages.map((img, i) => /* @__PURE__ */ jsxs13("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2173
|
+
/* @__PURE__ */ jsx15("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
|
|
2174
|
+
img ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
2175
|
+
/* @__PURE__ */ jsx15("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0", onClick: () => setShowPickerFor(i) }),
|
|
2176
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
|
|
2177
|
+
/* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
|
|
2178
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex gap-3", children: [
|
|
2179
|
+
/* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2180
|
+
/* @__PURE__ */ jsx15("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updateImg(i, { sendToPromptAI: e.target.checked }) }),
|
|
2181
|
+
"KI-Prompt"
|
|
2182
|
+
] }),
|
|
2183
|
+
/* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2184
|
+
/* @__PURE__ */ jsx15("input", { type: "checkbox", checked: img.sendToImageGen, onChange: (e) => updateImg(i, { sendToImageGen: e.target.checked }) }),
|
|
2185
|
+
"Referenz"
|
|
2186
|
+
] })
|
|
2187
|
+
] }),
|
|
2188
|
+
/* @__PURE__ */ jsx15(
|
|
2189
|
+
"input",
|
|
2190
|
+
{
|
|
2191
|
+
value: img.roleForPrompt || "",
|
|
2192
|
+
onChange: (e) => updateImg(i, { roleForPrompt: e.target.value }),
|
|
2193
|
+
placeholder: "Rolle f\xFCr Prompt...",
|
|
2194
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2195
|
+
}
|
|
2196
|
+
),
|
|
2197
|
+
/* @__PURE__ */ jsx15(
|
|
2198
|
+
"input",
|
|
2199
|
+
{
|
|
2200
|
+
value: img.roleForImage || "",
|
|
2201
|
+
onChange: (e) => updateImg(i, { roleForImage: e.target.value }),
|
|
2202
|
+
placeholder: "Rolle f\xFCr Generierung...",
|
|
2203
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2204
|
+
}
|
|
2205
|
+
)
|
|
2206
|
+
] })
|
|
2207
|
+
] }) : /* @__PURE__ */ jsx15("button", { onClick: () => setShowPickerFor(i), className: "flex-1 py-3 border border-dashed border-white/20 rounded-lg text-[10px] text-white/30 active:bg-white/5", children: "Bild w\xE4hlen" }),
|
|
2208
|
+
/* @__PURE__ */ jsx15("button", { onClick: () => removeSlot(i), className: "text-white/20 active:text-red-400 self-start", children: /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[16px]", children: "close" }) })
|
|
2209
|
+
] }, i)),
|
|
2210
|
+
/* @__PURE__ */ jsxs13(
|
|
2211
|
+
"button",
|
|
2212
|
+
{
|
|
2213
|
+
onClick: addSlot,
|
|
2214
|
+
className: "w-full py-2 border border-dashed border-white/10 rounded-xl text-[10px] text-white/30 active:bg-white/5 flex items-center justify-center gap-1",
|
|
2215
|
+
children: [
|
|
2216
|
+
/* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2217
|
+
"Bild hinzuf\xFCgen"
|
|
2218
|
+
]
|
|
2219
|
+
}
|
|
2220
|
+
),
|
|
2221
|
+
/* @__PURE__ */ jsxs13("div", { children: [
|
|
2222
|
+
/* @__PURE__ */ jsx15("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Blend-Anweisung" }),
|
|
2223
|
+
/* @__PURE__ */ jsx15(
|
|
2224
|
+
"textarea",
|
|
2225
|
+
{
|
|
2226
|
+
value: instruction,
|
|
2227
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2228
|
+
placeholder: "z.B. Kombiniere die Stimmung von ImgA mit der Komposition von ImgB...",
|
|
2229
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2230
|
+
rows: 3
|
|
2231
|
+
}
|
|
2232
|
+
)
|
|
2233
|
+
] }),
|
|
2234
|
+
/* @__PURE__ */ jsx15(
|
|
2235
|
+
"button",
|
|
2236
|
+
{
|
|
2237
|
+
onClick: handleGeneratePrompt,
|
|
2238
|
+
disabled: selectedImages.filter(Boolean).length < 2 || !instruction.trim() || isGeneratingPrompt,
|
|
2239
|
+
className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
|
|
2240
|
+
children: isGeneratingPrompt ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
2241
|
+
/* @__PURE__ */ jsx15("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2242
|
+
/* @__PURE__ */ jsx15("span", { children: "Blend l\xE4uft..." })
|
|
2243
|
+
] }) : /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
2244
|
+
/* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[16px]", children: "merge" }),
|
|
2245
|
+
/* @__PURE__ */ jsx15("span", { children: "Prompts blenden" })
|
|
2246
|
+
] })
|
|
2247
|
+
}
|
|
2248
|
+
),
|
|
2249
|
+
generatedPrompt && /* @__PURE__ */ jsxs13("div", { children: [
|
|
2250
|
+
/* @__PURE__ */ jsx15("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Blend-Prompt" }),
|
|
2251
|
+
/* @__PURE__ */ jsx15(
|
|
2252
|
+
"textarea",
|
|
2253
|
+
{
|
|
2254
|
+
value: generatedPrompt,
|
|
2255
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2256
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2257
|
+
rows: 4
|
|
2258
|
+
}
|
|
2259
|
+
),
|
|
2260
|
+
/* @__PURE__ */ jsx15(
|
|
2261
|
+
"button",
|
|
2262
|
+
{
|
|
2263
|
+
onClick: handleGenerateImage,
|
|
2264
|
+
disabled: isGeneratingImage,
|
|
2265
|
+
className: "w-full mt-2 py-3 rounded-xl bg-blue-600/80 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-blue-600 flex items-center justify-center gap-2",
|
|
2266
|
+
children: isGeneratingImage ? /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
2267
|
+
/* @__PURE__ */ jsx15("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2268
|
+
/* @__PURE__ */ jsx15("span", { children: "Generiere..." })
|
|
2269
|
+
] }) : /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
2270
|
+
/* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2271
|
+
/* @__PURE__ */ jsx15("span", { children: "Bild generieren" })
|
|
2272
|
+
] })
|
|
2273
|
+
}
|
|
2274
|
+
)
|
|
2275
|
+
] }),
|
|
2276
|
+
resultImage && /* @__PURE__ */ jsx15("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ jsx15("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2277
|
+
] });
|
|
2278
|
+
};
|
|
2279
|
+
|
|
2280
|
+
// src/components/labs/LabCompare.tsx
|
|
2281
|
+
import { useState as useState10 } from "react";
|
|
2282
|
+
import { Fragment as Fragment7, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2283
|
+
var LabCompare = ({ services, onResult }) => {
|
|
2284
|
+
const [showPickerFor, setShowPickerFor] = useState10(null);
|
|
2285
|
+
const [selectedImages, setSelectedImages] = useState10([]);
|
|
2286
|
+
const [instruction, setInstruction] = useState10("");
|
|
2287
|
+
const [analysis, setAnalysis] = useState10("");
|
|
2288
|
+
const [generatedPrompt, setGeneratedPrompt] = useState10("");
|
|
2289
|
+
const [resultImage, setResultImage] = useState10(null);
|
|
2290
|
+
const [isAnalyzing, setIsAnalyzing] = useState10(false);
|
|
2291
|
+
const [isGeneratingImage, setIsGeneratingImage] = useState10(false);
|
|
2292
|
+
const handleSelectImage = (index, item, frame) => {
|
|
2293
|
+
services.onItemUsed(item);
|
|
2294
|
+
const newImg = {
|
|
2295
|
+
item,
|
|
2296
|
+
frame,
|
|
2297
|
+
label: autoLabel(index),
|
|
2298
|
+
sendToPromptAI: true,
|
|
2299
|
+
sendToImageGen: false,
|
|
2300
|
+
roleForPrompt: "",
|
|
2301
|
+
roleForImage: ""
|
|
2302
|
+
};
|
|
2303
|
+
setSelectedImages((prev) => {
|
|
2304
|
+
const next = [...prev];
|
|
2305
|
+
next[index] = newImg;
|
|
2306
|
+
return next;
|
|
2307
|
+
});
|
|
2308
|
+
setShowPickerFor(null);
|
|
2309
|
+
setAnalysis("");
|
|
2310
|
+
};
|
|
2311
|
+
const updateImg = (i, updates) => setSelectedImages((prev) => prev.map((img, idx) => idx === i && img ? { ...img, ...updates } : img));
|
|
2312
|
+
const addSlot = () => setSelectedImages((prev) => [...prev, null]);
|
|
2313
|
+
const removeSlot = (i) => setSelectedImages((prev) => prev.filter((_, idx) => idx !== i));
|
|
2314
|
+
const handleAnalyze = async () => {
|
|
2315
|
+
const filled = selectedImages.filter(Boolean);
|
|
2316
|
+
if (!filled.length || !instruction.trim()) return;
|
|
2317
|
+
setIsAnalyzing(true);
|
|
2318
|
+
try {
|
|
2319
|
+
const { text, systemInstruction } = buildCompareInstruction(filled, instruction);
|
|
2320
|
+
const images = filled.filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2321
|
+
const result = await services.generateText(text, { systemInstruction, images: images.length ? images : void 0 });
|
|
2322
|
+
setAnalysis(result);
|
|
2323
|
+
setGeneratedPrompt("");
|
|
2324
|
+
} finally {
|
|
2325
|
+
setIsAnalyzing(false);
|
|
2326
|
+
}
|
|
2327
|
+
};
|
|
2328
|
+
const handleGenerateImage = async () => {
|
|
2329
|
+
if (!generatedPrompt) return;
|
|
2330
|
+
setIsGeneratingImage(true);
|
|
2331
|
+
try {
|
|
2332
|
+
const { base64, mediaId } = await services.generateImage({ prompt: generatedPrompt, aspectRatio: "1:1", modelDisplayName: "\u{1F34C} Nano Banana Pro" });
|
|
2333
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2334
|
+
setResultImage(newBase64);
|
|
2335
|
+
const frameId = crypto.randomUUID();
|
|
2336
|
+
const newItem = {
|
|
2337
|
+
id: frameId,
|
|
2338
|
+
prompt: generatedPrompt,
|
|
2339
|
+
tags: [],
|
|
2340
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
2341
|
+
};
|
|
2342
|
+
services.saveResult?.(newItem);
|
|
2343
|
+
onResult?.(newItem);
|
|
2344
|
+
} finally {
|
|
2345
|
+
setIsGeneratingImage(false);
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
if (showPickerFor !== null) {
|
|
2349
|
+
return /* @__PURE__ */ jsx16(
|
|
2350
|
+
LabImagePicker,
|
|
2351
|
+
{
|
|
2352
|
+
availableItems: services.availableItems,
|
|
2353
|
+
recentItems: services.recentItems,
|
|
2354
|
+
onSelect: (item, frame) => handleSelectImage(showPickerFor, item, frame),
|
|
2355
|
+
onClose: () => setShowPickerFor(null),
|
|
2356
|
+
title: `${autoLabel(showPickerFor)} w\xE4hlen`
|
|
2357
|
+
}
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
2360
|
+
return /* @__PURE__ */ jsxs14("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2361
|
+
/* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder zum Vergleichen" }),
|
|
2362
|
+
selectedImages.map((img, i) => /* @__PURE__ */ jsxs14("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2363
|
+
/* @__PURE__ */ jsx16("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
|
|
2364
|
+
img ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2365
|
+
/* @__PURE__ */ jsx16("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0 cursor-pointer", onClick: () => setShowPickerFor(i) }),
|
|
2366
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
|
|
2367
|
+
/* @__PURE__ */ jsx16("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
|
|
2368
|
+
/* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2369
|
+
/* @__PURE__ */ jsx16("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updateImg(i, { sendToPromptAI: e.target.checked }) }),
|
|
2370
|
+
"Bild an KI mitgeben"
|
|
2371
|
+
] }),
|
|
2372
|
+
/* @__PURE__ */ jsx16(
|
|
2373
|
+
"input",
|
|
2374
|
+
{
|
|
2375
|
+
value: img.roleForPrompt || "",
|
|
2376
|
+
onChange: (e) => updateImg(i, { roleForPrompt: e.target.value }),
|
|
2377
|
+
placeholder: "Rolle z.B. Referenz A, Stilvergleich...",
|
|
2378
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2379
|
+
}
|
|
2380
|
+
)
|
|
2381
|
+
] })
|
|
2382
|
+
] }) : /* @__PURE__ */ jsx16("button", { onClick: () => setShowPickerFor(i), className: "flex-1 py-3 border border-dashed border-white/20 rounded-lg text-[10px] text-white/30 active:bg-white/5", children: "Bild w\xE4hlen" }),
|
|
2383
|
+
/* @__PURE__ */ jsx16("button", { onClick: () => removeSlot(i), className: "text-white/20 active:text-red-400 self-start", children: /* @__PURE__ */ jsx16("span", { className: "material-symbols-outlined text-[16px]", children: "close" }) })
|
|
2384
|
+
] }, i)),
|
|
2385
|
+
/* @__PURE__ */ jsxs14("button", { onClick: addSlot, className: "w-full py-2 border border-dashed border-white/10 rounded-xl text-[10px] text-white/30 active:bg-white/5 flex items-center justify-center gap-1", children: [
|
|
2386
|
+
/* @__PURE__ */ jsx16("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2387
|
+
"Bild hinzuf\xFCgen"
|
|
2388
|
+
] }),
|
|
2389
|
+
/* @__PURE__ */ jsxs14("div", { children: [
|
|
2390
|
+
/* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Analyse-Anweisung" }),
|
|
2391
|
+
/* @__PURE__ */ jsx16(
|
|
2392
|
+
"textarea",
|
|
2393
|
+
{
|
|
2394
|
+
value: instruction,
|
|
2395
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2396
|
+
placeholder: "z.B. Vergleiche Lichtstimmung und Komposition. Welches ist dramatischer?",
|
|
2397
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2398
|
+
rows: 3
|
|
2399
|
+
}
|
|
2400
|
+
)
|
|
2401
|
+
] }),
|
|
2402
|
+
/* @__PURE__ */ jsx16(
|
|
2403
|
+
"button",
|
|
2404
|
+
{
|
|
2405
|
+
onClick: handleAnalyze,
|
|
2406
|
+
disabled: selectedImages.filter(Boolean).length < 1 || !instruction.trim() || isAnalyzing,
|
|
2407
|
+
className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
|
|
2408
|
+
children: isAnalyzing ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2409
|
+
/* @__PURE__ */ jsx16("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2410
|
+
/* @__PURE__ */ jsx16("span", { children: "Analysiere..." })
|
|
2411
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2412
|
+
/* @__PURE__ */ jsx16("span", { className: "material-symbols-outlined text-[16px]", children: "compare" }),
|
|
2413
|
+
/* @__PURE__ */ jsx16("span", { children: "Analysieren" })
|
|
2414
|
+
] })
|
|
2415
|
+
}
|
|
2416
|
+
),
|
|
2417
|
+
analysis && /* @__PURE__ */ jsxs14("div", { children: [
|
|
2418
|
+
/* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Analyse" }),
|
|
2419
|
+
/* @__PURE__ */ jsx16("div", { className: "bg-white/5 border border-white/10 rounded-xl px-3 py-3", children: /* @__PURE__ */ jsx16("p", { className: "text-[12px] text-white/70 leading-relaxed whitespace-pre-wrap", children: analysis }) }),
|
|
2420
|
+
/* @__PURE__ */ jsxs14("div", { className: "mt-3", children: [
|
|
2421
|
+
/* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Als Prompt nutzen" }),
|
|
2422
|
+
/* @__PURE__ */ jsx16(
|
|
2423
|
+
"textarea",
|
|
2424
|
+
{
|
|
2425
|
+
value: generatedPrompt,
|
|
2426
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2427
|
+
placeholder: "Analyse-Ergebnis hier als Prompt einf\xFCgen oder eigenen Prompt schreiben...",
|
|
2428
|
+
className: "w-full bg-white/5 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2429
|
+
rows: 3
|
|
2430
|
+
}
|
|
2431
|
+
),
|
|
2432
|
+
/* @__PURE__ */ jsx16(
|
|
2433
|
+
"button",
|
|
2434
|
+
{
|
|
2435
|
+
onClick: handleGenerateImage,
|
|
2436
|
+
disabled: !generatedPrompt.trim() || isGeneratingImage,
|
|
2437
|
+
className: "w-full mt-2 py-3 rounded-xl bg-blue-600/80 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-blue-600 flex items-center justify-center gap-2",
|
|
2438
|
+
children: isGeneratingImage ? /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2439
|
+
/* @__PURE__ */ jsx16("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2440
|
+
/* @__PURE__ */ jsx16("span", { children: "Generiere..." })
|
|
2441
|
+
] }) : /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
2442
|
+
/* @__PURE__ */ jsx16("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2443
|
+
/* @__PURE__ */ jsx16("span", { children: "Bild generieren" })
|
|
2444
|
+
] })
|
|
2445
|
+
}
|
|
2446
|
+
)
|
|
2447
|
+
] })
|
|
2448
|
+
] }),
|
|
2449
|
+
resultImage && /* @__PURE__ */ jsx16("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ jsx16("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2450
|
+
] });
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
// src/components/labs/LabLoop.tsx
|
|
2454
|
+
import { useState as useState11 } from "react";
|
|
2455
|
+
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2456
|
+
var LabLoop = ({ services, onResult }) => {
|
|
2457
|
+
const [rounds, setRounds] = useState11([]);
|
|
2458
|
+
const [currentInstruction, setCurrentInstruction] = useState11("");
|
|
2459
|
+
const [showPickerForRound, setShowPickerForRound] = useState11(null);
|
|
2460
|
+
const [pendingImages, setPendingImages] = useState11([]);
|
|
2461
|
+
const [isGenerating, setIsGenerating] = useState11(false);
|
|
2462
|
+
const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
|
|
2463
|
+
const handleAddImage = (item, frame) => {
|
|
2464
|
+
services.onItemUsed(item);
|
|
2465
|
+
const label = autoLabel(pendingImages.length);
|
|
2466
|
+
setPendingImages((prev) => [...prev, {
|
|
2467
|
+
item,
|
|
2468
|
+
frame,
|
|
2469
|
+
label,
|
|
2470
|
+
sendToPromptAI: true,
|
|
2471
|
+
sendToImageGen: false,
|
|
2472
|
+
roleForPrompt: "",
|
|
2473
|
+
roleForImage: ""
|
|
2474
|
+
}]);
|
|
2475
|
+
setShowPickerForRound(null);
|
|
2476
|
+
};
|
|
2477
|
+
const updatePendingImage = (i, updates) => setPendingImages((prev) => prev.map((img, idx) => idx === i ? { ...img, ...updates } : img));
|
|
2478
|
+
const handleRunRound = async () => {
|
|
2479
|
+
if (!currentInstruction.trim()) return;
|
|
2480
|
+
setIsGenerating(true);
|
|
2481
|
+
try {
|
|
2482
|
+
const historyForBuilder = rounds.map((r) => ({
|
|
2483
|
+
prompt: r.prompt,
|
|
2484
|
+
instruction: r.instruction,
|
|
2485
|
+
images: r.images
|
|
2486
|
+
}));
|
|
2487
|
+
const { text, systemInstruction } = buildLoopInstruction(historyForBuilder, currentInstruction);
|
|
2488
|
+
const allImages = [...rounds.flatMap((r) => r.images), ...pendingImages].filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2489
|
+
const newPrompt = await services.generateText(text, {
|
|
2490
|
+
systemInstruction,
|
|
2491
|
+
images: allImages.length ? allImages : void 0
|
|
2492
|
+
});
|
|
2493
|
+
const { base64, mediaId } = await services.generateImage({
|
|
2494
|
+
prompt: newPrompt,
|
|
2495
|
+
aspectRatio: "1:1",
|
|
2496
|
+
modelDisplayName: "\u{1F34C} Nano Banana Pro"
|
|
2497
|
+
});
|
|
2498
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2499
|
+
const newFrame = { id: crypto.randomUUID(), base64: newBase64, mediaId, source: "generated" };
|
|
2500
|
+
const newRound = {
|
|
2501
|
+
prompt: newPrompt,
|
|
2502
|
+
instruction: currentInstruction,
|
|
2503
|
+
frame: newFrame,
|
|
2504
|
+
images: [...pendingImages]
|
|
2505
|
+
};
|
|
2506
|
+
setRounds((prev) => [...prev, newRound]);
|
|
2507
|
+
setCurrentInstruction("");
|
|
2508
|
+
setPendingImages([]);
|
|
2509
|
+
const newItem = {
|
|
2510
|
+
id: newFrame.id,
|
|
2511
|
+
prompt: newPrompt,
|
|
2512
|
+
tags: [],
|
|
2513
|
+
frames: [newFrame]
|
|
2514
|
+
};
|
|
2515
|
+
services.saveResult?.(newItem);
|
|
2516
|
+
onResult?.(newItem);
|
|
2517
|
+
} finally {
|
|
2518
|
+
setIsGenerating(false);
|
|
2519
|
+
}
|
|
2520
|
+
};
|
|
2521
|
+
if (showPickerForRound !== null) {
|
|
2522
|
+
return /* @__PURE__ */ jsx17(
|
|
2523
|
+
LabImagePicker,
|
|
2524
|
+
{
|
|
2525
|
+
availableItems: services.availableItems,
|
|
2526
|
+
recentItems: services.recentItems,
|
|
2527
|
+
onSelect: handleAddImage,
|
|
2528
|
+
onClose: () => setShowPickerForRound(null),
|
|
2529
|
+
title: "Referenzbild hinzuf\xFCgen"
|
|
2530
|
+
}
|
|
2531
|
+
);
|
|
2532
|
+
}
|
|
2533
|
+
return /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2534
|
+
rounds.map((round, i) => /* @__PURE__ */ jsxs15("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2535
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-col items-center gap-1 shrink-0", children: [
|
|
2536
|
+
/* @__PURE__ */ jsxs15("span", { className: "text-[9px] font-bold text-white/30", children: [
|
|
2537
|
+
"R",
|
|
2538
|
+
i + 1
|
|
2539
|
+
] }),
|
|
2540
|
+
round.frame && /* @__PURE__ */ jsx17("img", { src: round.frame.base64, className: "w-12 h-12 object-cover rounded-lg" })
|
|
2541
|
+
] }),
|
|
2542
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex-1 min-w-0", children: [
|
|
2543
|
+
/* @__PURE__ */ jsxs15("p", { className: "text-[9px] text-white/30 italic mb-1", children: [
|
|
2544
|
+
'"',
|
|
2545
|
+
round.instruction,
|
|
2546
|
+
'"'
|
|
2547
|
+
] }),
|
|
2548
|
+
/* @__PURE__ */ jsx17("p", { className: "text-[10px] text-white/60 leading-tight line-clamp-3", children: round.prompt })
|
|
2549
|
+
] })
|
|
2550
|
+
] }, i)),
|
|
2551
|
+
/* @__PURE__ */ jsxs15("div", { className: "rounded-xl border border-white/20 bg-white/5 p-3 flex flex-col gap-3", children: [
|
|
2552
|
+
/* @__PURE__ */ jsx17("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: rounds.length === 0 ? "Runde 1 \u2014 Erster Prompt" : `Runde ${rounds.length + 1}` }),
|
|
2553
|
+
currentPrompt && /* @__PURE__ */ jsxs15("p", { className: "text-[10px] text-white/40 italic line-clamp-2", children: [
|
|
2554
|
+
'Letzter Prompt: "',
|
|
2555
|
+
currentPrompt,
|
|
2556
|
+
'"'
|
|
2557
|
+
] }),
|
|
2558
|
+
/* @__PURE__ */ jsx17(
|
|
2559
|
+
"textarea",
|
|
2560
|
+
{
|
|
2561
|
+
value: currentInstruction,
|
|
2562
|
+
onChange: (e) => setCurrentInstruction(e.target.value),
|
|
2563
|
+
placeholder: rounds.length === 0 ? "Ersten Prompt beschreiben..." : "Was soll verbessert werden?",
|
|
2564
|
+
className: "w-full bg-black/30 border border-white/10 rounded-xl px-3 py-2.5 text-[12px] text-white/80 outline-none resize-none dark-scrollbar",
|
|
2565
|
+
rows: 3
|
|
2566
|
+
}
|
|
2567
|
+
),
|
|
2568
|
+
pendingImages.map((img, i) => /* @__PURE__ */ jsxs15("div", { className: "flex gap-2 items-center", children: [
|
|
2569
|
+
/* @__PURE__ */ jsx17("img", { src: img.frame.base64, className: "w-8 h-8 object-cover rounded" }),
|
|
2570
|
+
/* @__PURE__ */ jsx17("span", { className: "text-[10px] text-white/40", children: img.label }),
|
|
2571
|
+
/* @__PURE__ */ jsx17(
|
|
2572
|
+
"input",
|
|
2573
|
+
{
|
|
2574
|
+
value: img.roleForPrompt || "",
|
|
2575
|
+
onChange: (e) => updatePendingImage(i, { roleForPrompt: e.target.value }),
|
|
2576
|
+
placeholder: "Rolle...",
|
|
2577
|
+
className: "flex-1 bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2578
|
+
}
|
|
2579
|
+
),
|
|
2580
|
+
/* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-[9px] text-white/30", children: [
|
|
2581
|
+
/* @__PURE__ */ jsx17("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updatePendingImage(i, { sendToPromptAI: e.target.checked }) }),
|
|
2582
|
+
"KI"
|
|
2583
|
+
] }),
|
|
2584
|
+
/* @__PURE__ */ jsx17("button", { onClick: () => setPendingImages((prev) => prev.filter((_, idx) => idx !== i)), className: "text-white/20 active:text-red-400", children: /* @__PURE__ */ jsx17("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
2585
|
+
] }, i)),
|
|
2586
|
+
/* @__PURE__ */ jsxs15(
|
|
2587
|
+
"button",
|
|
2588
|
+
{
|
|
2589
|
+
onClick: () => setShowPickerForRound(rounds.length),
|
|
2590
|
+
className: "text-[10px] text-white/30 active:text-white/60 flex items-center gap-1 self-start",
|
|
2591
|
+
children: [
|
|
2592
|
+
/* @__PURE__ */ jsx17("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
|
|
2593
|
+
"Referenzbild hinzuf\xFCgen"
|
|
2594
|
+
]
|
|
2595
|
+
}
|
|
2596
|
+
),
|
|
2597
|
+
/* @__PURE__ */ jsx17(
|
|
2598
|
+
"button",
|
|
2599
|
+
{
|
|
2600
|
+
onClick: handleRunRound,
|
|
2601
|
+
disabled: !currentInstruction.trim() || isGenerating,
|
|
2602
|
+
className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
|
|
2603
|
+
children: isGenerating ? /* @__PURE__ */ jsxs15(Fragment8, { children: [
|
|
2604
|
+
/* @__PURE__ */ jsx17("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2605
|
+
/* @__PURE__ */ jsx17("span", { children: "Runde l\xE4uft..." })
|
|
2606
|
+
] }) : /* @__PURE__ */ jsxs15(Fragment8, { children: [
|
|
2607
|
+
/* @__PURE__ */ jsx17("span", { className: "material-symbols-outlined text-[16px]", children: "loop" }),
|
|
2608
|
+
/* @__PURE__ */ jsx17("span", { children: "Runde starten" })
|
|
2609
|
+
] })
|
|
2610
|
+
}
|
|
2611
|
+
)
|
|
2612
|
+
] })
|
|
2613
|
+
] });
|
|
2614
|
+
};
|
|
2615
|
+
|
|
2616
|
+
// src/components/labs/LabsTab.tsx
|
|
2617
|
+
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2618
|
+
var TABS = [
|
|
2619
|
+
{ key: "remix", label: "Remix", icon: "auto_fix_high" },
|
|
2620
|
+
{ key: "blend", label: "Blend", icon: "merge" },
|
|
2621
|
+
{ key: "compare", label: "Compare", icon: "compare" },
|
|
2622
|
+
{ key: "loop", label: "Loop", icon: "loop" }
|
|
2623
|
+
];
|
|
2624
|
+
var LabsTab = ({ services, onResult }) => {
|
|
2625
|
+
const [activeTab, setActiveTab] = useState12("remix");
|
|
2626
|
+
return /* @__PURE__ */ jsxs16("div", { className: "flex flex-col h-full overflow-hidden", children: [
|
|
2627
|
+
/* @__PURE__ */ jsx18("div", { className: "flex border-b border-white/5 shrink-0", children: TABS.map((tab) => /* @__PURE__ */ jsxs16(
|
|
2628
|
+
"button",
|
|
2629
|
+
{
|
|
2630
|
+
onClick: () => setActiveTab(tab.key),
|
|
2631
|
+
className: `flex-1 flex items-center justify-center gap-1 h-10 text-[9px] font-bold uppercase tracking-wide transition-colors ${activeTab === tab.key ? "text-white border-b-2 border-white" : "text-white/30 hover:text-white/60"}`,
|
|
2632
|
+
children: [
|
|
2633
|
+
/* @__PURE__ */ jsx18("span", { className: "material-symbols-outlined text-[14px]", children: tab.icon }),
|
|
2634
|
+
tab.label
|
|
2635
|
+
]
|
|
2636
|
+
},
|
|
2637
|
+
tab.key
|
|
2638
|
+
)) }),
|
|
2639
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex-1 overflow-hidden", children: [
|
|
2640
|
+
activeTab === "remix" && /* @__PURE__ */ jsx18(LabRemix, { services, onResult }),
|
|
2641
|
+
activeTab === "blend" && /* @__PURE__ */ jsx18(LabBlend, { services, onResult }),
|
|
2642
|
+
activeTab === "compare" && /* @__PURE__ */ jsx18(LabCompare, { services, onResult }),
|
|
2643
|
+
activeTab === "loop" && /* @__PURE__ */ jsx18(LabLoop, { services, onResult })
|
|
2644
|
+
] })
|
|
2645
|
+
] });
|
|
2646
|
+
};
|
|
2647
|
+
|
|
2648
|
+
// src/components/TagManagerPanel.tsx
|
|
2649
|
+
import { useState as useState13 } from "react";
|
|
2650
|
+
import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2651
|
+
function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
|
|
2652
|
+
const categories = Object.keys(workspaceTags.by_category).filter(
|
|
2653
|
+
(cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
|
|
2654
|
+
);
|
|
2655
|
+
const [selectedCategory, setSelectedCategory] = useState13(categories[0] || "");
|
|
2656
|
+
const effectiveCategory = categories.includes(selectedCategory) ? selectedCategory : categories[0] || "";
|
|
2657
|
+
const [editingLabel, setEditingLabel] = useState13(null);
|
|
2658
|
+
const [editState, setEditState] = useState13({ label: "", value: "" });
|
|
2659
|
+
const [newTag, setNewTag] = useState13({ label: "", value: "" });
|
|
2660
|
+
const [movingLabel, setMovingLabel] = useState13(null);
|
|
2661
|
+
const [moveTarget, setMoveTarget] = useState13("");
|
|
2662
|
+
const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
|
|
2663
|
+
const otherCategories = categories.filter((c) => c !== effectiveCategory);
|
|
2664
|
+
const startEdit = (tag) => {
|
|
2665
|
+
setEditingLabel(tag.label);
|
|
2666
|
+
setEditState({ label: tag.label, value: tag.value });
|
|
2667
|
+
setMovingLabel(null);
|
|
2668
|
+
};
|
|
2669
|
+
const saveEdit = (originalLabel) => {
|
|
2670
|
+
if (!editState.label.trim()) return;
|
|
2671
|
+
onTagUpdate(originalLabel, effectiveCategory, {
|
|
2672
|
+
label: editState.label.trim(),
|
|
2673
|
+
value: editState.value.trim() || editState.label.trim()
|
|
2674
|
+
});
|
|
2675
|
+
setEditingLabel(null);
|
|
2676
|
+
};
|
|
2677
|
+
const handleMoveUp = (index) => {
|
|
2678
|
+
if (index === 0) return;
|
|
2679
|
+
const reordered = [...tags];
|
|
2680
|
+
[reordered[index - 1], reordered[index]] = [reordered[index], reordered[index - 1]];
|
|
2681
|
+
onTagReorder(effectiveCategory, reordered);
|
|
2682
|
+
};
|
|
2683
|
+
const handleMoveDown = (index) => {
|
|
2684
|
+
if (index === tags.length - 1) return;
|
|
2685
|
+
const reordered = [...tags];
|
|
2686
|
+
[reordered[index], reordered[index + 1]] = [reordered[index + 1], reordered[index]];
|
|
2687
|
+
onTagReorder(effectiveCategory, reordered);
|
|
2688
|
+
};
|
|
2689
|
+
const handleMove = (tag) => {
|
|
2690
|
+
if (!moveTarget) return;
|
|
2691
|
+
onTagMove(tag.label, tag.value, effectiveCategory, moveTarget);
|
|
2692
|
+
setMovingLabel(null);
|
|
2693
|
+
setMoveTarget("");
|
|
2694
|
+
};
|
|
2695
|
+
const handleCreate = () => {
|
|
2696
|
+
if (!newTag.label.trim() || !effectiveCategory) return;
|
|
2697
|
+
onTagCreate({ label: newTag.label.trim(), value: newTag.value.trim() || newTag.label.trim(), category: effectiveCategory, is_user_created: true });
|
|
2698
|
+
setNewTag({ label: "", value: "" });
|
|
2699
|
+
};
|
|
2700
|
+
const handleDelete = (tag) => {
|
|
2701
|
+
if (!confirm(`Tag "${tag.label}" l\xF6schen?`)) return;
|
|
2702
|
+
onTagDelete(tag.label, effectiveCategory);
|
|
2703
|
+
};
|
|
2704
|
+
return /* @__PURE__ */ jsxs17("div", { className: "flex flex-col h-full overflow-hidden", children: [
|
|
2705
|
+
/* @__PURE__ */ jsx19("div", { className: "px-3 py-2 border-b border-white/5 shrink-0", children: /* @__PURE__ */ jsx19("span", { className: "text-[10px] font-bold uppercase tracking-widest text-white/40", children: "Tag Manager" }) }),
|
|
2706
|
+
/* @__PURE__ */ jsx19("div", { className: "px-3 py-2 shrink-0 overflow-x-auto", children: /* @__PURE__ */ jsxs17("div", { className: "flex gap-1.5 flex-nowrap", children: [
|
|
2707
|
+
categories.map((cat) => /* @__PURE__ */ jsxs17(
|
|
2708
|
+
"button",
|
|
2709
|
+
{
|
|
2710
|
+
onClick: () => {
|
|
2711
|
+
setSelectedCategory(cat);
|
|
2712
|
+
setEditingLabel(null);
|
|
2713
|
+
setMovingLabel(null);
|
|
2714
|
+
},
|
|
2715
|
+
className: `px-2.5 py-1 rounded text-[9px] font-bold border whitespace-nowrap transition ${cat === effectiveCategory ? "bg-blue-900/40 border-blue-600/60 text-blue-300" : "bg-white/5 border-white/10 text-white/40 hover:text-white/70"}`,
|
|
2716
|
+
children: [
|
|
2717
|
+
cat,
|
|
2718
|
+
" ",
|
|
2719
|
+
/* @__PURE__ */ jsx19("span", { className: "ml-1 opacity-50", children: (workspaceTags.by_category[cat] || []).filter((t) => !t.is_deleted).length })
|
|
2720
|
+
]
|
|
2721
|
+
},
|
|
2722
|
+
cat
|
|
2723
|
+
)),
|
|
2724
|
+
categories.length === 0 && /* @__PURE__ */ jsx19("span", { className: "text-[10px] text-white/20", children: "Erst Workspace importieren" })
|
|
2725
|
+
] }) }),
|
|
2726
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 pb-2 space-y-1", children: [
|
|
2727
|
+
tags.map((tag, i) => /* @__PURE__ */ jsxs17("div", { children: [
|
|
2728
|
+
editingLabel === tag.label ? /* @__PURE__ */ jsxs17("div", { className: "bg-white/5 border border-blue-600/40 rounded-lg p-2.5 space-y-1.5", children: [
|
|
2729
|
+
/* @__PURE__ */ jsx19(
|
|
2730
|
+
"input",
|
|
2731
|
+
{
|
|
2732
|
+
value: editState.label,
|
|
2733
|
+
onChange: (e) => setEditState((s) => ({ ...s, label: e.target.value })),
|
|
2734
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1 text-[12px] text-white outline-none focus:border-blue-500/50",
|
|
2735
|
+
placeholder: "Label",
|
|
2736
|
+
autoFocus: true,
|
|
2737
|
+
onKeyDown: (e) => e.key === "Enter" && saveEdit(tag.label)
|
|
2738
|
+
}
|
|
2739
|
+
),
|
|
2740
|
+
/* @__PURE__ */ jsx19(
|
|
2741
|
+
"textarea",
|
|
2742
|
+
{
|
|
2743
|
+
value: editState.value,
|
|
2744
|
+
onChange: (e) => setEditState((s) => ({ ...s, value: e.target.value })),
|
|
2745
|
+
rows: 2,
|
|
2746
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1 text-[11px] text-white/70 outline-none focus:border-blue-500/50 resize-none",
|
|
2747
|
+
placeholder: "Prompt-Wert (leer = Label)"
|
|
2748
|
+
}
|
|
2749
|
+
),
|
|
2750
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex gap-1.5 justify-end", children: [
|
|
2751
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => saveEdit(tag.label), className: "px-2.5 py-1 bg-blue-700 hover:bg-blue-600 text-white text-[10px] font-bold rounded transition", children: "SPEICHERN" }),
|
|
2752
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => setEditingLabel(null), className: "px-2.5 py-1 bg-white/5 hover:bg-white/10 text-white/50 text-[10px] font-bold rounded transition", children: "ABBRUCH" })
|
|
2753
|
+
] })
|
|
2754
|
+
] }) : /* @__PURE__ */ jsxs17("div", { className: "group flex items-center gap-1.5 bg-white/3 hover:bg-white/6 border border-white/5 rounded-lg px-2 py-1.5 transition", children: [
|
|
2755
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex flex-col gap-0 shrink-0", children: [
|
|
2756
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => handleMoveUp(i), disabled: i === 0, className: "text-white/20 hover:text-white/60 disabled:opacity-10 transition leading-none", children: /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[14px]", children: "arrow_drop_up" }) }),
|
|
2757
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => handleMoveDown(i), disabled: i === tags.length - 1, className: "text-white/20 hover:text-white/60 disabled:opacity-10 transition leading-none", children: /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[14px]", children: "arrow_drop_down" }) })
|
|
2758
|
+
] }),
|
|
2759
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex-1 min-w-0", children: [
|
|
2760
|
+
/* @__PURE__ */ jsx19("div", { className: "text-[12px] text-white/80 font-medium truncate", children: tag.label }),
|
|
2761
|
+
tag.value && tag.value !== tag.label && /* @__PURE__ */ jsxs17("div", { className: "text-[10px] text-white/30 truncate", children: [
|
|
2762
|
+
tag.value.slice(0, 60),
|
|
2763
|
+
tag.value.length > 60 ? "\u2026" : ""
|
|
2764
|
+
] })
|
|
2765
|
+
] }),
|
|
2766
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex gap-1 opacity-0 group-hover:opacity-100 transition shrink-0", children: [
|
|
2767
|
+
otherCategories.length > 0 && /* @__PURE__ */ jsx19(
|
|
2768
|
+
"button",
|
|
2769
|
+
{
|
|
2770
|
+
onClick: () => {
|
|
2771
|
+
setMovingLabel((l) => l === tag.label ? null : tag.label);
|
|
2772
|
+
setMoveTarget("");
|
|
2773
|
+
setEditingLabel(null);
|
|
2774
|
+
},
|
|
2775
|
+
className: "p-1 rounded text-white/30 hover:text-purple-400 transition",
|
|
2776
|
+
title: "Kategorie wechseln",
|
|
2777
|
+
children: /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[16px]", children: "drive_file_move" })
|
|
2778
|
+
}
|
|
2779
|
+
),
|
|
2780
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => startEdit(tag), className: "p-1 rounded text-white/30 hover:text-blue-400 transition", title: "Bearbeiten", children: /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[16px]", children: "edit" }) }),
|
|
2781
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => handleDelete(tag), className: "p-1 rounded text-white/30 hover:text-red-400 transition", title: "L\xF6schen", children: /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[16px]", children: "delete" }) })
|
|
2782
|
+
] })
|
|
2783
|
+
] }),
|
|
2784
|
+
movingLabel === tag.label && /* @__PURE__ */ jsxs17("div", { className: "mt-1 bg-purple-900/20 border border-purple-700/30 rounded-lg p-2.5 space-y-1.5", children: [
|
|
2785
|
+
/* @__PURE__ */ jsx19("div", { className: "text-[9px] font-bold uppercase tracking-widest text-purple-400/70", children: "Verschieben nach Kategorie" }),
|
|
2786
|
+
/* @__PURE__ */ jsxs17(
|
|
2787
|
+
"select",
|
|
2788
|
+
{
|
|
2789
|
+
value: moveTarget,
|
|
2790
|
+
onChange: (e) => setMoveTarget(e.target.value),
|
|
2791
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1 text-[11px] text-white/70 outline-none",
|
|
2792
|
+
children: [
|
|
2793
|
+
/* @__PURE__ */ jsx19("option", { value: "", children: "\u2014 Kategorie w\xE4hlen \u2014" }),
|
|
2794
|
+
otherCategories.map((cat) => /* @__PURE__ */ jsx19("option", { value: cat, children: cat }, cat))
|
|
2795
|
+
]
|
|
2796
|
+
}
|
|
2797
|
+
),
|
|
2798
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex gap-1.5 justify-end", children: [
|
|
2799
|
+
/* @__PURE__ */ jsx19(
|
|
2800
|
+
"button",
|
|
2801
|
+
{
|
|
2802
|
+
onClick: () => handleMove(tag),
|
|
2803
|
+
disabled: !moveTarget,
|
|
2804
|
+
className: "px-2.5 py-1 bg-purple-700 hover:bg-purple-600 text-white text-[10px] font-bold rounded transition disabled:opacity-40",
|
|
2805
|
+
children: "VERSCHIEBEN"
|
|
2806
|
+
}
|
|
2807
|
+
),
|
|
2808
|
+
/* @__PURE__ */ jsx19("button", { onClick: () => setMovingLabel(null), className: "px-2.5 py-1 bg-white/5 hover:bg-white/10 text-white/50 text-[10px] font-bold rounded transition", children: "ABBRUCH" })
|
|
2809
|
+
] })
|
|
2810
|
+
] })
|
|
2811
|
+
] }, `${effectiveCategory}-${i}`)),
|
|
2812
|
+
tags.length === 0 && effectiveCategory && /* @__PURE__ */ jsx19("div", { className: "text-center text-[11px] text-white/20 py-6", children: "Keine Tags in dieser Kategorie." })
|
|
2813
|
+
] }),
|
|
2814
|
+
effectiveCategory && /* @__PURE__ */ jsxs17("div", { className: "px-3 py-2 border-t border-white/5 shrink-0 space-y-1.5", children: [
|
|
2815
|
+
/* @__PURE__ */ jsxs17("div", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30", children: [
|
|
2816
|
+
"Neuer Tag in \u201E",
|
|
2817
|
+
effectiveCategory,
|
|
2818
|
+
'"'
|
|
2819
|
+
] }),
|
|
2820
|
+
/* @__PURE__ */ jsx19(
|
|
2821
|
+
"input",
|
|
2822
|
+
{
|
|
2823
|
+
value: newTag.label,
|
|
2824
|
+
onChange: (e) => setNewTag((s) => ({ ...s, label: e.target.value })),
|
|
2825
|
+
onKeyDown: (e) => e.key === "Enter" && !e.shiftKey && handleCreate(),
|
|
2826
|
+
placeholder: "Label\u2026",
|
|
2827
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1.5 text-[12px] text-white outline-none focus:border-white/20"
|
|
2828
|
+
}
|
|
2829
|
+
),
|
|
2830
|
+
/* @__PURE__ */ jsx19(
|
|
2831
|
+
"textarea",
|
|
2832
|
+
{
|
|
2833
|
+
value: newTag.value,
|
|
2834
|
+
onChange: (e) => setNewTag((s) => ({ ...s, value: e.target.value })),
|
|
2835
|
+
rows: 2,
|
|
2836
|
+
placeholder: "Prompt-Wert (leer = Label)",
|
|
2837
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1 text-[11px] text-white/60 outline-none focus:border-white/20 resize-none"
|
|
2838
|
+
}
|
|
2839
|
+
),
|
|
2840
|
+
/* @__PURE__ */ jsxs17(
|
|
2841
|
+
"button",
|
|
2842
|
+
{
|
|
2843
|
+
onClick: handleCreate,
|
|
2844
|
+
disabled: !newTag.label.trim(),
|
|
2845
|
+
className: "w-full py-1.5 bg-white/5 border border-white/10 text-white/60 text-[10px] font-bold rounded hover:bg-white/10 hover:text-white transition disabled:opacity-30 flex items-center justify-center gap-1.5",
|
|
2846
|
+
children: [
|
|
2847
|
+
/* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2848
|
+
"TAG ERSTELLEN"
|
|
2849
|
+
]
|
|
2850
|
+
}
|
|
2851
|
+
)
|
|
2852
|
+
] })
|
|
2853
|
+
] });
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// src/components/AvatarArchitectApp.tsx
|
|
2857
|
+
import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1609
2858
|
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
|
|
1610
2859
|
useEffect4(() => {
|
|
1611
2860
|
const id = "flow-styles";
|
|
@@ -1616,15 +2865,15 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1616
2865
|
document.head.appendChild(style);
|
|
1617
2866
|
}
|
|
1618
2867
|
}, []);
|
|
1619
|
-
const [showStart, setShowStart] =
|
|
1620
|
-
const [layoutChoice, setLayoutChoice] =
|
|
2868
|
+
const [showStart, setShowStart] = useState14(true);
|
|
2869
|
+
const [layoutChoice, setLayoutChoice] = useState14(() => {
|
|
1621
2870
|
try {
|
|
1622
2871
|
return localStorage.getItem("aa-layout") || null;
|
|
1623
2872
|
} catch {
|
|
1624
2873
|
return null;
|
|
1625
2874
|
}
|
|
1626
2875
|
});
|
|
1627
|
-
const [projectLoaded, setProjectLoaded] =
|
|
2876
|
+
const [projectLoaded, setProjectLoaded] = useState14(false);
|
|
1628
2877
|
const wsInputRef = useRef6(null);
|
|
1629
2878
|
const startApp = (choice) => {
|
|
1630
2879
|
try {
|
|
@@ -1634,61 +2883,63 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1634
2883
|
setLayoutChoice(choice);
|
|
1635
2884
|
setShowStart(false);
|
|
1636
2885
|
};
|
|
1637
|
-
const [nodes, setNodes] =
|
|
1638
|
-
const [edges, setEdges] =
|
|
1639
|
-
const [history, setHistory] =
|
|
1640
|
-
const [galleryItems, setGalleryItems] =
|
|
1641
|
-
const [activePrompt, setActivePrompt] =
|
|
1642
|
-
const [isSynthesizing, setIsSynthesizing] =
|
|
1643
|
-
const [activeGenerationsCount, setActiveGenerationsCount] =
|
|
1644
|
-
const [currentResult, setCurrentResult] =
|
|
1645
|
-
const [focusedNodeId, setFocusedNodeId] =
|
|
1646
|
-
const [leftTab, setLeftTab] =
|
|
1647
|
-
const [promptFeedback, setPromptFeedback] =
|
|
1648
|
-
const [lastPromptPayload, setLastPromptPayload] =
|
|
1649
|
-
const [isPromptTabGenerating, setIsPromptTabGenerating] =
|
|
1650
|
-
const [activeTab, setActiveTab] =
|
|
1651
|
-
const [mobileTab, setMobileTab] =
|
|
1652
|
-
const [
|
|
1653
|
-
const [
|
|
1654
|
-
const [
|
|
1655
|
-
const [
|
|
1656
|
-
const [
|
|
1657
|
-
const [
|
|
1658
|
-
const [
|
|
2886
|
+
const [nodes, setNodes] = useState14([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
|
|
2887
|
+
const [edges, setEdges] = useState14([]);
|
|
2888
|
+
const [history, setHistory] = useState14([]);
|
|
2889
|
+
const [galleryItems, setGalleryItems] = useState14([]);
|
|
2890
|
+
const [activePrompt, setActivePrompt] = useState14("");
|
|
2891
|
+
const [isSynthesizing, setIsSynthesizing] = useState14(false);
|
|
2892
|
+
const [activeGenerationsCount, setActiveGenerationsCount] = useState14(0);
|
|
2893
|
+
const [currentResult, setCurrentResult] = useState14(null);
|
|
2894
|
+
const [focusedNodeId, setFocusedNodeId] = useState14(null);
|
|
2895
|
+
const [leftTab, setLeftTab] = useState14("prompt");
|
|
2896
|
+
const [promptFeedback, setPromptFeedback] = useState14(null);
|
|
2897
|
+
const [lastPromptPayload, setLastPromptPayload] = useState14(null);
|
|
2898
|
+
const [isPromptTabGenerating, setIsPromptTabGenerating] = useState14(false);
|
|
2899
|
+
const [activeTab, setActiveTab] = useState14("history");
|
|
2900
|
+
const [mobileTab, setMobileTab] = useState14("stage");
|
|
2901
|
+
const [middlePanel, setMiddlePanel] = useState14("stage");
|
|
2902
|
+
const [recentLabItems, setRecentLabItems] = useState14([]);
|
|
2903
|
+
const [aspectRatio, setAspectRatio] = useState14("1:1");
|
|
2904
|
+
const [selectedModel, setSelectedModel] = useState14("\u{1F34C} Nano Banana Pro");
|
|
2905
|
+
const [seed, setSeed] = useState14(Math.floor(Math.random() * 1e6));
|
|
2906
|
+
const [seedMode, setSeedMode] = useState14("random");
|
|
2907
|
+
const [isLeftCollapsed, setIsLeftCollapsed] = useState14(false);
|
|
2908
|
+
const [isRightCollapsed, setIsRightCollapsed] = useState14(false);
|
|
2909
|
+
const [leftPanelWidth, setLeftPanelWidth] = useState14(() => {
|
|
1659
2910
|
try {
|
|
1660
2911
|
return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
|
|
1661
2912
|
} catch {
|
|
1662
2913
|
return 260;
|
|
1663
2914
|
}
|
|
1664
2915
|
});
|
|
1665
|
-
const [rightPanelWidth, setRightPanelWidth] =
|
|
2916
|
+
const [rightPanelWidth, setRightPanelWidth] = useState14(() => {
|
|
1666
2917
|
try {
|
|
1667
2918
|
return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
|
|
1668
2919
|
} catch {
|
|
1669
2920
|
return 320;
|
|
1670
2921
|
}
|
|
1671
2922
|
});
|
|
1672
|
-
const [isPromptCollapsed, setIsPromptCollapsed] =
|
|
1673
|
-
const [projectActionState, setProjectActionState] =
|
|
2923
|
+
const [isPromptCollapsed, setIsPromptCollapsed] = useState14(false);
|
|
2924
|
+
const [projectActionState, setProjectActionState] = useState14("idle");
|
|
1674
2925
|
const syncServerDataRef = useRef6(null);
|
|
1675
|
-
const [workspaceTags, setWorkspaceTags] =
|
|
1676
|
-
const [serverProjects, setServerProjects] =
|
|
1677
|
-
const [isLoadingFromServer, setIsLoadingFromServer] =
|
|
1678
|
-
const [highContrast, setHighContrast] =
|
|
2926
|
+
const [workspaceTags, setWorkspaceTags] = useState14(null);
|
|
2927
|
+
const [serverProjects, setServerProjects] = useState14([]);
|
|
2928
|
+
const [isLoadingFromServer, setIsLoadingFromServer] = useState14(false);
|
|
2929
|
+
const [highContrast, setHighContrast] = useState14(() => {
|
|
1679
2930
|
try {
|
|
1680
2931
|
return localStorage.getItem("aa-contrast") === "high";
|
|
1681
2932
|
} catch {
|
|
1682
2933
|
return false;
|
|
1683
2934
|
}
|
|
1684
2935
|
});
|
|
1685
|
-
const [activeReferenceId, setActiveReferenceId] =
|
|
1686
|
-
const [activeReferenceThumbnail, setActiveReferenceThumbnail] =
|
|
1687
|
-
const [isScanningImage, setIsScanningImage] =
|
|
1688
|
-
const [touchStartX, setTouchStartX] =
|
|
1689
|
-
const [isFullscreen, setIsFullscreen] =
|
|
1690
|
-
const [zoomScale, setZoomScale] =
|
|
1691
|
-
const [zoomOffset, setZoomOffset] =
|
|
2936
|
+
const [activeReferenceId, setActiveReferenceId] = useState14(null);
|
|
2937
|
+
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = useState14(null);
|
|
2938
|
+
const [isScanningImage, setIsScanningImage] = useState14(false);
|
|
2939
|
+
const [touchStartX, setTouchStartX] = useState14(null);
|
|
2940
|
+
const [isFullscreen, setIsFullscreen] = useState14(false);
|
|
2941
|
+
const [zoomScale, setZoomScale] = useState14(1);
|
|
2942
|
+
const [zoomOffset, setZoomOffset] = useState14({ x: 0, y: 0 });
|
|
1692
2943
|
const lastPinchDist = useRef6(null);
|
|
1693
2944
|
const lastTapTime = useRef6(0);
|
|
1694
2945
|
const dragStart = useRef6(null);
|
|
@@ -1754,6 +3005,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1754
3005
|
setActiveReferenceId(null);
|
|
1755
3006
|
setActiveReferenceThumbnail(null);
|
|
1756
3007
|
};
|
|
3008
|
+
const labServices = useMemo2(() => {
|
|
3009
|
+
const available = groupGenerationsToLabItems([...galleryItems, ...history]);
|
|
3010
|
+
return {
|
|
3011
|
+
availableItems: available,
|
|
3012
|
+
recentItems: recentLabItems,
|
|
3013
|
+
onItemUsed: (item) => setRecentLabItems(
|
|
3014
|
+
(prev) => [item, ...prev.filter((i) => i.id !== item.id)].slice(0, 10)
|
|
3015
|
+
),
|
|
3016
|
+
generateText: onGeneratePrompt,
|
|
3017
|
+
generateImage: onGenerateImage,
|
|
3018
|
+
saveResult: async (item) => {
|
|
3019
|
+
const frame = item.frames[0];
|
|
3020
|
+
if (frame?.base64) {
|
|
3021
|
+
const gen = frameToGeneration(frame, item);
|
|
3022
|
+
setGalleryItems((prev) => [gen, ...prev]);
|
|
3023
|
+
setHistory((prev) => [gen, ...prev]);
|
|
3024
|
+
}
|
|
3025
|
+
},
|
|
3026
|
+
workspaceTags: workspaceTags || void 0
|
|
3027
|
+
};
|
|
3028
|
+
}, [galleryItems, history, recentLabItems, onGeneratePrompt, onGenerateImage, workspaceTags]);
|
|
1757
3029
|
const startLeftResize = (e) => {
|
|
1758
3030
|
e.preventDefault();
|
|
1759
3031
|
const startX = e.clientX;
|
|
@@ -1801,7 +3073,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1801
3073
|
reader.onerror = reject;
|
|
1802
3074
|
reader.readAsDataURL(file);
|
|
1803
3075
|
});
|
|
1804
|
-
const
|
|
3076
|
+
const { text, systemInstruction } = buildScanInstruction();
|
|
3077
|
+
const prompt = await onGeneratePrompt(text, {
|
|
3078
|
+
images: [{ base64, mimeType: file.type || "image/png" }],
|
|
3079
|
+
systemInstruction
|
|
3080
|
+
});
|
|
1805
3081
|
setActivePrompt(prompt);
|
|
1806
3082
|
} catch (err) {
|
|
1807
3083
|
console.error("Scan fehlgeschlagen:", err);
|
|
@@ -1917,11 +3193,32 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1917
3193
|
return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
|
|
1918
3194
|
});
|
|
1919
3195
|
};
|
|
3196
|
+
const handleTagReorder = (category, reorderedTags) => {
|
|
3197
|
+
setWorkspaceTags((prev) => {
|
|
3198
|
+
if (!prev) return prev;
|
|
3199
|
+
const deletedTags = (prev.by_category[category] || []).filter((t) => t.is_deleted);
|
|
3200
|
+
const merged = [...reorderedTags, ...deletedTags];
|
|
3201
|
+
const newAll = [
|
|
3202
|
+
...prev.all.filter((t) => t.category !== category),
|
|
3203
|
+
...merged.map((t) => ({ ...t, category }))
|
|
3204
|
+
];
|
|
3205
|
+
return { by_category: { ...prev.by_category, [category]: merged }, all: newAll };
|
|
3206
|
+
});
|
|
3207
|
+
};
|
|
3208
|
+
const handleTagMove = (label, value, fromCategory, toCategory) => {
|
|
3209
|
+
setWorkspaceTags((prev) => {
|
|
3210
|
+
if (!prev) return prev;
|
|
3211
|
+
const fromTags = (prev.by_category[fromCategory] || []).filter((t) => t.label !== label);
|
|
3212
|
+
const toTags = [...prev.by_category[toCategory] || [], { label, value, is_user_created: true }];
|
|
3213
|
+
const newAll = prev.all.filter((t) => !(t.label === label && t.category === fromCategory)).concat({ label, value, is_user_created: true, category: toCategory });
|
|
3214
|
+
return { by_category: { ...prev.by_category, [fromCategory]: fromTags, [toCategory]: toTags }, all: newAll };
|
|
3215
|
+
});
|
|
3216
|
+
};
|
|
1920
3217
|
const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
|
|
1921
3218
|
setIsPromptTabGenerating(true);
|
|
1922
3219
|
setPromptFeedback(null);
|
|
1923
3220
|
try {
|
|
1924
|
-
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) :
|
|
3221
|
+
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : nodes.map((n) => n.data.label || "").filter(Boolean).join("\n");
|
|
1925
3222
|
const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
|
|
1926
3223
|
setLastPromptPayload(payload);
|
|
1927
3224
|
const raw = await onGeneratePrompt(payload);
|
|
@@ -1949,7 +3246,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1949
3246
|
const handleProjectExport = async () => {
|
|
1950
3247
|
setProjectActionState("working-full");
|
|
1951
3248
|
try {
|
|
1952
|
-
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
3249
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
|
|
1953
3250
|
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
1954
3251
|
setProjectActionState("done");
|
|
1955
3252
|
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
@@ -1977,6 +3274,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1977
3274
|
setSeedMode(data.settings.seedMode || "random");
|
|
1978
3275
|
}
|
|
1979
3276
|
if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
|
|
3277
|
+
if (data.recentLabItemIds?.length) {
|
|
3278
|
+
const allGens = [...data.history || [], ...data.galleryItems || []];
|
|
3279
|
+
const labItems = groupGenerationsToLabItems(allGens);
|
|
3280
|
+
const recent = data.recentLabItemIds.map((id) => labItems.find((item) => item.id === id)).filter(Boolean);
|
|
3281
|
+
setRecentLabItems(recent);
|
|
3282
|
+
}
|
|
1980
3283
|
setProjectActionState("done");
|
|
1981
3284
|
setProjectLoaded(true);
|
|
1982
3285
|
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
@@ -2017,7 +3320,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2017
3320
|
if (!onServerSave) return;
|
|
2018
3321
|
setProjectActionState("working-full");
|
|
2019
3322
|
try {
|
|
2020
|
-
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
3323
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
|
|
2021
3324
|
await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
|
|
2022
3325
|
await fetchServerProjects();
|
|
2023
3326
|
setProjectActionState("done");
|
|
@@ -2061,7 +3364,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2061
3364
|
mergedHistory,
|
|
2062
3365
|
serverData.galleryItems || galleryItems,
|
|
2063
3366
|
serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
|
|
2064
|
-
serverData.workspaceTags || workspaceTags
|
|
3367
|
+
serverData.workspaceTags || workspaceTags,
|
|
3368
|
+
serverData.recentLabItemIds || recentLabItems.map((i) => i.id)
|
|
2065
3369
|
);
|
|
2066
3370
|
await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
|
|
2067
3371
|
await fetchServerProjects();
|
|
@@ -2077,7 +3381,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2077
3381
|
}, [activeTab]);
|
|
2078
3382
|
if (isFullscreen && currentResult?.base64) {
|
|
2079
3383
|
const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
|
|
2080
|
-
return /* @__PURE__ */
|
|
3384
|
+
return /* @__PURE__ */ jsxs18(
|
|
2081
3385
|
"div",
|
|
2082
3386
|
{
|
|
2083
3387
|
className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
|
|
@@ -2085,7 +3389,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2085
3389
|
onTouchMove: handleFsTouchMove,
|
|
2086
3390
|
onTouchEnd: handleFsTouchEnd,
|
|
2087
3391
|
children: [
|
|
2088
|
-
/* @__PURE__ */
|
|
3392
|
+
/* @__PURE__ */ jsx20(
|
|
2089
3393
|
"img",
|
|
2090
3394
|
{
|
|
2091
3395
|
src: fsBase64,
|
|
@@ -2102,76 +3406,76 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2102
3406
|
}
|
|
2103
3407
|
}
|
|
2104
3408
|
),
|
|
2105
|
-
/* @__PURE__ */
|
|
2106
|
-
zoomScale > 1 && /* @__PURE__ */
|
|
3409
|
+
/* @__PURE__ */ jsx20("button", { onClick: closeFullscreen, className: "absolute top-4 right-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
|
|
3410
|
+
zoomScale > 1 && /* @__PURE__ */ jsx20("button", { onClick: () => {
|
|
2107
3411
|
setZoomScale(1);
|
|
2108
3412
|
setZoomOffset({ x: 0, y: 0 });
|
|
2109
|
-
}, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */
|
|
2110
|
-
history.length > 1 && /* @__PURE__ */
|
|
2111
|
-
/* @__PURE__ */
|
|
3413
|
+
}, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
|
|
3414
|
+
history.length > 1 && /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3415
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => {
|
|
2112
3416
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
2113
|
-
}, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
3417
|
+
}, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
3418
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => {
|
|
2115
3419
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
2116
|
-
}, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */
|
|
2117
|
-
/* @__PURE__ */
|
|
3420
|
+
}, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
3421
|
+
/* @__PURE__ */ jsxs18("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
|
|
2118
3422
|
currentIndex + 1,
|
|
2119
3423
|
" / ",
|
|
2120
3424
|
history.length
|
|
2121
3425
|
] })
|
|
2122
3426
|
] }),
|
|
2123
|
-
zoomScale === 1 && /* @__PURE__ */
|
|
3427
|
+
zoomScale === 1 && /* @__PURE__ */ jsx20("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
|
|
2124
3428
|
]
|
|
2125
3429
|
}
|
|
2126
3430
|
);
|
|
2127
3431
|
}
|
|
2128
3432
|
if (showStart) {
|
|
2129
|
-
return /* @__PURE__ */
|
|
2130
|
-
/* @__PURE__ */
|
|
3433
|
+
return /* @__PURE__ */ jsxs18("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
|
|
3434
|
+
/* @__PURE__ */ jsx20("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
|
|
2131
3435
|
const f = e.target.files?.[0];
|
|
2132
3436
|
if (f) handleProjectImport(f);
|
|
2133
3437
|
e.target.value = "";
|
|
2134
3438
|
} }),
|
|
2135
|
-
/* @__PURE__ */
|
|
2136
|
-
/* @__PURE__ */
|
|
2137
|
-
/* @__PURE__ */
|
|
2138
|
-
/* @__PURE__ */
|
|
3439
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-1", children: [
|
|
3440
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
|
|
3441
|
+
/* @__PURE__ */ jsx20("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
|
|
3442
|
+
/* @__PURE__ */ jsxs18("span", { className: "text-white text-[13px] font-mono", children: [
|
|
2139
3443
|
"v",
|
|
2140
3444
|
LIB_VERSION
|
|
2141
3445
|
] })
|
|
2142
3446
|
] }),
|
|
2143
|
-
/* @__PURE__ */
|
|
3447
|
+
/* @__PURE__ */ jsxs18(
|
|
2144
3448
|
"button",
|
|
2145
3449
|
{
|
|
2146
3450
|
onClick: toggleContrast,
|
|
2147
3451
|
className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
|
|
2148
3452
|
style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
|
|
2149
3453
|
children: [
|
|
2150
|
-
/* @__PURE__ */
|
|
2151
|
-
/* @__PURE__ */
|
|
2152
|
-
/* @__PURE__ */
|
|
2153
|
-
/* @__PURE__ */
|
|
3454
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
|
|
3455
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-start", children: [
|
|
3456
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
|
|
3457
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
|
|
2154
3458
|
] })
|
|
2155
3459
|
]
|
|
2156
3460
|
}
|
|
2157
3461
|
),
|
|
2158
|
-
/* @__PURE__ */
|
|
2159
|
-
/* @__PURE__ */
|
|
3462
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3463
|
+
/* @__PURE__ */ jsxs18(
|
|
2160
3464
|
"button",
|
|
2161
3465
|
{
|
|
2162
3466
|
onClick: () => wsInputRef.current?.click(),
|
|
2163
3467
|
className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform",
|
|
2164
3468
|
style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
|
|
2165
3469
|
children: [
|
|
2166
|
-
/* @__PURE__ */
|
|
3470
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
|
|
2167
3471
|
projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
|
|
2168
3472
|
]
|
|
2169
3473
|
}
|
|
2170
3474
|
),
|
|
2171
|
-
!projectLoaded && /* @__PURE__ */
|
|
3475
|
+
!projectLoaded && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
|
|
2172
3476
|
] }),
|
|
2173
|
-
onFetchServerProjects && /* @__PURE__ */
|
|
2174
|
-
/* @__PURE__ */
|
|
3477
|
+
onFetchServerProjects && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3478
|
+
/* @__PURE__ */ jsxs18(
|
|
2175
3479
|
"button",
|
|
2176
3480
|
{
|
|
2177
3481
|
disabled: isLoadingFromServer,
|
|
@@ -2192,35 +3496,35 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2192
3496
|
className: "w-full flex items-center justify-center gap-3 rounded-2xl font-bold text-[14px] uppercase tracking-wide text-white active:scale-95 transition-transform disabled:opacity-50",
|
|
2193
3497
|
style: { height: 56, background: "#7c3aed" },
|
|
2194
3498
|
children: [
|
|
2195
|
-
/* @__PURE__ */
|
|
3499
|
+
/* @__PURE__ */ jsx20("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
|
|
2196
3500
|
isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
|
|
2197
3501
|
]
|
|
2198
3502
|
}
|
|
2199
3503
|
),
|
|
2200
|
-
/* @__PURE__ */
|
|
3504
|
+
/* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
|
|
2201
3505
|
] }),
|
|
2202
|
-
/* @__PURE__ */
|
|
2203
|
-
/* @__PURE__ */
|
|
2204
|
-
/* @__PURE__ */
|
|
3506
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3507
|
+
/* @__PURE__ */ jsx20("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
|
|
3508
|
+
/* @__PURE__ */ jsx20("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
|
|
2205
3509
|
{ id: "mobile", icon: "smartphone", label: "Mobile" },
|
|
2206
3510
|
{ id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
|
|
2207
3511
|
{ id: "desktop", icon: "desktop_windows", label: "Desktop" },
|
|
2208
3512
|
{ id: "tablet-landscape", icon: "tablet", label: "Landscape" }
|
|
2209
|
-
].map((opt) => /* @__PURE__ */
|
|
3513
|
+
].map((opt) => /* @__PURE__ */ jsxs18(
|
|
2210
3514
|
"button",
|
|
2211
3515
|
{
|
|
2212
3516
|
onClick: () => startApp(opt.id),
|
|
2213
3517
|
className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
|
|
2214
3518
|
style: { borderColor: layoutChoice === opt.id ? "rgba(255,255,255,0.35)" : "rgba(255,255,255,0.08)", background: layoutChoice === opt.id ? "rgba(255,255,255,0.07)" : "transparent" },
|
|
2215
3519
|
children: [
|
|
2216
|
-
/* @__PURE__ */
|
|
2217
|
-
/* @__PURE__ */
|
|
3520
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
|
|
3521
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
|
|
2218
3522
|
]
|
|
2219
3523
|
},
|
|
2220
3524
|
opt.id
|
|
2221
3525
|
)) }),
|
|
2222
|
-
layoutChoice === "mobile-desktop" && /* @__PURE__ */
|
|
2223
|
-
layoutChoice === "tablet-landscape" && /* @__PURE__ */
|
|
3526
|
+
layoutChoice === "mobile-desktop" && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
|
|
3527
|
+
layoutChoice === "tablet-landscape" && /* @__PURE__ */ jsx20("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
|
|
2224
3528
|
] })
|
|
2225
3529
|
] });
|
|
2226
3530
|
}
|
|
@@ -2229,28 +3533,46 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2229
3533
|
const mdScale = mdMode ? window.innerWidth / 430 : 1;
|
|
2230
3534
|
const mdW = mdMode ? 430 : void 0;
|
|
2231
3535
|
const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
|
|
2232
|
-
const mobileRoot = /* @__PURE__ */
|
|
3536
|
+
const mobileRoot = /* @__PURE__ */ jsxs18("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
|
|
2233
3537
|
width: mdMode ? mdW : "100vw",
|
|
2234
3538
|
height: mdMode ? mdH : "100dvh",
|
|
2235
3539
|
transform: mdMode ? `scale(${mdScale})` : void 0,
|
|
2236
3540
|
transformOrigin: mdMode ? "top left" : void 0,
|
|
2237
3541
|
...hcStyle || {}
|
|
2238
3542
|
}, children: [
|
|
2239
|
-
mobileTab === "
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
3543
|
+
mobileTab === "labs" && /* @__PURE__ */ jsx20("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx20(LabsTab, { services: labServices, onResult: (item) => {
|
|
3544
|
+
const frame = item.frames[0];
|
|
3545
|
+
if (frame?.base64) {
|
|
3546
|
+
setCurrentResult(frameToGeneration(frame, item));
|
|
3547
|
+
setMobileTab("stage");
|
|
3548
|
+
}
|
|
3549
|
+
} }) }),
|
|
3550
|
+
mobileTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ jsx20(
|
|
3551
|
+
TagManagerPanel,
|
|
3552
|
+
{
|
|
3553
|
+
workspaceTags,
|
|
3554
|
+
onTagCreate: handleTagCreate,
|
|
3555
|
+
onTagUpdate: handleTagUpdate,
|
|
3556
|
+
onTagDelete: handleTagDelete,
|
|
3557
|
+
onTagReorder: handleTagReorder,
|
|
3558
|
+
onTagMove: handleTagMove
|
|
3559
|
+
}
|
|
3560
|
+
) }),
|
|
3561
|
+
mobileTab === "stage" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
3562
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
|
|
3563
|
+
/* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
3564
|
+
/* @__PURE__ */ jsx20(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" }] }),
|
|
3565
|
+
/* @__PURE__ */ jsx20("div", { className: "flex-1" }),
|
|
3566
|
+
activeReferenceThumbnail ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
|
|
3567
|
+
/* @__PURE__ */ jsx20("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
|
|
3568
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
|
|
3569
|
+
/* @__PURE__ */ jsx20("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
3570
|
+
] }) : /* @__PURE__ */ jsx20("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
|
|
3571
|
+
/* @__PURE__ */ jsx20("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
3572
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
|
|
2251
3573
|
] }),
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
3574
|
+
/* @__PURE__ */ jsx20("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ jsxs18("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
3575
|
+
/* @__PURE__ */ jsx20(
|
|
2254
3576
|
"textarea",
|
|
2255
3577
|
{
|
|
2256
3578
|
value: activePrompt,
|
|
@@ -2260,26 +3582,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2260
3582
|
placeholder: "Prompt eingeben..."
|
|
2261
3583
|
}
|
|
2262
3584
|
),
|
|
2263
|
-
activePrompt && !isSynthesizing && /* @__PURE__ */
|
|
3585
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ jsx20("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-white/20 active:text-white transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
|
|
2264
3586
|
] }) }),
|
|
2265
|
-
/* @__PURE__ */
|
|
3587
|
+
/* @__PURE__ */ jsx20("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ jsx20(
|
|
2266
3588
|
"button",
|
|
2267
3589
|
{
|
|
2268
3590
|
onClick: () => handleGenerateImage(),
|
|
2269
3591
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2270
3592
|
className: "w-full flex items-center justify-center gap-2 rounded-xl font-bold text-[14px] uppercase tracking-wide transition-all disabled:opacity-30 active:scale-95",
|
|
2271
3593
|
style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
|
|
2272
|
-
children: isGenerating ? /* @__PURE__ */
|
|
2273
|
-
/* @__PURE__ */
|
|
2274
|
-
/* @__PURE__ */
|
|
2275
|
-
] }) : /* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2277
|
-
/* @__PURE__ */
|
|
3594
|
+
children: isGenerating ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3595
|
+
/* @__PURE__ */ jsx20("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
|
|
3596
|
+
/* @__PURE__ */ jsx20("span", { children: "Generiere..." })
|
|
3597
|
+
] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3598
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
|
|
3599
|
+
/* @__PURE__ */ jsx20("span", { children: "Generieren" })
|
|
2278
3600
|
] })
|
|
2279
3601
|
}
|
|
2280
3602
|
) }),
|
|
2281
|
-
/* @__PURE__ */
|
|
2282
|
-
/* @__PURE__ */
|
|
3603
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
|
|
3604
|
+
/* @__PURE__ */ jsxs18(
|
|
2283
3605
|
"div",
|
|
2284
3606
|
{
|
|
2285
3607
|
className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
|
|
@@ -2293,25 +3615,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2293
3615
|
setTouchStartX(null);
|
|
2294
3616
|
},
|
|
2295
3617
|
children: [
|
|
2296
|
-
currentResult?.status === "processing" && /* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
3618
|
+
currentResult?.status === "processing" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-3", children: [
|
|
3619
|
+
/* @__PURE__ */ jsx20("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
3620
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
2299
3621
|
] }),
|
|
2300
|
-
currentResult?.status === "error" && /* @__PURE__ */
|
|
2301
|
-
/* @__PURE__ */
|
|
2302
|
-
/* @__PURE__ */
|
|
2303
|
-
/* @__PURE__ */
|
|
3622
|
+
currentResult?.status === "error" && /* @__PURE__ */ jsxs18("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
|
|
3623
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
|
|
3624
|
+
/* @__PURE__ */ jsx20("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
|
|
3625
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => handleGenerateImage(currentResult.prompt), className: "px-4 py-2 rounded-lg border border-white/20 text-[13px] text-white/70 active:bg-white/10", children: "Erneut versuchen" })
|
|
2304
3626
|
] }),
|
|
2305
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */
|
|
2306
|
-
!currentResult && /* @__PURE__ */
|
|
2307
|
-
/* @__PURE__ */
|
|
2308
|
-
/* @__PURE__ */
|
|
3627
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx20("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
|
|
3628
|
+
!currentResult && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
3629
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
|
|
3630
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
2309
3631
|
] }),
|
|
2310
|
-
currentResult?.status === "done" && /* @__PURE__ */
|
|
2311
|
-
history.length > 1 && currentResult && /* @__PURE__ */
|
|
2312
|
-
/* @__PURE__ */
|
|
2313
|
-
/* @__PURE__ */
|
|
2314
|
-
/* @__PURE__ */
|
|
3632
|
+
currentResult?.status === "done" && /* @__PURE__ */ jsx20("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
|
|
3633
|
+
history.length > 1 && currentResult && /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3634
|
+
/* @__PURE__ */ jsx20("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
3635
|
+
/* @__PURE__ */ jsx20("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
3636
|
+
/* @__PURE__ */ jsxs18("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
|
|
2315
3637
|
currentIndex + 1,
|
|
2316
3638
|
" / ",
|
|
2317
3639
|
history.length
|
|
@@ -2320,30 +3642,30 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2320
3642
|
]
|
|
2321
3643
|
}
|
|
2322
3644
|
),
|
|
2323
|
-
currentResult?.status === "done" && /* @__PURE__ */
|
|
2324
|
-
/* @__PURE__ */
|
|
2325
|
-
/* @__PURE__ */
|
|
2326
|
-
/* @__PURE__ */
|
|
3645
|
+
currentResult?.status === "done" && /* @__PURE__ */ jsxs18("div", { className: "flex gap-2 mt-3", children: [
|
|
3646
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
|
|
3647
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
|
|
3648
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/60", children: "Prompt" })
|
|
2327
3649
|
] }),
|
|
2328
|
-
/* @__PURE__ */
|
|
2329
|
-
/* @__PURE__ */
|
|
2330
|
-
/* @__PURE__ */
|
|
3650
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl bg-white/10 active:bg-white/15 transition-colors", style: { height: 44 }, children: [
|
|
3651
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
|
|
3652
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
|
|
2331
3653
|
] }),
|
|
2332
|
-
/* @__PURE__ */
|
|
2333
|
-
/* @__PURE__ */
|
|
2334
|
-
/* @__PURE__ */
|
|
3654
|
+
/* @__PURE__ */ jsxs18("button", { onClick: handleDownloadSingle, className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
|
|
3655
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
|
|
3656
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[12px] text-white/60", children: "Laden" })
|
|
2335
3657
|
] })
|
|
2336
3658
|
] })
|
|
2337
3659
|
] })
|
|
2338
3660
|
] }),
|
|
2339
|
-
mobileTab === "browse" && /* @__PURE__ */
|
|
2340
|
-
/* @__PURE__ */
|
|
2341
|
-
/* @__PURE__ */
|
|
2342
|
-
activeTab === "history" && /* @__PURE__ */
|
|
3661
|
+
mobileTab === "browse" && /* @__PURE__ */ jsxs18("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
3662
|
+
/* @__PURE__ */ jsx20("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab(tab), className: `flex-1 flex items-center justify-center gap-1.5 transition-colors text-[11px] font-bold uppercase tracking-wide ${activeTab === tab ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)) }),
|
|
3663
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
3664
|
+
activeTab === "history" && /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
|
|
2343
3665
|
setCurrentResult(g);
|
|
2344
3666
|
setMobileTab("stage");
|
|
2345
3667
|
}, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
2346
|
-
activeTab === "gallery" && /* @__PURE__ */
|
|
3668
|
+
activeTab === "gallery" && /* @__PURE__ */ jsx20(
|
|
2347
3669
|
MediaLibrary,
|
|
2348
3670
|
{
|
|
2349
3671
|
items: galleryItems,
|
|
@@ -2363,38 +3685,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2363
3685
|
}
|
|
2364
3686
|
}
|
|
2365
3687
|
),
|
|
2366
|
-
activeTab === "inspect" && /* @__PURE__ */
|
|
3688
|
+
activeTab === "inspect" && /* @__PURE__ */ jsx20(InspectPanel, { currentResult, history, onSelect: (g) => {
|
|
2367
3689
|
setCurrentResult(g);
|
|
2368
3690
|
} })
|
|
2369
3691
|
] })
|
|
2370
3692
|
] }),
|
|
2371
|
-
/* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2373
|
-
workspaceTags && /* @__PURE__ */
|
|
3693
|
+
/* @__PURE__ */ jsxs18("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
|
|
3694
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
|
|
3695
|
+
workspaceTags && /* @__PURE__ */ jsxs18("button", { onClick: () => {
|
|
2374
3696
|
setLeftTab("prompt");
|
|
2375
3697
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2376
3698
|
}, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
|
|
2377
|
-
/* @__PURE__ */
|
|
3699
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
|
|
2378
3700
|
"Prompt"
|
|
2379
3701
|
] }),
|
|
2380
|
-
/* @__PURE__ */
|
|
3702
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => {
|
|
2381
3703
|
setLeftTab("hierarchy");
|
|
2382
3704
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2383
3705
|
}, className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
|
|
2384
|
-
/* @__PURE__ */
|
|
3706
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
|
|
2385
3707
|
"Hierarchie"
|
|
2386
3708
|
] }),
|
|
2387
|
-
/* @__PURE__ */
|
|
2388
|
-
/* @__PURE__ */
|
|
3709
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => setActiveTab("setup"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "setup" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
|
|
3710
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
|
|
2389
3711
|
"Setup"
|
|
2390
3712
|
] }),
|
|
2391
|
-
/* @__PURE__ */
|
|
2392
|
-
/* @__PURE__ */
|
|
3713
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => setActiveTab("sync"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
|
|
3714
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
|
|
2393
3715
|
"Sync"
|
|
2394
|
-
] })
|
|
3716
|
+
] }),
|
|
3717
|
+
workspaceTags && /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
|
|
2395
3718
|
] }),
|
|
2396
|
-
/* @__PURE__ */
|
|
2397
|
-
leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */
|
|
3719
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
3720
|
+
leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ jsx20("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx20(
|
|
2398
3721
|
ListView,
|
|
2399
3722
|
{
|
|
2400
3723
|
nodes,
|
|
@@ -2425,12 +3748,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2425
3748
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2426
3749
|
}
|
|
2427
3750
|
) }),
|
|
2428
|
-
workspaceTags && /* @__PURE__ */
|
|
3751
|
+
workspaceTags && /* @__PURE__ */ jsx20("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ jsx20(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
|
|
2429
3752
|
handleGenerateImage(prompt);
|
|
2430
3753
|
setMobileTab("stage");
|
|
2431
3754
|
}, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
|
|
2432
|
-
activeTab === "setup" && /* @__PURE__ */
|
|
2433
|
-
activeTab === "sync" && /* @__PURE__ */
|
|
3755
|
+
activeTab === "setup" && /* @__PURE__ */ jsx20(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
3756
|
+
activeTab === "sync" && /* @__PURE__ */ jsx20(
|
|
2434
3757
|
ProjectSyncTab,
|
|
2435
3758
|
{
|
|
2436
3759
|
onProjectExport: handleProjectExport,
|
|
@@ -2445,20 +3768,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2445
3768
|
onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
|
|
2446
3769
|
onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
|
|
2447
3770
|
}
|
|
3771
|
+
),
|
|
3772
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
|
|
3773
|
+
TagManagerPanel,
|
|
3774
|
+
{
|
|
3775
|
+
workspaceTags,
|
|
3776
|
+
onTagCreate: handleTagCreate,
|
|
3777
|
+
onTagUpdate: handleTagUpdate,
|
|
3778
|
+
onTagDelete: handleTagDelete,
|
|
3779
|
+
onTagReorder: handleTagReorder,
|
|
3780
|
+
onTagMove: handleTagMove
|
|
3781
|
+
}
|
|
2448
3782
|
)
|
|
2449
3783
|
] })
|
|
2450
3784
|
] }),
|
|
2451
|
-
/* @__PURE__ */
|
|
3785
|
+
/* @__PURE__ */ jsx20("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
|
|
2452
3786
|
{ id: "tools", icon: "auto_fix_high", label: "Prompt" },
|
|
2453
3787
|
{ id: "stage", icon: "palette", label: "Stage" },
|
|
3788
|
+
{ id: "labs", icon: "science", label: "Labs" },
|
|
3789
|
+
...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
|
|
2454
3790
|
{ id: "browse", icon: "photo_library", label: "Galerie" }
|
|
2455
|
-
].map((tab) => /* @__PURE__ */
|
|
2456
|
-
/* @__PURE__ */
|
|
2457
|
-
/* @__PURE__ */
|
|
3791
|
+
].map((tab) => /* @__PURE__ */ jsxs18("button", { onClick: () => setMobileTab(tab.id), className: `flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors ${mobileTab === tab.id ? "text-white" : "text-white/30"}`, children: [
|
|
3792
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
|
|
3793
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
|
|
2458
3794
|
] }, tab.id)) })
|
|
2459
3795
|
] });
|
|
2460
3796
|
if (mdMode) {
|
|
2461
|
-
return /* @__PURE__ */
|
|
3797
|
+
return /* @__PURE__ */ jsx20("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
|
|
2462
3798
|
}
|
|
2463
3799
|
return mobileRoot;
|
|
2464
3800
|
}
|
|
@@ -2466,17 +3802,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2466
3802
|
const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
|
|
2467
3803
|
const tlW = 920;
|
|
2468
3804
|
const tlH = 520;
|
|
2469
|
-
return /* @__PURE__ */
|
|
2470
|
-
/* @__PURE__ */
|
|
2471
|
-
/* @__PURE__ */
|
|
2472
|
-
/* @__PURE__ */
|
|
2473
|
-
/* @__PURE__ */
|
|
2474
|
-
/* @__PURE__ */
|
|
2475
|
-
/* @__PURE__ */
|
|
2476
|
-
/* @__PURE__ */
|
|
3805
|
+
return /* @__PURE__ */ jsx20("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ jsxs18("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
|
|
3806
|
+
/* @__PURE__ */ jsxs18("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
|
|
3807
|
+
/* @__PURE__ */ jsxs18("div", { style: { height: 52, borderBottom: "1px solid rgba(255,255,255,0.05)", display: "flex", alignItems: "center", gap: 8, padding: "0 12px", flexShrink: 0 }, children: [
|
|
3808
|
+
/* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
3809
|
+
/* @__PURE__ */ jsx20(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" }] }),
|
|
3810
|
+
/* @__PURE__ */ jsx20("div", { style: { flex: 1 } }),
|
|
3811
|
+
/* @__PURE__ */ jsx20("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
3812
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
|
|
2477
3813
|
] }),
|
|
2478
|
-
/* @__PURE__ */
|
|
2479
|
-
/* @__PURE__ */
|
|
3814
|
+
/* @__PURE__ */ jsx20("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs18("div", { style: { position: "relative", borderRadius: 12, border: `1px solid ${isSynthesizing ? "rgba(255,255,255,0.2)" : "rgba(255,255,255,0.1)"}`, background: "rgba(255,255,255,0.05)" }, children: [
|
|
3815
|
+
/* @__PURE__ */ jsx20(
|
|
2480
3816
|
"textarea",
|
|
2481
3817
|
{
|
|
2482
3818
|
value: activePrompt,
|
|
@@ -2485,27 +3821,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2485
3821
|
placeholder: "Prompt eingeben..."
|
|
2486
3822
|
}
|
|
2487
3823
|
),
|
|
2488
|
-
activePrompt && /* @__PURE__ */
|
|
3824
|
+
activePrompt && /* @__PURE__ */ jsx20("button", { onClick: () => setActivePrompt(""), style: { position: "absolute", top: 6, right: 6, width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 0 }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
|
|
2489
3825
|
] }) }),
|
|
2490
|
-
/* @__PURE__ */
|
|
3826
|
+
/* @__PURE__ */ jsx20("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ jsx20(
|
|
2491
3827
|
"button",
|
|
2492
3828
|
{
|
|
2493
3829
|
onClick: () => handleGenerateImage(),
|
|
2494
3830
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2495
3831
|
style: { width: "100%", height: 42, display: "flex", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 10, fontWeight: "bold", fontSize: 13, textTransform: "uppercase", letterSpacing: "0.05em", border: "1px solid rgba(255,255,255,0.1)", background: activePrompt.trim() && !isGenerating ? "#0284c7" : "transparent", color: "#fff", cursor: activePrompt.trim() && !isGenerating ? "pointer" : "default", opacity: !activePrompt.trim() || isGenerating ? 0.3 : 1, fontFamily: "inherit", transition: "background 0.2s" },
|
|
2496
|
-
children: isGenerating ? /* @__PURE__ */
|
|
2497
|
-
/* @__PURE__ */
|
|
2498
|
-
/* @__PURE__ */
|
|
2499
|
-
] }) : /* @__PURE__ */
|
|
2500
|
-
/* @__PURE__ */
|
|
2501
|
-
/* @__PURE__ */
|
|
3832
|
+
children: isGenerating ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3833
|
+
/* @__PURE__ */ jsx20("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
3834
|
+
/* @__PURE__ */ jsx20("span", { children: "Generiere..." })
|
|
3835
|
+
] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3836
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
|
|
3837
|
+
/* @__PURE__ */ jsx20("span", { children: "Generieren" })
|
|
2502
3838
|
] })
|
|
2503
3839
|
}
|
|
2504
3840
|
) }),
|
|
2505
|
-
/* @__PURE__ */
|
|
3841
|
+
/* @__PURE__ */ jsx20("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
|
|
2506
3842
|
] }),
|
|
2507
|
-
/* @__PURE__ */
|
|
2508
|
-
/* @__PURE__ */
|
|
3843
|
+
/* @__PURE__ */ jsxs18("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
|
|
3844
|
+
/* @__PURE__ */ jsx20(
|
|
2509
3845
|
"div",
|
|
2510
3846
|
{
|
|
2511
3847
|
style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
|
|
@@ -2517,26 +3853,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2517
3853
|
else if (dx > 50) goToPrev();
|
|
2518
3854
|
setTouchStartX(null);
|
|
2519
3855
|
},
|
|
2520
|
-
children: /* @__PURE__ */
|
|
2521
|
-
currentResult?.status === "processing" && /* @__PURE__ */
|
|
2522
|
-
/* @__PURE__ */
|
|
2523
|
-
/* @__PURE__ */
|
|
3856
|
+
children: /* @__PURE__ */ jsxs18("div", { style: { height: "100%", width: "100%", borderRadius: 20, border: "1px solid rgba(255,255,255,0.05)", background: "rgba(0,0,0,0.4)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
|
|
3857
|
+
currentResult?.status === "processing" && /* @__PURE__ */ jsxs18("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
3858
|
+
/* @__PURE__ */ jsx20("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
3859
|
+
/* @__PURE__ */ jsx20("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
|
|
2524
3860
|
] }),
|
|
2525
|
-
currentResult?.status === "error" && /* @__PURE__ */
|
|
2526
|
-
/* @__PURE__ */
|
|
2527
|
-
/* @__PURE__ */
|
|
2528
|
-
/* @__PURE__ */
|
|
3861
|
+
currentResult?.status === "error" && /* @__PURE__ */ jsxs18("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
3862
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
|
|
3863
|
+
/* @__PURE__ */ jsx20("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
|
|
3864
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => handleGenerateImage(currentResult.prompt), style: { padding: "8px 16px", borderRadius: 8, border: "1px solid rgba(255,255,255,0.2)", fontSize: 11, color: "rgba(255,255,255,0.7)", background: "none", cursor: "pointer", fontFamily: "inherit" }, children: "Erneut versuchen" })
|
|
2529
3865
|
] }),
|
|
2530
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */
|
|
2531
|
-
!currentResult && /* @__PURE__ */
|
|
2532
|
-
/* @__PURE__ */
|
|
2533
|
-
/* @__PURE__ */
|
|
3866
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ jsx20("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
|
|
3867
|
+
!currentResult && /* @__PURE__ */ jsxs18("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
|
|
3868
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
|
|
3869
|
+
/* @__PURE__ */ jsx20("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
|
|
2534
3870
|
] }),
|
|
2535
|
-
currentResult?.status === "done" && /* @__PURE__ */
|
|
2536
|
-
history.length > 1 && currentResult && /* @__PURE__ */
|
|
2537
|
-
/* @__PURE__ */
|
|
2538
|
-
/* @__PURE__ */
|
|
2539
|
-
/* @__PURE__ */
|
|
3871
|
+
currentResult?.status === "done" && /* @__PURE__ */ jsx20("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
|
|
3872
|
+
history.length > 1 && currentResult && /* @__PURE__ */ jsxs18(Fragment9, { children: [
|
|
3873
|
+
/* @__PURE__ */ jsx20("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
|
|
3874
|
+
/* @__PURE__ */ jsx20("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
|
|
3875
|
+
/* @__PURE__ */ jsxs18("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
|
|
2540
3876
|
currentIndex + 1,
|
|
2541
3877
|
" / ",
|
|
2542
3878
|
history.length
|
|
@@ -2545,41 +3881,57 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2545
3881
|
] })
|
|
2546
3882
|
}
|
|
2547
3883
|
),
|
|
2548
|
-
currentResult?.status === "done" && /* @__PURE__ */
|
|
2549
|
-
/* @__PURE__ */
|
|
2550
|
-
/* @__PURE__ */
|
|
2551
|
-
/* @__PURE__ */
|
|
3884
|
+
currentResult?.status === "done" && /* @__PURE__ */ jsxs18("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
|
|
3885
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
|
|
3886
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
|
|
3887
|
+
/* @__PURE__ */ jsx20("span", { children: "Prompt" })
|
|
2552
3888
|
] }),
|
|
2553
|
-
/* @__PURE__ */
|
|
2554
|
-
/* @__PURE__ */
|
|
2555
|
-
/* @__PURE__ */
|
|
3889
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "none", background: "rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.8)", fontSize: 11, fontWeight: "bold", cursor: "pointer", fontFamily: "inherit" }, children: [
|
|
3890
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
|
|
3891
|
+
/* @__PURE__ */ jsx20("span", { children: "Referenz" })
|
|
2556
3892
|
] }),
|
|
2557
|
-
/* @__PURE__ */
|
|
2558
|
-
/* @__PURE__ */
|
|
2559
|
-
/* @__PURE__ */
|
|
3893
|
+
/* @__PURE__ */ jsxs18("button", { onClick: handleDownloadSingle, style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
|
|
3894
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
|
|
3895
|
+
/* @__PURE__ */ jsx20("span", { children: "Laden" })
|
|
2560
3896
|
] })
|
|
2561
3897
|
] })
|
|
2562
3898
|
] })
|
|
2563
3899
|
] }) });
|
|
2564
3900
|
}
|
|
2565
|
-
return /* @__PURE__ */
|
|
2566
|
-
/* @__PURE__ */
|
|
2567
|
-
/* @__PURE__ */
|
|
2568
|
-
/* @__PURE__ */
|
|
2569
|
-
!isLeftCollapsed && /* @__PURE__ */
|
|
2570
|
-
workspaceTags && /* @__PURE__ */
|
|
2571
|
-
/* @__PURE__ */
|
|
3901
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
|
|
3902
|
+
/* @__PURE__ */ jsx20("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ jsx20("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
|
|
3903
|
+
/* @__PURE__ */ jsxs18("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: [
|
|
3904
|
+
/* @__PURE__ */ jsxs18("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
|
|
3905
|
+
!isLeftCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex flex-1 gap-1", children: [
|
|
3906
|
+
workspaceTags && /* @__PURE__ */ jsxs18("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: [
|
|
3907
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
|
|
2572
3908
|
"Prompt"
|
|
2573
3909
|
] }),
|
|
2574
|
-
/* @__PURE__ */
|
|
2575
|
-
/* @__PURE__ */
|
|
3910
|
+
/* @__PURE__ */ jsxs18("button", { onClick: () => setLeftTab("hierarchy"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
|
|
3911
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
|
|
2576
3912
|
"Hierarchie"
|
|
2577
|
-
] })
|
|
3913
|
+
] }),
|
|
3914
|
+
workspaceTags && /* @__PURE__ */ jsx20("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
|
|
2578
3915
|
] }),
|
|
2579
|
-
/* @__PURE__ */
|
|
3916
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
|
|
2580
3917
|
] }),
|
|
2581
|
-
!isLeftCollapsed && /* @__PURE__ */
|
|
2582
|
-
|
|
3918
|
+
!isLeftCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
3919
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
|
|
3920
|
+
TagManagerPanel,
|
|
3921
|
+
{
|
|
3922
|
+
workspaceTags,
|
|
3923
|
+
onTagCreate: handleTagCreate,
|
|
3924
|
+
onTagUpdate: handleTagUpdate,
|
|
3925
|
+
onTagDelete: handleTagDelete,
|
|
3926
|
+
onTagReorder: handleTagReorder,
|
|
3927
|
+
onTagMove: handleTagMove
|
|
3928
|
+
}
|
|
3929
|
+
),
|
|
3930
|
+
activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs18("div", { children: [
|
|
3931
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
|
|
3932
|
+
/* @__PURE__ */ jsx20("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
|
|
3933
|
+
] }) }),
|
|
3934
|
+
leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ jsx20("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx20(
|
|
2583
3935
|
ListView,
|
|
2584
3936
|
{
|
|
2585
3937
|
nodes,
|
|
@@ -2604,75 +3956,111 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2604
3956
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2605
3957
|
}
|
|
2606
3958
|
) }),
|
|
2607
|
-
leftTab === "prompt" && workspaceTags && /* @__PURE__ */
|
|
3959
|
+
leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ jsx20(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 })
|
|
2608
3960
|
] })
|
|
2609
3961
|
] }),
|
|
2610
|
-
!isLeftCollapsed && /* @__PURE__ */
|
|
2611
|
-
/* @__PURE__ */
|
|
2612
|
-
/* @__PURE__ */
|
|
2613
|
-
/* @__PURE__ */
|
|
2614
|
-
/* @__PURE__ */
|
|
2615
|
-
/* @__PURE__ */
|
|
3962
|
+
!isLeftCollapsed && /* @__PURE__ */ jsx20("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
|
|
3963
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
|
|
3964
|
+
/* @__PURE__ */ jsxs18("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
|
|
3965
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1.5", children: [
|
|
3966
|
+
/* @__PURE__ */ jsx20(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
3967
|
+
/* @__PURE__ */ jsx20(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" }] })
|
|
2616
3968
|
] }),
|
|
2617
|
-
/* @__PURE__ */
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
3969
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 mx-auto", children: [
|
|
3970
|
+
/* @__PURE__ */ jsx20(
|
|
3971
|
+
"button",
|
|
3972
|
+
{
|
|
3973
|
+
onClick: () => setMiddlePanel("stage"),
|
|
3974
|
+
className: `px-3 h-7 rounded-lg text-[9px] font-bold uppercase tracking-wide transition-colors ${middlePanel === "stage" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/50"}`,
|
|
3975
|
+
children: "Stage"
|
|
3976
|
+
}
|
|
3977
|
+
),
|
|
3978
|
+
/* @__PURE__ */ jsx20(
|
|
3979
|
+
"button",
|
|
3980
|
+
{
|
|
3981
|
+
onClick: () => setMiddlePanel("labs"),
|
|
3982
|
+
className: `px-3 h-7 rounded-lg text-[9px] font-bold uppercase tracking-wide transition-colors ${middlePanel === "labs" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/50"}`,
|
|
3983
|
+
children: "Labs"
|
|
3984
|
+
}
|
|
3985
|
+
)
|
|
3986
|
+
] }),
|
|
3987
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", children: [
|
|
3988
|
+
activeReferenceThumbnail ? /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
|
|
3989
|
+
/* @__PURE__ */ jsx20("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
|
|
3990
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
|
|
3991
|
+
/* @__PURE__ */ jsx20("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
3992
|
+
] }) : /* @__PURE__ */ jsxs18("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: [
|
|
3993
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
|
|
3994
|
+
/* @__PURE__ */ jsx20("span", { children: "Ref" })
|
|
2625
3995
|
] }),
|
|
2626
|
-
/* @__PURE__ */
|
|
2627
|
-
/* @__PURE__ */
|
|
3996
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
|
|
3997
|
+
/* @__PURE__ */ jsx20(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
|
|
2628
3998
|
] })
|
|
2629
3999
|
] }),
|
|
2630
|
-
/* @__PURE__ */
|
|
2631
|
-
!isPromptCollapsed && /* @__PURE__ */
|
|
2632
|
-
/* @__PURE__ */
|
|
2633
|
-
activePrompt && !isSynthesizing && /* @__PURE__ */
|
|
4000
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
|
|
4001
|
+
!isPromptCollapsed && /* @__PURE__ */ jsx20("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ jsxs18("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
4002
|
+
/* @__PURE__ */ jsx20("textarea", { value: activePrompt, onChange: (e) => setActivePrompt(e.target.value), className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar", placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..." }),
|
|
4003
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ jsx20("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__ */ jsx20("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
2634
4004
|
] }) }),
|
|
2635
|
-
/* @__PURE__ */
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
4005
|
+
middlePanel === "labs" ? /* @__PURE__ */ jsx20("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsx20(LabsTab, { services: labServices, onResult: (item) => {
|
|
4006
|
+
const frame = item.frames[0];
|
|
4007
|
+
if (frame?.base64) setCurrentResult(frameToGeneration(frame, item));
|
|
4008
|
+
} }) }) : /* @__PURE__ */ jsx20("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ jsxs18("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: [
|
|
4009
|
+
isGenerating && currentResult?.status === "done" && /* @__PURE__ */ jsxs18("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: [
|
|
4010
|
+
/* @__PURE__ */ jsx20("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
|
|
4011
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
|
|
2639
4012
|
] }),
|
|
2640
|
-
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */
|
|
2641
|
-
/* @__PURE__ */
|
|
2642
|
-
/* @__PURE__ */
|
|
2643
|
-
] }) : currentResult.status === "error" ? /* @__PURE__ */
|
|
2644
|
-
/* @__PURE__ */
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
/* @__PURE__ */
|
|
2647
|
-
/* @__PURE__ */
|
|
4013
|
+
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-4", children: [
|
|
4014
|
+
/* @__PURE__ */ jsx20("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
4015
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
4016
|
+
] }) : currentResult.status === "error" ? /* @__PURE__ */ jsxs18("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
|
|
4017
|
+
/* @__PURE__ */ jsx20("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
|
|
4018
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col gap-2", children: [
|
|
4019
|
+
/* @__PURE__ */ jsx20("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
|
|
4020
|
+
/* @__PURE__ */ jsx20("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
|
|
2648
4021
|
] }),
|
|
2649
|
-
/* @__PURE__ */
|
|
2650
|
-
] }) : /* @__PURE__ */
|
|
2651
|
-
/* @__PURE__ */
|
|
2652
|
-
/* @__PURE__ */
|
|
2653
|
-
/* @__PURE__ */
|
|
2654
|
-
/* @__PURE__ */
|
|
2655
|
-
/* @__PURE__ */
|
|
4022
|
+
/* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
|
|
4023
|
+
] }) : /* @__PURE__ */ jsxs18("div", { className: "h-full w-full relative flex items-center justify-center", children: [
|
|
4024
|
+
/* @__PURE__ */ jsx20("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
|
|
4025
|
+
/* @__PURE__ */ jsxs18("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: [
|
|
4026
|
+
/* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
|
|
4027
|
+
/* @__PURE__ */ jsx20(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
|
|
4028
|
+
/* @__PURE__ */ jsx20(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
|
|
2656
4029
|
] })
|
|
2657
|
-
] }) : /* @__PURE__ */
|
|
2658
|
-
/* @__PURE__ */
|
|
2659
|
-
/* @__PURE__ */
|
|
4030
|
+
] }) : /* @__PURE__ */ jsxs18("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
4031
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
|
|
4032
|
+
/* @__PURE__ */ jsx20("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
2660
4033
|
] })
|
|
2661
4034
|
] }) })
|
|
2662
4035
|
] })
|
|
2663
4036
|
] }),
|
|
2664
|
-
!isRightCollapsed && /* @__PURE__ */
|
|
2665
|
-
/* @__PURE__ */
|
|
2666
|
-
/* @__PURE__ */
|
|
2667
|
-
/* @__PURE__ */
|
|
4037
|
+
!isRightCollapsed && /* @__PURE__ */ jsx20("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
|
|
4038
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
|
|
4039
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
|
|
4040
|
+
/* @__PURE__ */ jsx20("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ jsx20("button", { onClick: () => {
|
|
2668
4041
|
setActiveTab(tab);
|
|
2669
4042
|
setIsRightCollapsed(false);
|
|
2670
|
-
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */
|
|
2671
|
-
/* @__PURE__ */
|
|
4043
|
+
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : tab === "sync" ? "cloud_sync" : "label" }) }, tab)) }),
|
|
4044
|
+
/* @__PURE__ */ jsx20("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
|
|
2672
4045
|
] }),
|
|
2673
|
-
!isRightCollapsed && /* @__PURE__ */
|
|
2674
|
-
activeTab === "
|
|
2675
|
-
|
|
4046
|
+
!isRightCollapsed && /* @__PURE__ */ jsxs18("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
4047
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ jsx20(
|
|
4048
|
+
TagManagerPanel,
|
|
4049
|
+
{
|
|
4050
|
+
workspaceTags,
|
|
4051
|
+
onTagCreate: handleTagCreate,
|
|
4052
|
+
onTagUpdate: handleTagUpdate,
|
|
4053
|
+
onTagDelete: handleTagDelete,
|
|
4054
|
+
onTagReorder: handleTagReorder,
|
|
4055
|
+
onTagMove: handleTagMove
|
|
4056
|
+
}
|
|
4057
|
+
),
|
|
4058
|
+
activeTab === "tags" && !workspaceTags && /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ jsxs18("div", { children: [
|
|
4059
|
+
/* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
|
|
4060
|
+
/* @__PURE__ */ jsx20("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
|
|
4061
|
+
] }) }),
|
|
4062
|
+
activeTab === "history" && /* @__PURE__ */ jsx20(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
4063
|
+
activeTab === "gallery" && /* @__PURE__ */ jsx20(
|
|
2676
4064
|
MediaLibrary,
|
|
2677
4065
|
{
|
|
2678
4066
|
items: galleryItems,
|
|
@@ -2686,9 +4074,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2686
4074
|
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
2687
4075
|
}
|
|
2688
4076
|
),
|
|
2689
|
-
activeTab === "inspect" && /* @__PURE__ */
|
|
2690
|
-
activeTab === "setup" && /* @__PURE__ */
|
|
2691
|
-
activeTab === "sync" && /* @__PURE__ */
|
|
4077
|
+
activeTab === "inspect" && /* @__PURE__ */ jsx20(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
|
|
4078
|
+
activeTab === "setup" && /* @__PURE__ */ jsx20(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
4079
|
+
activeTab === "sync" && /* @__PURE__ */ jsx20(
|
|
2692
4080
|
ProjectSyncTab,
|
|
2693
4081
|
{
|
|
2694
4082
|
onProjectExport: handleProjectExport,
|
|
@@ -2710,7 +4098,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2710
4098
|
}
|
|
2711
4099
|
|
|
2712
4100
|
// src/index.ts
|
|
2713
|
-
var LIB_VERSION = "1.
|
|
4101
|
+
var LIB_VERSION = "1.2.2";
|
|
2714
4102
|
export {
|
|
2715
4103
|
AvatarArchitectApp,
|
|
2716
4104
|
CollapsibleCard,
|
|
@@ -2720,6 +4108,12 @@ export {
|
|
|
2720
4108
|
HistoryPanel,
|
|
2721
4109
|
InspectPanel,
|
|
2722
4110
|
LIB_VERSION,
|
|
4111
|
+
LabBlend,
|
|
4112
|
+
LabCompare,
|
|
4113
|
+
LabImagePicker,
|
|
4114
|
+
LabLoop,
|
|
4115
|
+
LabRemix,
|
|
4116
|
+
LabsTab,
|
|
2723
4117
|
ListView,
|
|
2724
4118
|
MediaLibrary,
|
|
2725
4119
|
PillButton,
|
|
@@ -2727,15 +4121,25 @@ export {
|
|
|
2727
4121
|
PromptTab,
|
|
2728
4122
|
SectionLabel,
|
|
2729
4123
|
SetupPanel,
|
|
4124
|
+
TagManagerPanel,
|
|
4125
|
+
autoLabel,
|
|
4126
|
+
buildBlendInstruction,
|
|
4127
|
+
buildCompareInstruction,
|
|
2730
4128
|
buildFallbackPrompt,
|
|
2731
4129
|
buildGenerationPrompt,
|
|
2732
4130
|
buildImageGenerationOptions,
|
|
4131
|
+
buildLoopInstruction,
|
|
2733
4132
|
buildPromptTabPayload,
|
|
4133
|
+
buildReferenceImageMediaIds,
|
|
4134
|
+
buildRemixInstruction,
|
|
4135
|
+
buildScanInstruction,
|
|
2734
4136
|
cleanAiResponse,
|
|
2735
4137
|
createFlowServices,
|
|
2736
4138
|
exportProjectToZip,
|
|
2737
4139
|
formatTreeToMarkdown,
|
|
4140
|
+
frameToGeneration,
|
|
2738
4141
|
getFormattedTimestamp,
|
|
4142
|
+
groupGenerationsToLabItems,
|
|
2739
4143
|
importProjectFromZip,
|
|
2740
4144
|
injectXMPMetadata,
|
|
2741
4145
|
interpretSdkError,
|