@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.js
CHANGED
|
@@ -38,6 +38,12 @@ __export(index_exports, {
|
|
|
38
38
|
HistoryPanel: () => HistoryPanel,
|
|
39
39
|
InspectPanel: () => InspectPanel,
|
|
40
40
|
LIB_VERSION: () => LIB_VERSION,
|
|
41
|
+
LabBlend: () => LabBlend,
|
|
42
|
+
LabCompare: () => LabCompare,
|
|
43
|
+
LabImagePicker: () => LabImagePicker,
|
|
44
|
+
LabLoop: () => LabLoop,
|
|
45
|
+
LabRemix: () => LabRemix,
|
|
46
|
+
LabsTab: () => LabsTab,
|
|
41
47
|
ListView: () => ListView,
|
|
42
48
|
MediaLibrary: () => MediaLibrary,
|
|
43
49
|
PillButton: () => PillButton,
|
|
@@ -45,15 +51,25 @@ __export(index_exports, {
|
|
|
45
51
|
PromptTab: () => PromptTab,
|
|
46
52
|
SectionLabel: () => SectionLabel,
|
|
47
53
|
SetupPanel: () => SetupPanel,
|
|
54
|
+
TagManagerPanel: () => TagManagerPanel,
|
|
55
|
+
autoLabel: () => autoLabel,
|
|
56
|
+
buildBlendInstruction: () => buildBlendInstruction,
|
|
57
|
+
buildCompareInstruction: () => buildCompareInstruction,
|
|
48
58
|
buildFallbackPrompt: () => buildFallbackPrompt,
|
|
49
59
|
buildGenerationPrompt: () => buildGenerationPrompt,
|
|
50
60
|
buildImageGenerationOptions: () => buildImageGenerationOptions,
|
|
61
|
+
buildLoopInstruction: () => buildLoopInstruction,
|
|
51
62
|
buildPromptTabPayload: () => buildPromptTabPayload,
|
|
63
|
+
buildReferenceImageMediaIds: () => buildReferenceImageMediaIds,
|
|
64
|
+
buildRemixInstruction: () => buildRemixInstruction,
|
|
65
|
+
buildScanInstruction: () => buildScanInstruction,
|
|
52
66
|
cleanAiResponse: () => cleanAiResponse,
|
|
53
67
|
createFlowServices: () => createFlowServices,
|
|
54
68
|
exportProjectToZip: () => exportProjectToZip,
|
|
55
69
|
formatTreeToMarkdown: () => formatTreeToMarkdown,
|
|
70
|
+
frameToGeneration: () => frameToGeneration,
|
|
56
71
|
getFormattedTimestamp: () => getFormattedTimestamp,
|
|
72
|
+
groupGenerationsToLabItems: () => groupGenerationsToLabItems,
|
|
57
73
|
importProjectFromZip: () => importProjectFromZip,
|
|
58
74
|
injectXMPMetadata: () => injectXMPMetadata,
|
|
59
75
|
interpretSdkError: () => interpretSdkError,
|
|
@@ -263,7 +279,7 @@ function generateMarkdownReport(nodes, history) {
|
|
|
263
279
|
});
|
|
264
280
|
return md;
|
|
265
281
|
}
|
|
266
|
-
async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags) {
|
|
282
|
+
async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags, recentLabItemIds) {
|
|
267
283
|
const zip = new import_jszip.default();
|
|
268
284
|
const projectData = {
|
|
269
285
|
nodes,
|
|
@@ -272,7 +288,8 @@ async function exportProjectToZip(nodes, edges, history, galleryItems, settings,
|
|
|
272
288
|
galleryItems: galleryItems.map((g) => ({ ...g, base64: void 0 })),
|
|
273
289
|
settings,
|
|
274
290
|
workspaceTags: workspaceTags || null,
|
|
275
|
-
|
|
291
|
+
recentLabItemIds: recentLabItemIds || [],
|
|
292
|
+
version: "1.3.0"
|
|
276
293
|
};
|
|
277
294
|
zip.file("project.json", JSON.stringify(projectData, null, 2));
|
|
278
295
|
zip.file("projekt_bericht.md", generateMarkdownReport(nodes, history));
|
|
@@ -428,35 +445,18 @@ function createFlowServices(Flow) {
|
|
|
428
445
|
throw interpretSdkError(err);
|
|
429
446
|
}
|
|
430
447
|
},
|
|
431
|
-
generateText: async (
|
|
432
|
-
if (mode === "scan" && images?.length) {
|
|
433
|
-
try {
|
|
434
|
-
const response = await Flow.generate.text(
|
|
435
|
-
"Analyze this image and create a detailed image generation prompt that would recreate it. Focus on: subject, style, lighting, composition, color palette, mood, and technical quality. Output ONLY the prompt string, no explanations.",
|
|
436
|
-
{
|
|
437
|
-
thinkingLevel: "low",
|
|
438
|
-
systemInstruction: "You are an expert image analyst and prompt engineer. Always return ONLY the generation prompt, no headers, no quotes, no conversational filler.",
|
|
439
|
-
images
|
|
440
|
-
}
|
|
441
|
-
);
|
|
442
|
-
const result = response?.text?.trim() || "";
|
|
443
|
-
if (!result) throw new Error("Empty AI response");
|
|
444
|
-
return cleanAiResponse(result);
|
|
445
|
-
} catch (err) {
|
|
446
|
-
throw interpretSdkError(err);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
const prompt = buildGenerationPrompt(hierarchyText, mode);
|
|
448
|
+
generateText: async (text, options) => {
|
|
450
449
|
try {
|
|
451
|
-
const response = await Flow.generate.text(
|
|
450
|
+
const response = await Flow.generate.text(text, {
|
|
452
451
|
thinkingLevel: "low",
|
|
453
|
-
systemInstruction: "You are an expert prompt engineer. Always return ONLY the generated prompt text, no headers, no quotes, no conversational filler."
|
|
452
|
+
systemInstruction: options?.systemInstruction || "You are an expert prompt engineer. Always return ONLY the generated prompt text, no headers, no quotes, no conversational filler.",
|
|
453
|
+
images: options?.images
|
|
454
454
|
});
|
|
455
455
|
const result = response?.text?.trim() || "";
|
|
456
456
|
if (!result) throw new Error("Empty AI response");
|
|
457
457
|
return cleanAiResponse(result);
|
|
458
|
-
} catch {
|
|
459
|
-
|
|
458
|
+
} catch (err) {
|
|
459
|
+
throw interpretSdkError(err);
|
|
460
460
|
}
|
|
461
461
|
}
|
|
462
462
|
};
|
|
@@ -740,7 +740,35 @@ var import_react8 = require("motion/react");
|
|
|
740
740
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
741
741
|
var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
|
|
742
742
|
const workspaceInputRef = (0, import_react7.useRef)(null);
|
|
743
|
-
|
|
743
|
+
const blobTestInputRef = (0, import_react7.useRef)(null);
|
|
744
|
+
const [blobStatus, setBlobStatus] = (0, import_react7.useState)("idle");
|
|
745
|
+
const [blobThumb, setBlobThumb] = (0, import_react7.useState)(null);
|
|
746
|
+
const [blobError, setBlobError] = (0, import_react7.useState)(null);
|
|
747
|
+
const handleBlobTest = async (file) => {
|
|
748
|
+
setBlobStatus("loading");
|
|
749
|
+
setBlobThumb(null);
|
|
750
|
+
setBlobError(null);
|
|
751
|
+
const blobUrl = URL.createObjectURL(file);
|
|
752
|
+
try {
|
|
753
|
+
const res = await fetch(blobUrl);
|
|
754
|
+
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
755
|
+
const blob = await res.blob();
|
|
756
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
757
|
+
const reader = new FileReader();
|
|
758
|
+
reader.onload = () => resolve(reader.result);
|
|
759
|
+
reader.onerror = () => reject(new Error("FileReader failed"));
|
|
760
|
+
reader.readAsDataURL(blob);
|
|
761
|
+
});
|
|
762
|
+
setBlobThumb(dataUrl);
|
|
763
|
+
setBlobStatus("ok");
|
|
764
|
+
} catch (e) {
|
|
765
|
+
setBlobError(e.message || String(e));
|
|
766
|
+
setBlobStatus("error");
|
|
767
|
+
} finally {
|
|
768
|
+
URL.revokeObjectURL(blobUrl);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react8.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10 overflow-y-auto", children: [
|
|
744
772
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
745
773
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
746
774
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Workspace Management" }),
|
|
@@ -753,6 +781,46 @@ var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
|
|
|
753
781
|
e.target.value = "";
|
|
754
782
|
} })
|
|
755
783
|
] }),
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "blob: URL Test" }),
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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." })
|
|
788
|
+
] }),
|
|
789
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
790
|
+
PillButton,
|
|
791
|
+
{
|
|
792
|
+
variant: "outline",
|
|
793
|
+
icon: blobStatus === "loading" ? "sync" : "image_search",
|
|
794
|
+
onClick: () => blobTestInputRef.current?.click(),
|
|
795
|
+
children: "Bild w\xE4hlen & testen"
|
|
796
|
+
}
|
|
797
|
+
),
|
|
798
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
799
|
+
"input",
|
|
800
|
+
{
|
|
801
|
+
ref: blobTestInputRef,
|
|
802
|
+
type: "file",
|
|
803
|
+
accept: "image/*",
|
|
804
|
+
className: "hidden",
|
|
805
|
+
onChange: (e) => {
|
|
806
|
+
const f = e.target.files?.[0];
|
|
807
|
+
if (f) handleBlobTest(f);
|
|
808
|
+
e.target.value = "";
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
),
|
|
812
|
+
blobStatus === "ok" && blobThumb && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
813
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: blobThumb, alt: "blob test", className: "w-full max-h-48 object-contain rounded-xl border border-white/10 bg-black/40" }),
|
|
814
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-[10px] text-green-400 font-mono flex items-center gap-1", children: [
|
|
815
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "check_circle" }),
|
|
816
|
+
"blob: fetch \u2713 \u2014 base64 erfolgreich"
|
|
817
|
+
] })
|
|
818
|
+
] }),
|
|
819
|
+
blobStatus === "error" && blobError && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-[10px] text-red-400 font-mono flex items-start gap-1", children: [
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "material-symbols-outlined text-[14px] shrink-0", children: "error" }),
|
|
821
|
+
blobError
|
|
822
|
+
] })
|
|
823
|
+
] }),
|
|
756
824
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-auto p-4 bg-white/5 rounded-2xl border border-white/5 flex flex-col gap-2 opacity-50", children: [
|
|
757
825
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
|
|
758
826
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
|
|
@@ -922,7 +990,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
922
990
|
}
|
|
923
991
|
|
|
924
992
|
// src/components/AvatarArchitectApp.tsx
|
|
925
|
-
var
|
|
993
|
+
var import_react21 = require("react");
|
|
926
994
|
|
|
927
995
|
// src/components/PromptTab.tsx
|
|
928
996
|
var import_react12 = require("react");
|
|
@@ -1670,10 +1738,1207 @@ var ProjectSyncTab = ({
|
|
|
1670
1738
|
] }) });
|
|
1671
1739
|
};
|
|
1672
1740
|
|
|
1673
|
-
// src/
|
|
1741
|
+
// src/lib/labHelpers.ts
|
|
1742
|
+
function groupGenerationsToLabItems(generations) {
|
|
1743
|
+
const map = /* @__PURE__ */ new Map();
|
|
1744
|
+
for (const gen of generations) {
|
|
1745
|
+
if (gen.status !== "done" || !gen.base64) continue;
|
|
1746
|
+
const prompt = gen.prompt || "";
|
|
1747
|
+
const existing = map.get(prompt);
|
|
1748
|
+
const frame = {
|
|
1749
|
+
id: gen.id,
|
|
1750
|
+
base64: gen.base64,
|
|
1751
|
+
mediaId: gen.mediaId,
|
|
1752
|
+
seed: gen.seed,
|
|
1753
|
+
model: gen.model,
|
|
1754
|
+
source: gen.type === "import" ? "zip-import" : "generated"
|
|
1755
|
+
};
|
|
1756
|
+
if (existing) {
|
|
1757
|
+
existing.frames.push(frame);
|
|
1758
|
+
} else {
|
|
1759
|
+
map.set(prompt, {
|
|
1760
|
+
id: gen.id,
|
|
1761
|
+
prompt,
|
|
1762
|
+
tags: gen.tags,
|
|
1763
|
+
frames: [frame]
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return Array.from(map.values());
|
|
1768
|
+
}
|
|
1769
|
+
function frameToGeneration(frame, item) {
|
|
1770
|
+
return {
|
|
1771
|
+
id: frame.id,
|
|
1772
|
+
nodeId: "1",
|
|
1773
|
+
base64: frame.base64,
|
|
1774
|
+
mediaId: frame.mediaId,
|
|
1775
|
+
prompt: item.prompt,
|
|
1776
|
+
seed: frame.seed,
|
|
1777
|
+
model: frame.model,
|
|
1778
|
+
timestamp: Date.now(),
|
|
1779
|
+
status: "done",
|
|
1780
|
+
tags: item.tags || [],
|
|
1781
|
+
type: frame.source === "zip-import" ? "import" : "generation"
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
function buildImageContext(images) {
|
|
1785
|
+
const active = images.filter((i) => i.sendToPromptAI);
|
|
1786
|
+
if (!active.length) return "";
|
|
1787
|
+
const lines = active.map((i) => {
|
|
1788
|
+
const role = i.roleForPrompt ? ` (${i.roleForPrompt})` : "";
|
|
1789
|
+
const promptSnippet = i.item.prompt ? `: "${i.item.prompt.slice(0, 120)}"` : "";
|
|
1790
|
+
return `- ${i.label}${role}${promptSnippet}`;
|
|
1791
|
+
});
|
|
1792
|
+
return `
|
|
1793
|
+
|
|
1794
|
+
Reference images:
|
|
1795
|
+
${lines.join("\n")}`;
|
|
1796
|
+
}
|
|
1797
|
+
function buildScanInstruction() {
|
|
1798
|
+
return {
|
|
1799
|
+
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.",
|
|
1800
|
+
systemInstruction: "You are an expert image analyst and prompt engineer. Return ONLY the generation prompt, no headers, no quotes, no explanations."
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
function buildRemixInstruction(images, userInstruction, currentPrompt) {
|
|
1804
|
+
const imgContext = buildImageContext(images);
|
|
1805
|
+
return {
|
|
1806
|
+
text: `Current prompt: "${currentPrompt}"
|
|
1807
|
+
|
|
1808
|
+
Instruction: ${userInstruction}${imgContext}
|
|
1809
|
+
|
|
1810
|
+
Create a creative variation of this prompt based on the instruction. Output ONLY the new prompt string.`,
|
|
1811
|
+
systemInstruction: "You are an expert prompt engineer. Return ONLY the new generation prompt, no headers, no quotes, no explanations."
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
function buildBlendInstruction(images, userInstruction) {
|
|
1815
|
+
const imgContext = buildImageContext(images);
|
|
1816
|
+
const promptList = images.map((i) => `${i.label}: "${i.item.prompt}"`).join("\n");
|
|
1817
|
+
return {
|
|
1818
|
+
text: `Blend these prompts into one new prompt:
|
|
1819
|
+
${promptList}
|
|
1820
|
+
|
|
1821
|
+
Instruction: ${userInstruction}${imgContext}
|
|
1822
|
+
|
|
1823
|
+
Output ONLY the blended prompt string.`,
|
|
1824
|
+
systemInstruction: "You are an expert prompt engineer. Blend the given prompts into one coherent generation prompt. Return ONLY the prompt string."
|
|
1825
|
+
};
|
|
1826
|
+
}
|
|
1827
|
+
function buildCompareInstruction(images, userInstruction) {
|
|
1828
|
+
const imgContext = buildImageContext(images);
|
|
1829
|
+
return {
|
|
1830
|
+
text: `${userInstruction}${imgContext}
|
|
1831
|
+
|
|
1832
|
+
Analyze the provided images according to the instruction above.`,
|
|
1833
|
+
systemInstruction: "You are an expert image analyst. Provide a clear, structured analysis. Be concise and actionable."
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
function buildLoopInstruction(rounds, newInstruction) {
|
|
1837
|
+
const history = rounds.map(
|
|
1838
|
+
(r, i) => `Round ${i + 1} prompt: "${r.prompt}"
|
|
1839
|
+
Instruction given: "${r.instruction}"`
|
|
1840
|
+
).join("\n\n");
|
|
1841
|
+
const activeImages = rounds.flatMap((r) => r.images.filter((i) => i.sendToPromptAI));
|
|
1842
|
+
const imgContext = activeImages.length ? buildImageContext(activeImages) : "";
|
|
1843
|
+
return {
|
|
1844
|
+
text: `Previous rounds:
|
|
1845
|
+
${history}
|
|
1846
|
+
|
|
1847
|
+
New instruction: ${newInstruction}${imgContext}
|
|
1848
|
+
|
|
1849
|
+
Improve the prompt cumulatively based on all previous rounds and the new instruction. Output ONLY the new prompt string.`,
|
|
1850
|
+
systemInstruction: "You are an expert prompt engineer refining prompts iteratively. Each round the prompt should improve. Return ONLY the new prompt string."
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
function autoLabel(index) {
|
|
1854
|
+
return `Img${String.fromCharCode(65 + index)}`;
|
|
1855
|
+
}
|
|
1856
|
+
function buildReferenceImageMediaIds(images) {
|
|
1857
|
+
return images.filter((i) => i.sendToImageGen && i.frame.mediaId).map((i) => i.frame.mediaId);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
// src/components/labs/LabsTab.tsx
|
|
1861
|
+
var import_react19 = require("react");
|
|
1862
|
+
|
|
1863
|
+
// src/components/labs/LabRemix.tsx
|
|
1864
|
+
var import_react15 = require("react");
|
|
1865
|
+
|
|
1866
|
+
// src/components/labs/LabImagePicker.tsx
|
|
1867
|
+
var import_react14 = require("react");
|
|
1674
1868
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1869
|
+
var LabImagePicker = ({
|
|
1870
|
+
availableItems,
|
|
1871
|
+
recentItems,
|
|
1872
|
+
onSelect,
|
|
1873
|
+
onClose,
|
|
1874
|
+
title = "Bild w\xE4hlen"
|
|
1875
|
+
}) => {
|
|
1876
|
+
const [search, setSearch] = (0, import_react14.useState)("");
|
|
1877
|
+
const [drillItem, setDrillItem] = (0, import_react14.useState)(null);
|
|
1878
|
+
const filtered = availableItems.filter(
|
|
1879
|
+
(item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
|
|
1880
|
+
);
|
|
1881
|
+
const handleItemClick = (item) => {
|
|
1882
|
+
if (item.frames.length === 1) {
|
|
1883
|
+
onSelect(item, item.frames[0]);
|
|
1884
|
+
onClose();
|
|
1885
|
+
} else {
|
|
1886
|
+
setDrillItem(item);
|
|
1887
|
+
}
|
|
1888
|
+
};
|
|
1889
|
+
const renderItemCard = (item) => {
|
|
1890
|
+
const firstFrame = item.frames[0];
|
|
1891
|
+
if (!firstFrame) return null;
|
|
1892
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1893
|
+
"button",
|
|
1894
|
+
{
|
|
1895
|
+
onClick: () => handleItemClick(item),
|
|
1896
|
+
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",
|
|
1897
|
+
children: [
|
|
1898
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative w-full aspect-square rounded-lg overflow-hidden bg-black/40", children: [
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: firstFrame.base64, className: "w-full h-full object-cover" }),
|
|
1900
|
+
item.frames.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
|
|
1901
|
+
item.frames.length,
|
|
1902
|
+
"x"
|
|
1903
|
+
] })
|
|
1904
|
+
] }),
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[10px] text-white/50 leading-tight line-clamp-2", children: item.prompt || "(kein Prompt)" })
|
|
1906
|
+
]
|
|
1907
|
+
},
|
|
1908
|
+
item.id
|
|
1909
|
+
);
|
|
1910
|
+
};
|
|
1911
|
+
if (drillItem) {
|
|
1912
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col h-full", children: [
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 px-4 py-3 border-b border-white/5 shrink-0", children: [
|
|
1914
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setDrillItem(null), className: "text-white/40 active:text-white shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "arrow_back" }) }),
|
|
1915
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60 truncate flex-1", children: drillItem.prompt || "(kein Prompt)" }),
|
|
1916
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: onClose, className: "text-white/30 active:text-white shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
|
|
1917
|
+
] }),
|
|
1918
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 py-3", children: [
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2", children: [
|
|
1920
|
+
drillItem.frames.length,
|
|
1921
|
+
" Varianten"
|
|
1922
|
+
] }),
|
|
1923
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "grid grid-cols-3 gap-2", children: drillItem.frames.map((frame, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
1924
|
+
"button",
|
|
1925
|
+
{
|
|
1926
|
+
onClick: () => {
|
|
1927
|
+
onSelect(drillItem, frame);
|
|
1928
|
+
onClose();
|
|
1929
|
+
},
|
|
1930
|
+
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",
|
|
1931
|
+
children: [
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative w-full aspect-square rounded-lg overflow-hidden bg-black/40", children: [
|
|
1933
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: frame.base64, className: "w-full h-full object-cover" }),
|
|
1934
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("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: [
|
|
1935
|
+
"#",
|
|
1936
|
+
i + 1
|
|
1937
|
+
] })
|
|
1938
|
+
] }),
|
|
1939
|
+
frame.seed != null && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { className: "text-[9px] text-white/30 font-mono truncate", children: [
|
|
1940
|
+
"seed ",
|
|
1941
|
+
frame.seed
|
|
1942
|
+
] })
|
|
1943
|
+
]
|
|
1944
|
+
},
|
|
1945
|
+
frame.id
|
|
1946
|
+
)) })
|
|
1947
|
+
] })
|
|
1948
|
+
] });
|
|
1949
|
+
}
|
|
1950
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col h-full", children: [
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between px-4 py-3 border-b border-white/5 shrink-0", children: [
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-widest text-white/60", children: title }),
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: onClose, className: "text-white/30 active:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
|
|
1954
|
+
] }),
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-3 py-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1956
|
+
"input",
|
|
1957
|
+
{
|
|
1958
|
+
value: search,
|
|
1959
|
+
onChange: (e) => setSearch(e.target.value),
|
|
1960
|
+
placeholder: "Suchen...",
|
|
1961
|
+
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"
|
|
1962
|
+
}
|
|
1963
|
+
) }),
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 pb-3", children: [
|
|
1965
|
+
recentItems.length > 0 && !search && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
1966
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2 mt-1", children: "Zuletzt verwendet" }),
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "grid grid-cols-3 gap-2 mb-4", children: recentItems.slice(0, 6).map(renderItemCard) }),
|
|
1968
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2", children: "Alle Bilder" })
|
|
1969
|
+
] }),
|
|
1970
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "grid grid-cols-3 gap-2", children: filtered.map(renderItemCard) }),
|
|
1971
|
+
filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-center text-[11px] text-white/20 py-8", children: "Keine Bilder vorhanden" })
|
|
1972
|
+
] })
|
|
1973
|
+
] });
|
|
1974
|
+
};
|
|
1975
|
+
|
|
1976
|
+
// src/components/labs/LabRemix.tsx
|
|
1977
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1978
|
+
var LabRemix = ({ services, onResult }) => {
|
|
1979
|
+
const [showPicker, setShowPicker] = (0, import_react15.useState)(false);
|
|
1980
|
+
const [selected, setSelected] = (0, import_react15.useState)(null);
|
|
1981
|
+
const [instruction, setInstruction] = (0, import_react15.useState)("");
|
|
1982
|
+
const [generatedPrompt, setGeneratedPrompt] = (0, import_react15.useState)("");
|
|
1983
|
+
const [resultImage, setResultImage] = (0, import_react15.useState)(null);
|
|
1984
|
+
const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react15.useState)(false);
|
|
1985
|
+
const [isGeneratingImage, setIsGeneratingImage] = (0, import_react15.useState)(false);
|
|
1986
|
+
const handleSelectImage = (item, frame) => {
|
|
1987
|
+
services.onItemUsed(item);
|
|
1988
|
+
setSelected({
|
|
1989
|
+
item,
|
|
1990
|
+
frame,
|
|
1991
|
+
label: autoLabel(0),
|
|
1992
|
+
sendToPromptAI: true,
|
|
1993
|
+
sendToImageGen: false,
|
|
1994
|
+
roleForPrompt: "",
|
|
1995
|
+
roleForImage: ""
|
|
1996
|
+
});
|
|
1997
|
+
setGeneratedPrompt("");
|
|
1998
|
+
setResultImage(null);
|
|
1999
|
+
};
|
|
2000
|
+
const handleGeneratePrompt = async () => {
|
|
2001
|
+
if (!selected || !instruction.trim()) return;
|
|
2002
|
+
setIsGeneratingPrompt(true);
|
|
2003
|
+
try {
|
|
2004
|
+
const { text, systemInstruction } = buildRemixInstruction(
|
|
2005
|
+
[selected],
|
|
2006
|
+
instruction,
|
|
2007
|
+
selected.item.prompt
|
|
2008
|
+
);
|
|
2009
|
+
const images = selected.sendToPromptAI ? [{ base64: selected.frame.base64, mimeType: "image/png" }] : void 0;
|
|
2010
|
+
const result = await services.generateText(text, { systemInstruction, images });
|
|
2011
|
+
setGeneratedPrompt(result);
|
|
2012
|
+
} finally {
|
|
2013
|
+
setIsGeneratingPrompt(false);
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
const handleGenerateImage = async () => {
|
|
2017
|
+
if (!generatedPrompt) return;
|
|
2018
|
+
setIsGeneratingImage(true);
|
|
2019
|
+
try {
|
|
2020
|
+
const refIds = buildReferenceImageMediaIds(selected ? [selected] : []);
|
|
2021
|
+
const { base64, mediaId } = await services.generateImage({
|
|
2022
|
+
prompt: generatedPrompt,
|
|
2023
|
+
aspectRatio: selected?.frame.aspectRatio || "1:1",
|
|
2024
|
+
modelDisplayName: selected?.frame.model || "\u{1F34C} Nano Banana Pro",
|
|
2025
|
+
...refIds.length ? { referenceImageMediaIds: refIds } : {}
|
|
2026
|
+
});
|
|
2027
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2028
|
+
setResultImage(newBase64);
|
|
2029
|
+
const frameId = crypto.randomUUID();
|
|
2030
|
+
const newItem = {
|
|
2031
|
+
id: frameId,
|
|
2032
|
+
prompt: generatedPrompt,
|
|
2033
|
+
tags: selected?.item.tags || [],
|
|
2034
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
2035
|
+
};
|
|
2036
|
+
services.saveResult?.(newItem);
|
|
2037
|
+
onResult?.(newItem);
|
|
2038
|
+
} finally {
|
|
2039
|
+
setIsGeneratingImage(false);
|
|
2040
|
+
}
|
|
2041
|
+
};
|
|
2042
|
+
if (showPicker) {
|
|
2043
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2044
|
+
LabImagePicker,
|
|
2045
|
+
{
|
|
2046
|
+
availableItems: services.availableItems,
|
|
2047
|
+
recentItems: services.recentItems,
|
|
2048
|
+
onSelect: handleSelectImage,
|
|
2049
|
+
onClose: () => setShowPicker(false),
|
|
2050
|
+
title: "Basis-Bild w\xE4hlen"
|
|
2051
|
+
}
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2055
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2056
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Basis-Bild" }),
|
|
2057
|
+
selected ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2058
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("img", { src: selected.frame.base64, className: "w-16 h-16 object-cover rounded-lg shrink-0" }),
|
|
2059
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex-1 min-w-0 flex flex-col gap-1.5", children: [
|
|
2060
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[10px] text-white/60 leading-tight line-clamp-2", children: selected.item.prompt }),
|
|
2061
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-[10px] text-white/50", children: [
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2063
|
+
"input",
|
|
2064
|
+
{
|
|
2065
|
+
type: "checkbox",
|
|
2066
|
+
checked: selected.sendToPromptAI,
|
|
2067
|
+
onChange: (e) => setSelected((s) => s ? { ...s, sendToPromptAI: e.target.checked } : s)
|
|
2068
|
+
}
|
|
2069
|
+
),
|
|
2070
|
+
"Bild an KI (Prompt)"
|
|
2071
|
+
] }),
|
|
2072
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("label", { className: "flex items-center gap-2 text-[10px] text-white/50", children: [
|
|
2073
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2074
|
+
"input",
|
|
2075
|
+
{
|
|
2076
|
+
type: "checkbox",
|
|
2077
|
+
checked: selected.sendToImageGen,
|
|
2078
|
+
onChange: (e) => setSelected((s) => s ? { ...s, sendToImageGen: e.target.checked } : s)
|
|
2079
|
+
}
|
|
2080
|
+
),
|
|
2081
|
+
"Bild als Referenz (Generierung)"
|
|
2082
|
+
] }),
|
|
2083
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2084
|
+
"input",
|
|
2085
|
+
{
|
|
2086
|
+
value: selected.roleForPrompt || "",
|
|
2087
|
+
onChange: (e) => setSelected((s) => s ? { ...s, roleForPrompt: e.target.value } : s),
|
|
2088
|
+
placeholder: "Rolle z.B. Subjekt, Farbreferenz...",
|
|
2089
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-1 text-[10px] text-white/70 outline-none"
|
|
2090
|
+
}
|
|
2091
|
+
)
|
|
2092
|
+
] }),
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { onClick: () => setShowPicker(true), className: "text-white/30 active:text-white self-start", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "swap_horiz" }) })
|
|
2094
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2095
|
+
"button",
|
|
2096
|
+
{
|
|
2097
|
+
onClick: () => setShowPicker(true),
|
|
2098
|
+
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",
|
|
2099
|
+
children: [
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }),
|
|
2101
|
+
"Bild w\xE4hlen"
|
|
2102
|
+
]
|
|
2103
|
+
}
|
|
2104
|
+
)
|
|
2105
|
+
] }),
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Anweisung" }),
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2109
|
+
"textarea",
|
|
2110
|
+
{
|
|
2111
|
+
value: instruction,
|
|
2112
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2113
|
+
placeholder: "z.B. Mache es dramatischer, andere Lichtstimmung...",
|
|
2114
|
+
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",
|
|
2115
|
+
rows: 3
|
|
2116
|
+
}
|
|
2117
|
+
)
|
|
2118
|
+
] }),
|
|
2119
|
+
services.workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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)" }),
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2121
|
+
"button",
|
|
2122
|
+
{
|
|
2123
|
+
onClick: handleGeneratePrompt,
|
|
2124
|
+
disabled: !selected || !instruction.trim() || isGeneratingPrompt,
|
|
2125
|
+
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",
|
|
2126
|
+
children: isGeneratingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2127
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2128
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Generiere Prompt..." })
|
|
2129
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2130
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "auto_fix_high" }),
|
|
2131
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Prompt generieren" })
|
|
2132
|
+
] })
|
|
2133
|
+
}
|
|
2134
|
+
),
|
|
2135
|
+
generatedPrompt && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2136
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Generierter Prompt" }),
|
|
2137
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2138
|
+
"textarea",
|
|
2139
|
+
{
|
|
2140
|
+
value: generatedPrompt,
|
|
2141
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2142
|
+
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",
|
|
2143
|
+
rows: 4
|
|
2144
|
+
}
|
|
2145
|
+
),
|
|
2146
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2147
|
+
"button",
|
|
2148
|
+
{
|
|
2149
|
+
onClick: handleGenerateImage,
|
|
2150
|
+
disabled: isGeneratingImage,
|
|
2151
|
+
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",
|
|
2152
|
+
children: isGeneratingImage ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2153
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2154
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Generiere Bild..." })
|
|
2155
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2156
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2157
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "Bild generieren" })
|
|
2158
|
+
] })
|
|
2159
|
+
}
|
|
2160
|
+
)
|
|
2161
|
+
] }),
|
|
2162
|
+
resultImage && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2163
|
+
] });
|
|
2164
|
+
};
|
|
2165
|
+
|
|
2166
|
+
// src/components/labs/LabBlend.tsx
|
|
2167
|
+
var import_react16 = require("react");
|
|
2168
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2169
|
+
var LabBlend = ({ services, onResult }) => {
|
|
2170
|
+
const [showPickerFor, setShowPickerFor] = (0, import_react16.useState)(null);
|
|
2171
|
+
const [selectedImages, setSelectedImages] = (0, import_react16.useState)([]);
|
|
2172
|
+
const [instruction, setInstruction] = (0, import_react16.useState)("");
|
|
2173
|
+
const [generatedPrompt, setGeneratedPrompt] = (0, import_react16.useState)("");
|
|
2174
|
+
const [resultImage, setResultImage] = (0, import_react16.useState)(null);
|
|
2175
|
+
const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react16.useState)(false);
|
|
2176
|
+
const [isGeneratingImage, setIsGeneratingImage] = (0, import_react16.useState)(false);
|
|
2177
|
+
const handleSelectImage = (index, item, frame) => {
|
|
2178
|
+
services.onItemUsed(item);
|
|
2179
|
+
const newImg = {
|
|
2180
|
+
item,
|
|
2181
|
+
frame,
|
|
2182
|
+
label: autoLabel(index),
|
|
2183
|
+
sendToPromptAI: true,
|
|
2184
|
+
sendToImageGen: false,
|
|
2185
|
+
roleForPrompt: "",
|
|
2186
|
+
roleForImage: ""
|
|
2187
|
+
};
|
|
2188
|
+
setSelectedImages((prev) => {
|
|
2189
|
+
const next = [...prev];
|
|
2190
|
+
next[index] = newImg;
|
|
2191
|
+
return next;
|
|
2192
|
+
});
|
|
2193
|
+
setShowPickerFor(null);
|
|
2194
|
+
setGeneratedPrompt("");
|
|
2195
|
+
setResultImage(null);
|
|
2196
|
+
};
|
|
2197
|
+
const addSlot = () => setSelectedImages((prev) => [...prev, null]);
|
|
2198
|
+
const removeSlot = (i) => setSelectedImages((prev) => prev.filter((_, idx) => idx !== i));
|
|
2199
|
+
const updateImg = (i, updates) => setSelectedImages((prev) => prev.map((img, idx) => idx === i && img ? { ...img, ...updates } : img));
|
|
2200
|
+
const handleGeneratePrompt = async () => {
|
|
2201
|
+
const filled = selectedImages.filter(Boolean);
|
|
2202
|
+
if (!filled.length || !instruction.trim()) return;
|
|
2203
|
+
setIsGeneratingPrompt(true);
|
|
2204
|
+
try {
|
|
2205
|
+
const { text, systemInstruction } = buildBlendInstruction(filled, instruction);
|
|
2206
|
+
const images = filled.filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2207
|
+
const result = await services.generateText(text, { systemInstruction, images: images.length ? images : void 0 });
|
|
2208
|
+
setGeneratedPrompt(result);
|
|
2209
|
+
} finally {
|
|
2210
|
+
setIsGeneratingPrompt(false);
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
2213
|
+
const handleGenerateImage = async () => {
|
|
2214
|
+
if (!generatedPrompt) return;
|
|
2215
|
+
setIsGeneratingImage(true);
|
|
2216
|
+
try {
|
|
2217
|
+
const filled = selectedImages.filter(Boolean);
|
|
2218
|
+
const refIds = buildReferenceImageMediaIds(filled);
|
|
2219
|
+
const { base64, mediaId } = await services.generateImage({
|
|
2220
|
+
prompt: generatedPrompt,
|
|
2221
|
+
aspectRatio: "1:1",
|
|
2222
|
+
modelDisplayName: "\u{1F34C} Nano Banana Pro",
|
|
2223
|
+
...refIds.length ? { referenceImageMediaIds: refIds } : {}
|
|
2224
|
+
});
|
|
2225
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2226
|
+
setResultImage(newBase64);
|
|
2227
|
+
const frameId = crypto.randomUUID();
|
|
2228
|
+
const newItem = {
|
|
2229
|
+
id: frameId,
|
|
2230
|
+
prompt: generatedPrompt,
|
|
2231
|
+
tags: [],
|
|
2232
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
2233
|
+
};
|
|
2234
|
+
services.saveResult?.(newItem);
|
|
2235
|
+
onResult?.(newItem);
|
|
2236
|
+
} finally {
|
|
2237
|
+
setIsGeneratingImage(false);
|
|
2238
|
+
}
|
|
2239
|
+
};
|
|
2240
|
+
if (showPickerFor !== null) {
|
|
2241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2242
|
+
LabImagePicker,
|
|
2243
|
+
{
|
|
2244
|
+
availableItems: services.availableItems,
|
|
2245
|
+
recentItems: services.recentItems,
|
|
2246
|
+
onSelect: (item, frame) => handleSelectImage(showPickerFor, item, frame),
|
|
2247
|
+
onClose: () => setShowPickerFor(null),
|
|
2248
|
+
title: `${autoLabel(showPickerFor)} w\xE4hlen`
|
|
2249
|
+
}
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2253
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder" }),
|
|
2254
|
+
selectedImages.map((img, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2255
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
|
|
2256
|
+
img ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0", onClick: () => setShowPickerFor(i) }),
|
|
2258
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
|
|
2259
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
|
|
2260
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex gap-3", children: [
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updateImg(i, { sendToPromptAI: e.target.checked }) }),
|
|
2263
|
+
"KI-Prompt"
|
|
2264
|
+
] }),
|
|
2265
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2266
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("input", { type: "checkbox", checked: img.sendToImageGen, onChange: (e) => updateImg(i, { sendToImageGen: e.target.checked }) }),
|
|
2267
|
+
"Referenz"
|
|
2268
|
+
] })
|
|
2269
|
+
] }),
|
|
2270
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2271
|
+
"input",
|
|
2272
|
+
{
|
|
2273
|
+
value: img.roleForPrompt || "",
|
|
2274
|
+
onChange: (e) => updateImg(i, { roleForPrompt: e.target.value }),
|
|
2275
|
+
placeholder: "Rolle f\xFCr Prompt...",
|
|
2276
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2277
|
+
}
|
|
2278
|
+
),
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2280
|
+
"input",
|
|
2281
|
+
{
|
|
2282
|
+
value: img.roleForImage || "",
|
|
2283
|
+
onChange: (e) => updateImg(i, { roleForImage: e.target.value }),
|
|
2284
|
+
placeholder: "Rolle f\xFCr Generierung...",
|
|
2285
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2286
|
+
}
|
|
2287
|
+
)
|
|
2288
|
+
] })
|
|
2289
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("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" }),
|
|
2290
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { onClick: () => removeSlot(i), className: "text-white/20 active:text-red-400 self-start", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "close" }) })
|
|
2291
|
+
] }, i)),
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2293
|
+
"button",
|
|
2294
|
+
{
|
|
2295
|
+
onClick: addSlot,
|
|
2296
|
+
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",
|
|
2297
|
+
children: [
|
|
2298
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2299
|
+
"Bild hinzuf\xFCgen"
|
|
2300
|
+
]
|
|
2301
|
+
}
|
|
2302
|
+
),
|
|
2303
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Blend-Anweisung" }),
|
|
2305
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2306
|
+
"textarea",
|
|
2307
|
+
{
|
|
2308
|
+
value: instruction,
|
|
2309
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2310
|
+
placeholder: "z.B. Kombiniere die Stimmung von ImgA mit der Komposition von ImgB...",
|
|
2311
|
+
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",
|
|
2312
|
+
rows: 3
|
|
2313
|
+
}
|
|
2314
|
+
)
|
|
2315
|
+
] }),
|
|
2316
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2317
|
+
"button",
|
|
2318
|
+
{
|
|
2319
|
+
onClick: handleGeneratePrompt,
|
|
2320
|
+
disabled: selectedImages.filter(Boolean).length < 2 || !instruction.trim() || isGeneratingPrompt,
|
|
2321
|
+
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",
|
|
2322
|
+
children: isGeneratingPrompt ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2324
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Blend l\xE4uft..." })
|
|
2325
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2326
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "merge" }),
|
|
2327
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Prompts blenden" })
|
|
2328
|
+
] })
|
|
2329
|
+
}
|
|
2330
|
+
),
|
|
2331
|
+
generatedPrompt && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
2332
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Blend-Prompt" }),
|
|
2333
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2334
|
+
"textarea",
|
|
2335
|
+
{
|
|
2336
|
+
value: generatedPrompt,
|
|
2337
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2338
|
+
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",
|
|
2339
|
+
rows: 4
|
|
2340
|
+
}
|
|
2341
|
+
),
|
|
2342
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2343
|
+
"button",
|
|
2344
|
+
{
|
|
2345
|
+
onClick: handleGenerateImage,
|
|
2346
|
+
disabled: isGeneratingImage,
|
|
2347
|
+
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",
|
|
2348
|
+
children: isGeneratingImage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2349
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2350
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Generiere..." })
|
|
2351
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2353
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "Bild generieren" })
|
|
2354
|
+
] })
|
|
2355
|
+
}
|
|
2356
|
+
)
|
|
2357
|
+
] }),
|
|
2358
|
+
resultImage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2359
|
+
] });
|
|
2360
|
+
};
|
|
2361
|
+
|
|
2362
|
+
// src/components/labs/LabCompare.tsx
|
|
2363
|
+
var import_react17 = require("react");
|
|
2364
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2365
|
+
var LabCompare = ({ services, onResult }) => {
|
|
2366
|
+
const [showPickerFor, setShowPickerFor] = (0, import_react17.useState)(null);
|
|
2367
|
+
const [selectedImages, setSelectedImages] = (0, import_react17.useState)([]);
|
|
2368
|
+
const [instruction, setInstruction] = (0, import_react17.useState)("");
|
|
2369
|
+
const [analysis, setAnalysis] = (0, import_react17.useState)("");
|
|
2370
|
+
const [generatedPrompt, setGeneratedPrompt] = (0, import_react17.useState)("");
|
|
2371
|
+
const [resultImage, setResultImage] = (0, import_react17.useState)(null);
|
|
2372
|
+
const [isAnalyzing, setIsAnalyzing] = (0, import_react17.useState)(false);
|
|
2373
|
+
const [isGeneratingImage, setIsGeneratingImage] = (0, import_react17.useState)(false);
|
|
2374
|
+
const handleSelectImage = (index, item, frame) => {
|
|
2375
|
+
services.onItemUsed(item);
|
|
2376
|
+
const newImg = {
|
|
2377
|
+
item,
|
|
2378
|
+
frame,
|
|
2379
|
+
label: autoLabel(index),
|
|
2380
|
+
sendToPromptAI: true,
|
|
2381
|
+
sendToImageGen: false,
|
|
2382
|
+
roleForPrompt: "",
|
|
2383
|
+
roleForImage: ""
|
|
2384
|
+
};
|
|
2385
|
+
setSelectedImages((prev) => {
|
|
2386
|
+
const next = [...prev];
|
|
2387
|
+
next[index] = newImg;
|
|
2388
|
+
return next;
|
|
2389
|
+
});
|
|
2390
|
+
setShowPickerFor(null);
|
|
2391
|
+
setAnalysis("");
|
|
2392
|
+
};
|
|
2393
|
+
const updateImg = (i, updates) => setSelectedImages((prev) => prev.map((img, idx) => idx === i && img ? { ...img, ...updates } : img));
|
|
2394
|
+
const addSlot = () => setSelectedImages((prev) => [...prev, null]);
|
|
2395
|
+
const removeSlot = (i) => setSelectedImages((prev) => prev.filter((_, idx) => idx !== i));
|
|
2396
|
+
const handleAnalyze = async () => {
|
|
2397
|
+
const filled = selectedImages.filter(Boolean);
|
|
2398
|
+
if (!filled.length || !instruction.trim()) return;
|
|
2399
|
+
setIsAnalyzing(true);
|
|
2400
|
+
try {
|
|
2401
|
+
const { text, systemInstruction } = buildCompareInstruction(filled, instruction);
|
|
2402
|
+
const images = filled.filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2403
|
+
const result = await services.generateText(text, { systemInstruction, images: images.length ? images : void 0 });
|
|
2404
|
+
setAnalysis(result);
|
|
2405
|
+
setGeneratedPrompt("");
|
|
2406
|
+
} finally {
|
|
2407
|
+
setIsAnalyzing(false);
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
const handleGenerateImage = async () => {
|
|
2411
|
+
if (!generatedPrompt) return;
|
|
2412
|
+
setIsGeneratingImage(true);
|
|
2413
|
+
try {
|
|
2414
|
+
const { base64, mediaId } = await services.generateImage({ prompt: generatedPrompt, aspectRatio: "1:1", modelDisplayName: "\u{1F34C} Nano Banana Pro" });
|
|
2415
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2416
|
+
setResultImage(newBase64);
|
|
2417
|
+
const frameId = crypto.randomUUID();
|
|
2418
|
+
const newItem = {
|
|
2419
|
+
id: frameId,
|
|
2420
|
+
prompt: generatedPrompt,
|
|
2421
|
+
tags: [],
|
|
2422
|
+
frames: [{ id: frameId, base64: newBase64, mediaId, source: "generated" }]
|
|
2423
|
+
};
|
|
2424
|
+
services.saveResult?.(newItem);
|
|
2425
|
+
onResult?.(newItem);
|
|
2426
|
+
} finally {
|
|
2427
|
+
setIsGeneratingImage(false);
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
if (showPickerFor !== null) {
|
|
2431
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2432
|
+
LabImagePicker,
|
|
2433
|
+
{
|
|
2434
|
+
availableItems: services.availableItems,
|
|
2435
|
+
recentItems: services.recentItems,
|
|
2436
|
+
onSelect: (item, frame) => handleSelectImage(showPickerFor, item, frame),
|
|
2437
|
+
onClose: () => setShowPickerFor(null),
|
|
2438
|
+
title: `${autoLabel(showPickerFor)} w\xE4hlen`
|
|
2439
|
+
}
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2443
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder zum Vergleichen" }),
|
|
2444
|
+
selectedImages.map((img, i) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2445
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
|
|
2446
|
+
img ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2447
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0 cursor-pointer", onClick: () => setShowPickerFor(i) }),
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
|
|
2450
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("label", { className: "flex items-center gap-1 text-[10px] text-white/40", children: [
|
|
2451
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updateImg(i, { sendToPromptAI: e.target.checked }) }),
|
|
2452
|
+
"Bild an KI mitgeben"
|
|
2453
|
+
] }),
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2455
|
+
"input",
|
|
2456
|
+
{
|
|
2457
|
+
value: img.roleForPrompt || "",
|
|
2458
|
+
onChange: (e) => updateImg(i, { roleForPrompt: e.target.value }),
|
|
2459
|
+
placeholder: "Rolle z.B. Referenz A, Stilvergleich...",
|
|
2460
|
+
className: "bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2461
|
+
}
|
|
2462
|
+
)
|
|
2463
|
+
] })
|
|
2464
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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" }),
|
|
2465
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { onClick: () => removeSlot(i), className: "text-white/20 active:text-red-400 self-start", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "close" }) })
|
|
2466
|
+
] }, i)),
|
|
2467
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("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: [
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2469
|
+
"Bild hinzuf\xFCgen"
|
|
2470
|
+
] }),
|
|
2471
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
|
|
2472
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Analyse-Anweisung" }),
|
|
2473
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2474
|
+
"textarea",
|
|
2475
|
+
{
|
|
2476
|
+
value: instruction,
|
|
2477
|
+
onChange: (e) => setInstruction(e.target.value),
|
|
2478
|
+
placeholder: "z.B. Vergleiche Lichtstimmung und Komposition. Welches ist dramatischer?",
|
|
2479
|
+
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",
|
|
2480
|
+
rows: 3
|
|
2481
|
+
}
|
|
2482
|
+
)
|
|
2483
|
+
] }),
|
|
2484
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2485
|
+
"button",
|
|
2486
|
+
{
|
|
2487
|
+
onClick: handleAnalyze,
|
|
2488
|
+
disabled: selectedImages.filter(Boolean).length < 1 || !instruction.trim() || isAnalyzing,
|
|
2489
|
+
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",
|
|
2490
|
+
children: isAnalyzing ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Analysiere..." })
|
|
2493
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "compare" }),
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Analysieren" })
|
|
2496
|
+
] })
|
|
2497
|
+
}
|
|
2498
|
+
),
|
|
2499
|
+
analysis && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
|
|
2500
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Analyse" }),
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "bg-white/5 border border-white/10 rounded-xl px-3 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[12px] text-white/70 leading-relaxed whitespace-pre-wrap", children: analysis }) }),
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "mt-3", children: [
|
|
2503
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40 mb-2", children: "Als Prompt nutzen" }),
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2505
|
+
"textarea",
|
|
2506
|
+
{
|
|
2507
|
+
value: generatedPrompt,
|
|
2508
|
+
onChange: (e) => setGeneratedPrompt(e.target.value),
|
|
2509
|
+
placeholder: "Analyse-Ergebnis hier als Prompt einf\xFCgen oder eigenen Prompt schreiben...",
|
|
2510
|
+
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",
|
|
2511
|
+
rows: 3
|
|
2512
|
+
}
|
|
2513
|
+
),
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2515
|
+
"button",
|
|
2516
|
+
{
|
|
2517
|
+
onClick: handleGenerateImage,
|
|
2518
|
+
disabled: !generatedPrompt.trim() || isGeneratingImage,
|
|
2519
|
+
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",
|
|
2520
|
+
children: isGeneratingImage ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Generiere..." })
|
|
2523
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
2524
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: "Bild generieren" })
|
|
2526
|
+
] })
|
|
2527
|
+
}
|
|
2528
|
+
)
|
|
2529
|
+
] })
|
|
2530
|
+
] }),
|
|
2531
|
+
resultImage && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "rounded-xl overflow-hidden border border-white/10", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("img", { src: resultImage, className: "w-full object-cover" }) })
|
|
2532
|
+
] });
|
|
2533
|
+
};
|
|
2534
|
+
|
|
2535
|
+
// src/components/labs/LabLoop.tsx
|
|
2536
|
+
var import_react18 = require("react");
|
|
2537
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2538
|
+
var LabLoop = ({ services, onResult }) => {
|
|
2539
|
+
const [rounds, setRounds] = (0, import_react18.useState)([]);
|
|
2540
|
+
const [currentInstruction, setCurrentInstruction] = (0, import_react18.useState)("");
|
|
2541
|
+
const [showPickerForRound, setShowPickerForRound] = (0, import_react18.useState)(null);
|
|
2542
|
+
const [pendingImages, setPendingImages] = (0, import_react18.useState)([]);
|
|
2543
|
+
const [isGenerating, setIsGenerating] = (0, import_react18.useState)(false);
|
|
2544
|
+
const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
|
|
2545
|
+
const handleAddImage = (item, frame) => {
|
|
2546
|
+
services.onItemUsed(item);
|
|
2547
|
+
const label = autoLabel(pendingImages.length);
|
|
2548
|
+
setPendingImages((prev) => [...prev, {
|
|
2549
|
+
item,
|
|
2550
|
+
frame,
|
|
2551
|
+
label,
|
|
2552
|
+
sendToPromptAI: true,
|
|
2553
|
+
sendToImageGen: false,
|
|
2554
|
+
roleForPrompt: "",
|
|
2555
|
+
roleForImage: ""
|
|
2556
|
+
}]);
|
|
2557
|
+
setShowPickerForRound(null);
|
|
2558
|
+
};
|
|
2559
|
+
const updatePendingImage = (i, updates) => setPendingImages((prev) => prev.map((img, idx) => idx === i ? { ...img, ...updates } : img));
|
|
2560
|
+
const handleRunRound = async () => {
|
|
2561
|
+
if (!currentInstruction.trim()) return;
|
|
2562
|
+
setIsGenerating(true);
|
|
2563
|
+
try {
|
|
2564
|
+
const historyForBuilder = rounds.map((r) => ({
|
|
2565
|
+
prompt: r.prompt,
|
|
2566
|
+
instruction: r.instruction,
|
|
2567
|
+
images: r.images
|
|
2568
|
+
}));
|
|
2569
|
+
const { text, systemInstruction } = buildLoopInstruction(historyForBuilder, currentInstruction);
|
|
2570
|
+
const allImages = [...rounds.flatMap((r) => r.images), ...pendingImages].filter((i) => i.sendToPromptAI).map((i) => ({ base64: i.frame.base64, mimeType: "image/png" }));
|
|
2571
|
+
const newPrompt = await services.generateText(text, {
|
|
2572
|
+
systemInstruction,
|
|
2573
|
+
images: allImages.length ? allImages : void 0
|
|
2574
|
+
});
|
|
2575
|
+
const { base64, mediaId } = await services.generateImage({
|
|
2576
|
+
prompt: newPrompt,
|
|
2577
|
+
aspectRatio: "1:1",
|
|
2578
|
+
modelDisplayName: "\u{1F34C} Nano Banana Pro"
|
|
2579
|
+
});
|
|
2580
|
+
const newBase64 = `data:image/png;base64,${base64}`;
|
|
2581
|
+
const newFrame = { id: crypto.randomUUID(), base64: newBase64, mediaId, source: "generated" };
|
|
2582
|
+
const newRound = {
|
|
2583
|
+
prompt: newPrompt,
|
|
2584
|
+
instruction: currentInstruction,
|
|
2585
|
+
frame: newFrame,
|
|
2586
|
+
images: [...pendingImages]
|
|
2587
|
+
};
|
|
2588
|
+
setRounds((prev) => [...prev, newRound]);
|
|
2589
|
+
setCurrentInstruction("");
|
|
2590
|
+
setPendingImages([]);
|
|
2591
|
+
const newItem = {
|
|
2592
|
+
id: newFrame.id,
|
|
2593
|
+
prompt: newPrompt,
|
|
2594
|
+
tags: [],
|
|
2595
|
+
frames: [newFrame]
|
|
2596
|
+
};
|
|
2597
|
+
services.saveResult?.(newItem);
|
|
2598
|
+
onResult?.(newItem);
|
|
2599
|
+
} finally {
|
|
2600
|
+
setIsGenerating(false);
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
if (showPickerForRound !== null) {
|
|
2604
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2605
|
+
LabImagePicker,
|
|
2606
|
+
{
|
|
2607
|
+
availableItems: services.availableItems,
|
|
2608
|
+
recentItems: services.recentItems,
|
|
2609
|
+
onSelect: handleAddImage,
|
|
2610
|
+
onClose: () => setShowPickerForRound(null),
|
|
2611
|
+
title: "Referenzbild hinzuf\xFCgen"
|
|
2612
|
+
}
|
|
2613
|
+
);
|
|
2614
|
+
}
|
|
2615
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col gap-3 p-4 overflow-y-auto dark-scrollbar", children: [
|
|
2616
|
+
rounds.map((round, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
|
|
2617
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex flex-col items-center gap-1 shrink-0", children: [
|
|
2618
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "text-[9px] font-bold text-white/30", children: [
|
|
2619
|
+
"R",
|
|
2620
|
+
i + 1
|
|
2621
|
+
] }),
|
|
2622
|
+
round.frame && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { src: round.frame.base64, className: "w-12 h-12 object-cover rounded-lg" })
|
|
2623
|
+
] }),
|
|
2624
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
2625
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-[9px] text-white/30 italic mb-1", children: [
|
|
2626
|
+
'"',
|
|
2627
|
+
round.instruction,
|
|
2628
|
+
'"'
|
|
2629
|
+
] }),
|
|
2630
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("p", { className: "text-[10px] text-white/60 leading-tight line-clamp-3", children: round.prompt })
|
|
2631
|
+
] })
|
|
2632
|
+
] }, i)),
|
|
2633
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "rounded-xl border border-white/20 bg-white/5 p-3 flex flex-col gap-3", children: [
|
|
2634
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("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}` }),
|
|
2635
|
+
currentPrompt && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "text-[10px] text-white/40 italic line-clamp-2", children: [
|
|
2636
|
+
'Letzter Prompt: "',
|
|
2637
|
+
currentPrompt,
|
|
2638
|
+
'"'
|
|
2639
|
+
] }),
|
|
2640
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2641
|
+
"textarea",
|
|
2642
|
+
{
|
|
2643
|
+
value: currentInstruction,
|
|
2644
|
+
onChange: (e) => setCurrentInstruction(e.target.value),
|
|
2645
|
+
placeholder: rounds.length === 0 ? "Ersten Prompt beschreiben..." : "Was soll verbessert werden?",
|
|
2646
|
+
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",
|
|
2647
|
+
rows: 3
|
|
2648
|
+
}
|
|
2649
|
+
),
|
|
2650
|
+
pendingImages.map((img, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex gap-2 items-center", children: [
|
|
2651
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { src: img.frame.base64, className: "w-8 h-8 object-cover rounded" }),
|
|
2652
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-[10px] text-white/40", children: img.label }),
|
|
2653
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2654
|
+
"input",
|
|
2655
|
+
{
|
|
2656
|
+
value: img.roleForPrompt || "",
|
|
2657
|
+
onChange: (e) => updatePendingImage(i, { roleForPrompt: e.target.value }),
|
|
2658
|
+
placeholder: "Rolle...",
|
|
2659
|
+
className: "flex-1 bg-black/30 border border-white/10 rounded px-2 py-0.5 text-[10px] text-white/60 outline-none"
|
|
2660
|
+
}
|
|
2661
|
+
),
|
|
2662
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("label", { className: "flex items-center gap-1 text-[9px] text-white/30", children: [
|
|
2663
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { type: "checkbox", checked: img.sendToPromptAI, onChange: (e) => updatePendingImage(i, { sendToPromptAI: e.target.checked }) }),
|
|
2664
|
+
"KI"
|
|
2665
|
+
] }),
|
|
2666
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { onClick: () => setPendingImages((prev) => prev.filter((_, idx) => idx !== i)), className: "text-white/20 active:text-red-400", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
2667
|
+
] }, i)),
|
|
2668
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
2669
|
+
"button",
|
|
2670
|
+
{
|
|
2671
|
+
onClick: () => setShowPickerForRound(rounds.length),
|
|
2672
|
+
className: "text-[10px] text-white/30 active:text-white/60 flex items-center gap-1 self-start",
|
|
2673
|
+
children: [
|
|
2674
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
|
|
2675
|
+
"Referenzbild hinzuf\xFCgen"
|
|
2676
|
+
]
|
|
2677
|
+
}
|
|
2678
|
+
),
|
|
2679
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
2680
|
+
"button",
|
|
2681
|
+
{
|
|
2682
|
+
onClick: handleRunRound,
|
|
2683
|
+
disabled: !currentInstruction.trim() || isGenerating,
|
|
2684
|
+
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",
|
|
2685
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
2686
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
|
|
2687
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Runde l\xE4uft..." })
|
|
2688
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
2689
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "loop" }),
|
|
2690
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Runde starten" })
|
|
2691
|
+
] })
|
|
2692
|
+
}
|
|
2693
|
+
)
|
|
2694
|
+
] })
|
|
2695
|
+
] });
|
|
2696
|
+
};
|
|
2697
|
+
|
|
2698
|
+
// src/components/labs/LabsTab.tsx
|
|
2699
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2700
|
+
var TABS = [
|
|
2701
|
+
{ key: "remix", label: "Remix", icon: "auto_fix_high" },
|
|
2702
|
+
{ key: "blend", label: "Blend", icon: "merge" },
|
|
2703
|
+
{ key: "compare", label: "Compare", icon: "compare" },
|
|
2704
|
+
{ key: "loop", label: "Loop", icon: "loop" }
|
|
2705
|
+
];
|
|
2706
|
+
var LabsTab = ({ services, onResult }) => {
|
|
2707
|
+
const [activeTab, setActiveTab] = (0, import_react19.useState)("remix");
|
|
2708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col h-full overflow-hidden", children: [
|
|
2709
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex border-b border-white/5 shrink-0", children: TABS.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
2710
|
+
"button",
|
|
2711
|
+
{
|
|
2712
|
+
onClick: () => setActiveTab(tab.key),
|
|
2713
|
+
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"}`,
|
|
2714
|
+
children: [
|
|
2715
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: tab.icon }),
|
|
2716
|
+
tab.label
|
|
2717
|
+
]
|
|
2718
|
+
},
|
|
2719
|
+
tab.key
|
|
2720
|
+
)) }),
|
|
2721
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex-1 overflow-hidden", children: [
|
|
2722
|
+
activeTab === "remix" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LabRemix, { services, onResult }),
|
|
2723
|
+
activeTab === "blend" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LabBlend, { services, onResult }),
|
|
2724
|
+
activeTab === "compare" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LabCompare, { services, onResult }),
|
|
2725
|
+
activeTab === "loop" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LabLoop, { services, onResult })
|
|
2726
|
+
] })
|
|
2727
|
+
] });
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2730
|
+
// src/components/TagManagerPanel.tsx
|
|
2731
|
+
var import_react20 = require("react");
|
|
2732
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2733
|
+
function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
|
|
2734
|
+
const categories = Object.keys(workspaceTags.by_category).filter(
|
|
2735
|
+
(cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
|
|
2736
|
+
);
|
|
2737
|
+
const [selectedCategory, setSelectedCategory] = (0, import_react20.useState)(categories[0] || "");
|
|
2738
|
+
const effectiveCategory = categories.includes(selectedCategory) ? selectedCategory : categories[0] || "";
|
|
2739
|
+
const [editingLabel, setEditingLabel] = (0, import_react20.useState)(null);
|
|
2740
|
+
const [editState, setEditState] = (0, import_react20.useState)({ label: "", value: "" });
|
|
2741
|
+
const [newTag, setNewTag] = (0, import_react20.useState)({ label: "", value: "" });
|
|
2742
|
+
const [movingLabel, setMovingLabel] = (0, import_react20.useState)(null);
|
|
2743
|
+
const [moveTarget, setMoveTarget] = (0, import_react20.useState)("");
|
|
2744
|
+
const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
|
|
2745
|
+
const otherCategories = categories.filter((c) => c !== effectiveCategory);
|
|
2746
|
+
const startEdit = (tag) => {
|
|
2747
|
+
setEditingLabel(tag.label);
|
|
2748
|
+
setEditState({ label: tag.label, value: tag.value });
|
|
2749
|
+
setMovingLabel(null);
|
|
2750
|
+
};
|
|
2751
|
+
const saveEdit = (originalLabel) => {
|
|
2752
|
+
if (!editState.label.trim()) return;
|
|
2753
|
+
onTagUpdate(originalLabel, effectiveCategory, {
|
|
2754
|
+
label: editState.label.trim(),
|
|
2755
|
+
value: editState.value.trim() || editState.label.trim()
|
|
2756
|
+
});
|
|
2757
|
+
setEditingLabel(null);
|
|
2758
|
+
};
|
|
2759
|
+
const handleMoveUp = (index) => {
|
|
2760
|
+
if (index === 0) return;
|
|
2761
|
+
const reordered = [...tags];
|
|
2762
|
+
[reordered[index - 1], reordered[index]] = [reordered[index], reordered[index - 1]];
|
|
2763
|
+
onTagReorder(effectiveCategory, reordered);
|
|
2764
|
+
};
|
|
2765
|
+
const handleMoveDown = (index) => {
|
|
2766
|
+
if (index === tags.length - 1) return;
|
|
2767
|
+
const reordered = [...tags];
|
|
2768
|
+
[reordered[index], reordered[index + 1]] = [reordered[index + 1], reordered[index]];
|
|
2769
|
+
onTagReorder(effectiveCategory, reordered);
|
|
2770
|
+
};
|
|
2771
|
+
const handleMove = (tag) => {
|
|
2772
|
+
if (!moveTarget) return;
|
|
2773
|
+
onTagMove(tag.label, tag.value, effectiveCategory, moveTarget);
|
|
2774
|
+
setMovingLabel(null);
|
|
2775
|
+
setMoveTarget("");
|
|
2776
|
+
};
|
|
2777
|
+
const handleCreate = () => {
|
|
2778
|
+
if (!newTag.label.trim() || !effectiveCategory) return;
|
|
2779
|
+
onTagCreate({ label: newTag.label.trim(), value: newTag.value.trim() || newTag.label.trim(), category: effectiveCategory, is_user_created: true });
|
|
2780
|
+
setNewTag({ label: "", value: "" });
|
|
2781
|
+
};
|
|
2782
|
+
const handleDelete = (tag) => {
|
|
2783
|
+
if (!confirm(`Tag "${tag.label}" l\xF6schen?`)) return;
|
|
2784
|
+
onTagDelete(tag.label, effectiveCategory);
|
|
2785
|
+
};
|
|
2786
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col h-full overflow-hidden", children: [
|
|
2787
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "px-3 py-2 border-b border-white/5 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-widest text-white/40", children: "Tag Manager" }) }),
|
|
2788
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "px-3 py-2 shrink-0 overflow-x-auto", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1.5 flex-nowrap", children: [
|
|
2789
|
+
categories.map((cat) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
2790
|
+
"button",
|
|
2791
|
+
{
|
|
2792
|
+
onClick: () => {
|
|
2793
|
+
setSelectedCategory(cat);
|
|
2794
|
+
setEditingLabel(null);
|
|
2795
|
+
setMovingLabel(null);
|
|
2796
|
+
},
|
|
2797
|
+
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"}`,
|
|
2798
|
+
children: [
|
|
2799
|
+
cat,
|
|
2800
|
+
" ",
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-1 opacity-50", children: (workspaceTags.by_category[cat] || []).filter((t) => !t.is_deleted).length })
|
|
2802
|
+
]
|
|
2803
|
+
},
|
|
2804
|
+
cat
|
|
2805
|
+
)),
|
|
2806
|
+
categories.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-[10px] text-white/20", children: "Erst Workspace importieren" })
|
|
2807
|
+
] }) }),
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 pb-2 space-y-1", children: [
|
|
2809
|
+
tags.map((tag, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { children: [
|
|
2810
|
+
editingLabel === tag.label ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "bg-white/5 border border-blue-600/40 rounded-lg p-2.5 space-y-1.5", children: [
|
|
2811
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2812
|
+
"input",
|
|
2813
|
+
{
|
|
2814
|
+
value: editState.label,
|
|
2815
|
+
onChange: (e) => setEditState((s) => ({ ...s, label: e.target.value })),
|
|
2816
|
+
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",
|
|
2817
|
+
placeholder: "Label",
|
|
2818
|
+
autoFocus: true,
|
|
2819
|
+
onKeyDown: (e) => e.key === "Enter" && saveEdit(tag.label)
|
|
2820
|
+
}
|
|
2821
|
+
),
|
|
2822
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2823
|
+
"textarea",
|
|
2824
|
+
{
|
|
2825
|
+
value: editState.value,
|
|
2826
|
+
onChange: (e) => setEditState((s) => ({ ...s, value: e.target.value })),
|
|
2827
|
+
rows: 2,
|
|
2828
|
+
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",
|
|
2829
|
+
placeholder: "Prompt-Wert (leer = Label)"
|
|
2830
|
+
}
|
|
2831
|
+
),
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1.5 justify-end", children: [
|
|
2833
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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" }),
|
|
2834
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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" })
|
|
2835
|
+
] })
|
|
2836
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("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: [
|
|
2837
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-0 shrink-0", children: [
|
|
2838
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { onClick: () => handleMoveUp(i), disabled: i === 0, className: "text-white/20 hover:text-white/60 disabled:opacity-10 transition leading-none", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "arrow_drop_up" }) }),
|
|
2839
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "arrow_drop_down" }) })
|
|
2840
|
+
] }),
|
|
2841
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
2842
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-[12px] text-white/80 font-medium truncate", children: tag.label }),
|
|
2843
|
+
tag.value && tag.value !== tag.label && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "text-[10px] text-white/30 truncate", children: [
|
|
2844
|
+
tag.value.slice(0, 60),
|
|
2845
|
+
tag.value.length > 60 ? "\u2026" : ""
|
|
2846
|
+
] })
|
|
2847
|
+
] }),
|
|
2848
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1 opacity-0 group-hover:opacity-100 transition shrink-0", children: [
|
|
2849
|
+
otherCategories.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2850
|
+
"button",
|
|
2851
|
+
{
|
|
2852
|
+
onClick: () => {
|
|
2853
|
+
setMovingLabel((l) => l === tag.label ? null : tag.label);
|
|
2854
|
+
setMoveTarget("");
|
|
2855
|
+
setEditingLabel(null);
|
|
2856
|
+
},
|
|
2857
|
+
className: "p-1 rounded text-white/30 hover:text-purple-400 transition",
|
|
2858
|
+
title: "Kategorie wechseln",
|
|
2859
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "drive_file_move" })
|
|
2860
|
+
}
|
|
2861
|
+
),
|
|
2862
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { onClick: () => startEdit(tag), className: "p-1 rounded text-white/30 hover:text-blue-400 transition", title: "Bearbeiten", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "edit" }) }),
|
|
2863
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { onClick: () => handleDelete(tag), className: "p-1 rounded text-white/30 hover:text-red-400 transition", title: "L\xF6schen", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "delete" }) })
|
|
2864
|
+
] })
|
|
2865
|
+
] }),
|
|
2866
|
+
movingLabel === tag.label && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "mt-1 bg-purple-900/20 border border-purple-700/30 rounded-lg p-2.5 space-y-1.5", children: [
|
|
2867
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-[9px] font-bold uppercase tracking-widest text-purple-400/70", children: "Verschieben nach Kategorie" }),
|
|
2868
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
2869
|
+
"select",
|
|
2870
|
+
{
|
|
2871
|
+
value: moveTarget,
|
|
2872
|
+
onChange: (e) => setMoveTarget(e.target.value),
|
|
2873
|
+
className: "w-full bg-black/40 border border-white/10 rounded px-2 py-1 text-[11px] text-white/70 outline-none",
|
|
2874
|
+
children: [
|
|
2875
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: "", children: "\u2014 Kategorie w\xE4hlen \u2014" }),
|
|
2876
|
+
otherCategories.map((cat) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("option", { value: cat, children: cat }, cat))
|
|
2877
|
+
]
|
|
2878
|
+
}
|
|
2879
|
+
),
|
|
2880
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex gap-1.5 justify-end", children: [
|
|
2881
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2882
|
+
"button",
|
|
2883
|
+
{
|
|
2884
|
+
onClick: () => handleMove(tag),
|
|
2885
|
+
disabled: !moveTarget,
|
|
2886
|
+
className: "px-2.5 py-1 bg-purple-700 hover:bg-purple-600 text-white text-[10px] font-bold rounded transition disabled:opacity-40",
|
|
2887
|
+
children: "VERSCHIEBEN"
|
|
2888
|
+
}
|
|
2889
|
+
),
|
|
2890
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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" })
|
|
2891
|
+
] })
|
|
2892
|
+
] })
|
|
2893
|
+
] }, `${effectiveCategory}-${i}`)),
|
|
2894
|
+
tags.length === 0 && effectiveCategory && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "text-center text-[11px] text-white/20 py-6", children: "Keine Tags in dieser Kategorie." })
|
|
2895
|
+
] }),
|
|
2896
|
+
effectiveCategory && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "px-3 py-2 border-t border-white/5 shrink-0 space-y-1.5", children: [
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30", children: [
|
|
2898
|
+
"Neuer Tag in \u201E",
|
|
2899
|
+
effectiveCategory,
|
|
2900
|
+
'"'
|
|
2901
|
+
] }),
|
|
2902
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2903
|
+
"input",
|
|
2904
|
+
{
|
|
2905
|
+
value: newTag.label,
|
|
2906
|
+
onChange: (e) => setNewTag((s) => ({ ...s, label: e.target.value })),
|
|
2907
|
+
onKeyDown: (e) => e.key === "Enter" && !e.shiftKey && handleCreate(),
|
|
2908
|
+
placeholder: "Label\u2026",
|
|
2909
|
+
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"
|
|
2910
|
+
}
|
|
2911
|
+
),
|
|
2912
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
2913
|
+
"textarea",
|
|
2914
|
+
{
|
|
2915
|
+
value: newTag.value,
|
|
2916
|
+
onChange: (e) => setNewTag((s) => ({ ...s, value: e.target.value })),
|
|
2917
|
+
rows: 2,
|
|
2918
|
+
placeholder: "Prompt-Wert (leer = Label)",
|
|
2919
|
+
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"
|
|
2920
|
+
}
|
|
2921
|
+
),
|
|
2922
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
2923
|
+
"button",
|
|
2924
|
+
{
|
|
2925
|
+
onClick: handleCreate,
|
|
2926
|
+
disabled: !newTag.label.trim(),
|
|
2927
|
+
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",
|
|
2928
|
+
children: [
|
|
2929
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add" }),
|
|
2930
|
+
"TAG ERSTELLEN"
|
|
2931
|
+
]
|
|
2932
|
+
}
|
|
2933
|
+
)
|
|
2934
|
+
] })
|
|
2935
|
+
] });
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
// src/components/AvatarArchitectApp.tsx
|
|
2939
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1675
2940
|
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
|
|
1676
|
-
(0,
|
|
2941
|
+
(0, import_react21.useEffect)(() => {
|
|
1677
2942
|
const id = "flow-styles";
|
|
1678
2943
|
if (!document.getElementById(id)) {
|
|
1679
2944
|
const style = document.createElement("style");
|
|
@@ -1682,16 +2947,16 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1682
2947
|
document.head.appendChild(style);
|
|
1683
2948
|
}
|
|
1684
2949
|
}, []);
|
|
1685
|
-
const [showStart, setShowStart] = (0,
|
|
1686
|
-
const [layoutChoice, setLayoutChoice] = (0,
|
|
2950
|
+
const [showStart, setShowStart] = (0, import_react21.useState)(true);
|
|
2951
|
+
const [layoutChoice, setLayoutChoice] = (0, import_react21.useState)(() => {
|
|
1687
2952
|
try {
|
|
1688
2953
|
return localStorage.getItem("aa-layout") || null;
|
|
1689
2954
|
} catch {
|
|
1690
2955
|
return null;
|
|
1691
2956
|
}
|
|
1692
2957
|
});
|
|
1693
|
-
const [projectLoaded, setProjectLoaded] = (0,
|
|
1694
|
-
const wsInputRef = (0,
|
|
2958
|
+
const [projectLoaded, setProjectLoaded] = (0, import_react21.useState)(false);
|
|
2959
|
+
const wsInputRef = (0, import_react21.useRef)(null);
|
|
1695
2960
|
const startApp = (choice) => {
|
|
1696
2961
|
try {
|
|
1697
2962
|
localStorage.setItem("aa-layout", choice);
|
|
@@ -1700,64 +2965,66 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1700
2965
|
setLayoutChoice(choice);
|
|
1701
2966
|
setShowStart(false);
|
|
1702
2967
|
};
|
|
1703
|
-
const [nodes, setNodes] = (0,
|
|
1704
|
-
const [edges, setEdges] = (0,
|
|
1705
|
-
const [history, setHistory] = (0,
|
|
1706
|
-
const [galleryItems, setGalleryItems] = (0,
|
|
1707
|
-
const [activePrompt, setActivePrompt] = (0,
|
|
1708
|
-
const [isSynthesizing, setIsSynthesizing] = (0,
|
|
1709
|
-
const [activeGenerationsCount, setActiveGenerationsCount] = (0,
|
|
1710
|
-
const [currentResult, setCurrentResult] = (0,
|
|
1711
|
-
const [focusedNodeId, setFocusedNodeId] = (0,
|
|
1712
|
-
const [leftTab, setLeftTab] = (0,
|
|
1713
|
-
const [promptFeedback, setPromptFeedback] = (0,
|
|
1714
|
-
const [lastPromptPayload, setLastPromptPayload] = (0,
|
|
1715
|
-
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0,
|
|
1716
|
-
const [activeTab, setActiveTab] = (0,
|
|
1717
|
-
const [mobileTab, setMobileTab] = (0,
|
|
1718
|
-
const [
|
|
1719
|
-
const [
|
|
1720
|
-
const [
|
|
1721
|
-
const [
|
|
1722
|
-
const [
|
|
1723
|
-
const [
|
|
1724
|
-
const [
|
|
2968
|
+
const [nodes, setNodes] = (0, import_react21.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
|
|
2969
|
+
const [edges, setEdges] = (0, import_react21.useState)([]);
|
|
2970
|
+
const [history, setHistory] = (0, import_react21.useState)([]);
|
|
2971
|
+
const [galleryItems, setGalleryItems] = (0, import_react21.useState)([]);
|
|
2972
|
+
const [activePrompt, setActivePrompt] = (0, import_react21.useState)("");
|
|
2973
|
+
const [isSynthesizing, setIsSynthesizing] = (0, import_react21.useState)(false);
|
|
2974
|
+
const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react21.useState)(0);
|
|
2975
|
+
const [currentResult, setCurrentResult] = (0, import_react21.useState)(null);
|
|
2976
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react21.useState)(null);
|
|
2977
|
+
const [leftTab, setLeftTab] = (0, import_react21.useState)("prompt");
|
|
2978
|
+
const [promptFeedback, setPromptFeedback] = (0, import_react21.useState)(null);
|
|
2979
|
+
const [lastPromptPayload, setLastPromptPayload] = (0, import_react21.useState)(null);
|
|
2980
|
+
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react21.useState)(false);
|
|
2981
|
+
const [activeTab, setActiveTab] = (0, import_react21.useState)("history");
|
|
2982
|
+
const [mobileTab, setMobileTab] = (0, import_react21.useState)("stage");
|
|
2983
|
+
const [middlePanel, setMiddlePanel] = (0, import_react21.useState)("stage");
|
|
2984
|
+
const [recentLabItems, setRecentLabItems] = (0, import_react21.useState)([]);
|
|
2985
|
+
const [aspectRatio, setAspectRatio] = (0, import_react21.useState)("1:1");
|
|
2986
|
+
const [selectedModel, setSelectedModel] = (0, import_react21.useState)("\u{1F34C} Nano Banana Pro");
|
|
2987
|
+
const [seed, setSeed] = (0, import_react21.useState)(Math.floor(Math.random() * 1e6));
|
|
2988
|
+
const [seedMode, setSeedMode] = (0, import_react21.useState)("random");
|
|
2989
|
+
const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react21.useState)(false);
|
|
2990
|
+
const [isRightCollapsed, setIsRightCollapsed] = (0, import_react21.useState)(false);
|
|
2991
|
+
const [leftPanelWidth, setLeftPanelWidth] = (0, import_react21.useState)(() => {
|
|
1725
2992
|
try {
|
|
1726
2993
|
return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
|
|
1727
2994
|
} catch {
|
|
1728
2995
|
return 260;
|
|
1729
2996
|
}
|
|
1730
2997
|
});
|
|
1731
|
-
const [rightPanelWidth, setRightPanelWidth] = (0,
|
|
2998
|
+
const [rightPanelWidth, setRightPanelWidth] = (0, import_react21.useState)(() => {
|
|
1732
2999
|
try {
|
|
1733
3000
|
return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
|
|
1734
3001
|
} catch {
|
|
1735
3002
|
return 320;
|
|
1736
3003
|
}
|
|
1737
3004
|
});
|
|
1738
|
-
const [isPromptCollapsed, setIsPromptCollapsed] = (0,
|
|
1739
|
-
const [projectActionState, setProjectActionState] = (0,
|
|
1740
|
-
const syncServerDataRef = (0,
|
|
1741
|
-
const [workspaceTags, setWorkspaceTags] = (0,
|
|
1742
|
-
const [serverProjects, setServerProjects] = (0,
|
|
1743
|
-
const [isLoadingFromServer, setIsLoadingFromServer] = (0,
|
|
1744
|
-
const [highContrast, setHighContrast] = (0,
|
|
3005
|
+
const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react21.useState)(false);
|
|
3006
|
+
const [projectActionState, setProjectActionState] = (0, import_react21.useState)("idle");
|
|
3007
|
+
const syncServerDataRef = (0, import_react21.useRef)(null);
|
|
3008
|
+
const [workspaceTags, setWorkspaceTags] = (0, import_react21.useState)(null);
|
|
3009
|
+
const [serverProjects, setServerProjects] = (0, import_react21.useState)([]);
|
|
3010
|
+
const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react21.useState)(false);
|
|
3011
|
+
const [highContrast, setHighContrast] = (0, import_react21.useState)(() => {
|
|
1745
3012
|
try {
|
|
1746
3013
|
return localStorage.getItem("aa-contrast") === "high";
|
|
1747
3014
|
} catch {
|
|
1748
3015
|
return false;
|
|
1749
3016
|
}
|
|
1750
3017
|
});
|
|
1751
|
-
const [activeReferenceId, setActiveReferenceId] = (0,
|
|
1752
|
-
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0,
|
|
1753
|
-
const [isScanningImage, setIsScanningImage] = (0,
|
|
1754
|
-
const [touchStartX, setTouchStartX] = (0,
|
|
1755
|
-
const [isFullscreen, setIsFullscreen] = (0,
|
|
1756
|
-
const [zoomScale, setZoomScale] = (0,
|
|
1757
|
-
const [zoomOffset, setZoomOffset] = (0,
|
|
1758
|
-
const lastPinchDist = (0,
|
|
1759
|
-
const lastTapTime = (0,
|
|
1760
|
-
const dragStart = (0,
|
|
3018
|
+
const [activeReferenceId, setActiveReferenceId] = (0, import_react21.useState)(null);
|
|
3019
|
+
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react21.useState)(null);
|
|
3020
|
+
const [isScanningImage, setIsScanningImage] = (0, import_react21.useState)(false);
|
|
3021
|
+
const [touchStartX, setTouchStartX] = (0, import_react21.useState)(null);
|
|
3022
|
+
const [isFullscreen, setIsFullscreen] = (0, import_react21.useState)(false);
|
|
3023
|
+
const [zoomScale, setZoomScale] = (0, import_react21.useState)(1);
|
|
3024
|
+
const [zoomOffset, setZoomOffset] = (0, import_react21.useState)({ x: 0, y: 0 });
|
|
3025
|
+
const lastPinchDist = (0, import_react21.useRef)(null);
|
|
3026
|
+
const lastTapTime = (0, import_react21.useRef)(0);
|
|
3027
|
+
const dragStart = (0, import_react21.useRef)(null);
|
|
1761
3028
|
const openFullscreen = () => {
|
|
1762
3029
|
setIsFullscreen(true);
|
|
1763
3030
|
setZoomScale(1);
|
|
@@ -1820,6 +3087,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1820
3087
|
setActiveReferenceId(null);
|
|
1821
3088
|
setActiveReferenceThumbnail(null);
|
|
1822
3089
|
};
|
|
3090
|
+
const labServices = (0, import_react21.useMemo)(() => {
|
|
3091
|
+
const available = groupGenerationsToLabItems([...galleryItems, ...history]);
|
|
3092
|
+
return {
|
|
3093
|
+
availableItems: available,
|
|
3094
|
+
recentItems: recentLabItems,
|
|
3095
|
+
onItemUsed: (item) => setRecentLabItems(
|
|
3096
|
+
(prev) => [item, ...prev.filter((i) => i.id !== item.id)].slice(0, 10)
|
|
3097
|
+
),
|
|
3098
|
+
generateText: onGeneratePrompt,
|
|
3099
|
+
generateImage: onGenerateImage,
|
|
3100
|
+
saveResult: async (item) => {
|
|
3101
|
+
const frame = item.frames[0];
|
|
3102
|
+
if (frame?.base64) {
|
|
3103
|
+
const gen = frameToGeneration(frame, item);
|
|
3104
|
+
setGalleryItems((prev) => [gen, ...prev]);
|
|
3105
|
+
setHistory((prev) => [gen, ...prev]);
|
|
3106
|
+
}
|
|
3107
|
+
},
|
|
3108
|
+
workspaceTags: workspaceTags || void 0
|
|
3109
|
+
};
|
|
3110
|
+
}, [galleryItems, history, recentLabItems, onGeneratePrompt, onGenerateImage, workspaceTags]);
|
|
1823
3111
|
const startLeftResize = (e) => {
|
|
1824
3112
|
e.preventDefault();
|
|
1825
3113
|
const startX = e.clientX;
|
|
@@ -1867,7 +3155,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1867
3155
|
reader.onerror = reject;
|
|
1868
3156
|
reader.readAsDataURL(file);
|
|
1869
3157
|
});
|
|
1870
|
-
const
|
|
3158
|
+
const { text, systemInstruction } = buildScanInstruction();
|
|
3159
|
+
const prompt = await onGeneratePrompt(text, {
|
|
3160
|
+
images: [{ base64, mimeType: file.type || "image/png" }],
|
|
3161
|
+
systemInstruction
|
|
3162
|
+
});
|
|
1871
3163
|
setActivePrompt(prompt);
|
|
1872
3164
|
} catch (err) {
|
|
1873
3165
|
console.error("Scan fehlgeschlagen:", err);
|
|
@@ -1875,17 +3167,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1875
3167
|
setIsScanningImage(false);
|
|
1876
3168
|
}
|
|
1877
3169
|
};
|
|
1878
|
-
const currentIndex = (0,
|
|
1879
|
-
const goToPrev = (0,
|
|
3170
|
+
const currentIndex = (0, import_react21.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
|
|
3171
|
+
const goToPrev = (0, import_react21.useCallback)(() => {
|
|
1880
3172
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
1881
3173
|
}, [currentIndex, history]);
|
|
1882
|
-
const goToNext = (0,
|
|
3174
|
+
const goToNext = (0, import_react21.useCallback)(() => {
|
|
1883
3175
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
1884
3176
|
}, [currentIndex, history]);
|
|
1885
3177
|
const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
|
|
1886
3178
|
const isGenerating = activeGenerationsCount > 0;
|
|
1887
3179
|
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
1888
|
-
const getSubtreeFormat = (0,
|
|
3180
|
+
const getSubtreeFormat = (0, import_react21.useCallback)((nodeId, depth = 0) => {
|
|
1889
3181
|
const node = nodes.find((n) => n.id === nodeId);
|
|
1890
3182
|
if (!node) return "";
|
|
1891
3183
|
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
@@ -1893,7 +3185,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1893
3185
|
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
1894
3186
|
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
1895
3187
|
}, [nodes, edges]);
|
|
1896
|
-
const activePath = (0,
|
|
3188
|
+
const activePath = (0, import_react21.useMemo)(() => {
|
|
1897
3189
|
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
1898
3190
|
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
1899
3191
|
let currId = focusedNodeId;
|
|
@@ -1983,11 +3275,32 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1983
3275
|
return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
|
|
1984
3276
|
});
|
|
1985
3277
|
};
|
|
3278
|
+
const handleTagReorder = (category, reorderedTags) => {
|
|
3279
|
+
setWorkspaceTags((prev) => {
|
|
3280
|
+
if (!prev) return prev;
|
|
3281
|
+
const deletedTags = (prev.by_category[category] || []).filter((t) => t.is_deleted);
|
|
3282
|
+
const merged = [...reorderedTags, ...deletedTags];
|
|
3283
|
+
const newAll = [
|
|
3284
|
+
...prev.all.filter((t) => t.category !== category),
|
|
3285
|
+
...merged.map((t) => ({ ...t, category }))
|
|
3286
|
+
];
|
|
3287
|
+
return { by_category: { ...prev.by_category, [category]: merged }, all: newAll };
|
|
3288
|
+
});
|
|
3289
|
+
};
|
|
3290
|
+
const handleTagMove = (label, value, fromCategory, toCategory) => {
|
|
3291
|
+
setWorkspaceTags((prev) => {
|
|
3292
|
+
if (!prev) return prev;
|
|
3293
|
+
const fromTags = (prev.by_category[fromCategory] || []).filter((t) => t.label !== label);
|
|
3294
|
+
const toTags = [...prev.by_category[toCategory] || [], { label, value, is_user_created: true }];
|
|
3295
|
+
const newAll = prev.all.filter((t) => !(t.label === label && t.category === fromCategory)).concat({ label, value, is_user_created: true, category: toCategory });
|
|
3296
|
+
return { by_category: { ...prev.by_category, [fromCategory]: fromTags, [toCategory]: toTags }, all: newAll };
|
|
3297
|
+
});
|
|
3298
|
+
};
|
|
1986
3299
|
const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
|
|
1987
3300
|
setIsPromptTabGenerating(true);
|
|
1988
3301
|
setPromptFeedback(null);
|
|
1989
3302
|
try {
|
|
1990
|
-
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) :
|
|
3303
|
+
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : nodes.map((n) => n.data.label || "").filter(Boolean).join("\n");
|
|
1991
3304
|
const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
|
|
1992
3305
|
setLastPromptPayload(payload);
|
|
1993
3306
|
const raw = await onGeneratePrompt(payload);
|
|
@@ -2015,7 +3328,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2015
3328
|
const handleProjectExport = async () => {
|
|
2016
3329
|
setProjectActionState("working-full");
|
|
2017
3330
|
try {
|
|
2018
|
-
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
3331
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
|
|
2019
3332
|
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
2020
3333
|
setProjectActionState("done");
|
|
2021
3334
|
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
@@ -2043,6 +3356,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2043
3356
|
setSeedMode(data.settings.seedMode || "random");
|
|
2044
3357
|
}
|
|
2045
3358
|
if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
|
|
3359
|
+
if (data.recentLabItemIds?.length) {
|
|
3360
|
+
const allGens = [...data.history || [], ...data.galleryItems || []];
|
|
3361
|
+
const labItems = groupGenerationsToLabItems(allGens);
|
|
3362
|
+
const recent = data.recentLabItemIds.map((id) => labItems.find((item) => item.id === id)).filter(Boolean);
|
|
3363
|
+
setRecentLabItems(recent);
|
|
3364
|
+
}
|
|
2046
3365
|
setProjectActionState("done");
|
|
2047
3366
|
setProjectLoaded(true);
|
|
2048
3367
|
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
@@ -2083,7 +3402,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2083
3402
|
if (!onServerSave) return;
|
|
2084
3403
|
setProjectActionState("working-full");
|
|
2085
3404
|
try {
|
|
2086
|
-
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
3405
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
|
|
2087
3406
|
await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
|
|
2088
3407
|
await fetchServerProjects();
|
|
2089
3408
|
setProjectActionState("done");
|
|
@@ -2127,7 +3446,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2127
3446
|
mergedHistory,
|
|
2128
3447
|
serverData.galleryItems || galleryItems,
|
|
2129
3448
|
serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
|
|
2130
|
-
serverData.workspaceTags || workspaceTags
|
|
3449
|
+
serverData.workspaceTags || workspaceTags,
|
|
3450
|
+
serverData.recentLabItemIds || recentLabItems.map((i) => i.id)
|
|
2131
3451
|
);
|
|
2132
3452
|
await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
|
|
2133
3453
|
await fetchServerProjects();
|
|
@@ -2138,12 +3458,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2138
3458
|
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
2139
3459
|
}
|
|
2140
3460
|
};
|
|
2141
|
-
(0,
|
|
3461
|
+
(0, import_react21.useEffect)(() => {
|
|
2142
3462
|
if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
|
|
2143
3463
|
}, [activeTab]);
|
|
2144
3464
|
if (isFullscreen && currentResult?.base64) {
|
|
2145
3465
|
const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
|
|
2146
|
-
return /* @__PURE__ */ (0,
|
|
3466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2147
3467
|
"div",
|
|
2148
3468
|
{
|
|
2149
3469
|
className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
|
|
@@ -2151,7 +3471,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2151
3471
|
onTouchMove: handleFsTouchMove,
|
|
2152
3472
|
onTouchEnd: handleFsTouchEnd,
|
|
2153
3473
|
children: [
|
|
2154
|
-
/* @__PURE__ */ (0,
|
|
3474
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2155
3475
|
"img",
|
|
2156
3476
|
{
|
|
2157
3477
|
src: fsBase64,
|
|
@@ -2168,76 +3488,76 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2168
3488
|
}
|
|
2169
3489
|
}
|
|
2170
3490
|
),
|
|
2171
|
-
/* @__PURE__ */ (0,
|
|
2172
|
-
zoomScale > 1 && /* @__PURE__ */ (0,
|
|
3491
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
|
|
3492
|
+
zoomScale > 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
|
|
2173
3493
|
setZoomScale(1);
|
|
2174
3494
|
setZoomOffset({ x: 0, y: 0 });
|
|
2175
|
-
}, 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__ */ (0,
|
|
2176
|
-
history.length > 1 && /* @__PURE__ */ (0,
|
|
2177
|
-
/* @__PURE__ */ (0,
|
|
3495
|
+
}, 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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
|
|
3496
|
+
history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3497
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
|
|
2178
3498
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
2179
|
-
}, 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__ */ (0,
|
|
2180
|
-
/* @__PURE__ */ (0,
|
|
3499
|
+
}, 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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
3500
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
|
|
2181
3501
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
2182
|
-
}, 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__ */ (0,
|
|
2183
|
-
/* @__PURE__ */ (0,
|
|
3502
|
+
}, 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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
3503
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
2184
3504
|
currentIndex + 1,
|
|
2185
3505
|
" / ",
|
|
2186
3506
|
history.length
|
|
2187
3507
|
] })
|
|
2188
3508
|
] }),
|
|
2189
|
-
zoomScale === 1 && /* @__PURE__ */ (0,
|
|
3509
|
+
zoomScale === 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
|
|
2190
3510
|
]
|
|
2191
3511
|
}
|
|
2192
3512
|
);
|
|
2193
3513
|
}
|
|
2194
3514
|
if (showStart) {
|
|
2195
|
-
return /* @__PURE__ */ (0,
|
|
2196
|
-
/* @__PURE__ */ (0,
|
|
3515
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
|
|
3516
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
|
|
2197
3517
|
const f = e.target.files?.[0];
|
|
2198
3518
|
if (f) handleProjectImport(f);
|
|
2199
3519
|
e.target.value = "";
|
|
2200
3520
|
} }),
|
|
2201
|
-
/* @__PURE__ */ (0,
|
|
2202
|
-
/* @__PURE__ */ (0,
|
|
2203
|
-
/* @__PURE__ */ (0,
|
|
2204
|
-
/* @__PURE__ */ (0,
|
|
3521
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
|
|
3523
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
|
|
3524
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-white text-[13px] font-mono", children: [
|
|
2205
3525
|
"v",
|
|
2206
3526
|
LIB_VERSION
|
|
2207
3527
|
] })
|
|
2208
3528
|
] }),
|
|
2209
|
-
/* @__PURE__ */ (0,
|
|
3529
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2210
3530
|
"button",
|
|
2211
3531
|
{
|
|
2212
3532
|
onClick: toggleContrast,
|
|
2213
3533
|
className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
|
|
2214
3534
|
style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
|
|
2215
3535
|
children: [
|
|
2216
|
-
/* @__PURE__ */ (0,
|
|
2217
|
-
/* @__PURE__ */ (0,
|
|
2218
|
-
/* @__PURE__ */ (0,
|
|
2219
|
-
/* @__PURE__ */ (0,
|
|
3536
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
|
|
3537
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-start", children: [
|
|
3538
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
|
|
3539
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
|
|
2220
3540
|
] })
|
|
2221
3541
|
]
|
|
2222
3542
|
}
|
|
2223
3543
|
),
|
|
2224
|
-
/* @__PURE__ */ (0,
|
|
2225
|
-
/* @__PURE__ */ (0,
|
|
3544
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3545
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2226
3546
|
"button",
|
|
2227
3547
|
{
|
|
2228
3548
|
onClick: () => wsInputRef.current?.click(),
|
|
2229
3549
|
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",
|
|
2230
3550
|
style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
|
|
2231
3551
|
children: [
|
|
2232
|
-
/* @__PURE__ */ (0,
|
|
3552
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
|
|
2233
3553
|
projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
|
|
2234
3554
|
]
|
|
2235
3555
|
}
|
|
2236
3556
|
),
|
|
2237
|
-
!projectLoaded && /* @__PURE__ */ (0,
|
|
3557
|
+
!projectLoaded && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
|
|
2238
3558
|
] }),
|
|
2239
|
-
onFetchServerProjects && /* @__PURE__ */ (0,
|
|
2240
|
-
/* @__PURE__ */ (0,
|
|
3559
|
+
onFetchServerProjects && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3560
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2241
3561
|
"button",
|
|
2242
3562
|
{
|
|
2243
3563
|
disabled: isLoadingFromServer,
|
|
@@ -2258,35 +3578,35 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2258
3578
|
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",
|
|
2259
3579
|
style: { height: 56, background: "#7c3aed" },
|
|
2260
3580
|
children: [
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
3581
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
|
|
2262
3582
|
isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
|
|
2263
3583
|
]
|
|
2264
3584
|
}
|
|
2265
3585
|
),
|
|
2266
|
-
/* @__PURE__ */ (0,
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
|
|
2267
3587
|
] }),
|
|
2268
|
-
/* @__PURE__ */ (0,
|
|
2269
|
-
/* @__PURE__ */ (0,
|
|
2270
|
-
/* @__PURE__ */ (0,
|
|
3588
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
3589
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
|
|
2271
3591
|
{ id: "mobile", icon: "smartphone", label: "Mobile" },
|
|
2272
3592
|
{ id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
|
|
2273
3593
|
{ id: "desktop", icon: "desktop_windows", label: "Desktop" },
|
|
2274
3594
|
{ id: "tablet-landscape", icon: "tablet", label: "Landscape" }
|
|
2275
|
-
].map((opt) => /* @__PURE__ */ (0,
|
|
3595
|
+
].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2276
3596
|
"button",
|
|
2277
3597
|
{
|
|
2278
3598
|
onClick: () => startApp(opt.id),
|
|
2279
3599
|
className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
|
|
2280
3600
|
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" },
|
|
2281
3601
|
children: [
|
|
2282
|
-
/* @__PURE__ */ (0,
|
|
2283
|
-
/* @__PURE__ */ (0,
|
|
3602
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
|
|
2284
3604
|
]
|
|
2285
3605
|
},
|
|
2286
3606
|
opt.id
|
|
2287
3607
|
)) }),
|
|
2288
|
-
layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0,
|
|
2289
|
-
layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0,
|
|
3608
|
+
layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
|
|
3609
|
+
layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
|
|
2290
3610
|
] })
|
|
2291
3611
|
] });
|
|
2292
3612
|
}
|
|
@@ -2295,28 +3615,46 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2295
3615
|
const mdScale = mdMode ? window.innerWidth / 430 : 1;
|
|
2296
3616
|
const mdW = mdMode ? 430 : void 0;
|
|
2297
3617
|
const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
|
|
2298
|
-
const mobileRoot = /* @__PURE__ */ (0,
|
|
3618
|
+
const mobileRoot = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
|
|
2299
3619
|
width: mdMode ? mdW : "100vw",
|
|
2300
3620
|
height: mdMode ? mdH : "100dvh",
|
|
2301
3621
|
transform: mdMode ? `scale(${mdScale})` : void 0,
|
|
2302
3622
|
transformOrigin: mdMode ? "top left" : void 0,
|
|
2303
3623
|
...hcStyle || {}
|
|
2304
3624
|
}, children: [
|
|
2305
|
-
mobileTab === "
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
3625
|
+
mobileTab === "labs" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LabsTab, { services: labServices, onResult: (item) => {
|
|
3626
|
+
const frame = item.frames[0];
|
|
3627
|
+
if (frame?.base64) {
|
|
3628
|
+
setCurrentResult(frameToGeneration(frame, item));
|
|
3629
|
+
setMobileTab("stage");
|
|
3630
|
+
}
|
|
3631
|
+
} }) }),
|
|
3632
|
+
mobileTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3633
|
+
TagManagerPanel,
|
|
3634
|
+
{
|
|
3635
|
+
workspaceTags,
|
|
3636
|
+
onTagCreate: handleTagCreate,
|
|
3637
|
+
onTagUpdate: handleTagUpdate,
|
|
3638
|
+
onTagDelete: handleTagDelete,
|
|
3639
|
+
onTagReorder: handleTagReorder,
|
|
3640
|
+
onTagMove: handleTagMove
|
|
3641
|
+
}
|
|
3642
|
+
) }),
|
|
3643
|
+
mobileTab === "stage" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
3644
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
|
|
3645
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
3646
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
|
|
3647
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1" }),
|
|
3648
|
+
activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
|
|
3649
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
|
|
3650
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
|
|
3651
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
3652
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
|
|
3653
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
3654
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
|
|
2317
3655
|
] }),
|
|
2318
|
-
/* @__PURE__ */ (0,
|
|
2319
|
-
/* @__PURE__ */ (0,
|
|
3656
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
3657
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2320
3658
|
"textarea",
|
|
2321
3659
|
{
|
|
2322
3660
|
value: activePrompt,
|
|
@@ -2326,26 +3664,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2326
3664
|
placeholder: "Prompt eingeben..."
|
|
2327
3665
|
}
|
|
2328
3666
|
),
|
|
2329
|
-
activePrompt && !isSynthesizing && /* @__PURE__ */ (0,
|
|
3667
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
|
|
2330
3668
|
] }) }),
|
|
2331
|
-
/* @__PURE__ */ (0,
|
|
3669
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2332
3670
|
"button",
|
|
2333
3671
|
{
|
|
2334
3672
|
onClick: () => handleGenerateImage(),
|
|
2335
3673
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2336
3674
|
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",
|
|
2337
3675
|
style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
|
|
2338
|
-
children: isGenerating ? /* @__PURE__ */ (0,
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2340
|
-
/* @__PURE__ */ (0,
|
|
2341
|
-
] }) : /* @__PURE__ */ (0,
|
|
2342
|
-
/* @__PURE__ */ (0,
|
|
2343
|
-
/* @__PURE__ */ (0,
|
|
3676
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3677
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
|
|
3678
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generiere..." })
|
|
3679
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3680
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
|
|
3681
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generieren" })
|
|
2344
3682
|
] })
|
|
2345
3683
|
}
|
|
2346
3684
|
) }),
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
2348
|
-
/* @__PURE__ */ (0,
|
|
3685
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
|
|
3686
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2349
3687
|
"div",
|
|
2350
3688
|
{
|
|
2351
3689
|
className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
|
|
@@ -2359,25 +3697,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2359
3697
|
setTouchStartX(null);
|
|
2360
3698
|
},
|
|
2361
3699
|
children: [
|
|
2362
|
-
currentResult?.status === "processing" && /* @__PURE__ */ (0,
|
|
2363
|
-
/* @__PURE__ */ (0,
|
|
2364
|
-
/* @__PURE__ */ (0,
|
|
3700
|
+
currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
|
|
3701
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
2365
3703
|
] }),
|
|
2366
|
-
currentResult?.status === "error" && /* @__PURE__ */ (0,
|
|
2367
|
-
/* @__PURE__ */ (0,
|
|
2368
|
-
/* @__PURE__ */ (0,
|
|
2369
|
-
/* @__PURE__ */ (0,
|
|
3704
|
+
currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
|
|
3705
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
|
|
3706
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
|
|
3707
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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" })
|
|
2370
3708
|
] }),
|
|
2371
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0,
|
|
2372
|
-
!currentResult && /* @__PURE__ */ (0,
|
|
2373
|
-
/* @__PURE__ */ (0,
|
|
2374
|
-
/* @__PURE__ */ (0,
|
|
3709
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
|
|
3710
|
+
!currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
3711
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
|
|
3712
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
2375
3713
|
] }),
|
|
2376
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2377
|
-
history.length > 1 && currentResult && /* @__PURE__ */ (0,
|
|
2378
|
-
/* @__PURE__ */ (0,
|
|
2379
|
-
/* @__PURE__ */ (0,
|
|
2380
|
-
/* @__PURE__ */ (0,
|
|
3714
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
|
|
3715
|
+
history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3716
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
3717
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
3718
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
2381
3719
|
currentIndex + 1,
|
|
2382
3720
|
" / ",
|
|
2383
3721
|
history.length
|
|
@@ -2386,30 +3724,30 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2386
3724
|
]
|
|
2387
3725
|
}
|
|
2388
3726
|
),
|
|
2389
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2390
|
-
/* @__PURE__ */ (0,
|
|
2391
|
-
/* @__PURE__ */ (0,
|
|
2392
|
-
/* @__PURE__ */ (0,
|
|
3727
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex gap-2 mt-3", children: [
|
|
3728
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
|
|
3730
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/60", children: "Prompt" })
|
|
2393
3731
|
] }),
|
|
2394
|
-
/* @__PURE__ */ (0,
|
|
2395
|
-
/* @__PURE__ */ (0,
|
|
2396
|
-
/* @__PURE__ */ (0,
|
|
3732
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3733
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
|
|
3734
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
|
|
2397
3735
|
] }),
|
|
2398
|
-
/* @__PURE__ */ (0,
|
|
2399
|
-
/* @__PURE__ */ (0,
|
|
2400
|
-
/* @__PURE__ */ (0,
|
|
3736
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3737
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
|
|
3738
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/60", children: "Laden" })
|
|
2401
3739
|
] })
|
|
2402
3740
|
] })
|
|
2403
3741
|
] })
|
|
2404
3742
|
] }),
|
|
2405
|
-
mobileTab === "browse" && /* @__PURE__ */ (0,
|
|
2406
|
-
/* @__PURE__ */ (0,
|
|
2407
|
-
/* @__PURE__ */ (0,
|
|
2408
|
-
activeTab === "history" && /* @__PURE__ */ (0,
|
|
3743
|
+
mobileTab === "browse" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
3744
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)) }),
|
|
3745
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
3746
|
+
activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
|
|
2409
3747
|
setCurrentResult(g);
|
|
2410
3748
|
setMobileTab("stage");
|
|
2411
3749
|
}, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
2412
|
-
activeTab === "gallery" && /* @__PURE__ */ (0,
|
|
3750
|
+
activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2413
3751
|
MediaLibrary,
|
|
2414
3752
|
{
|
|
2415
3753
|
items: galleryItems,
|
|
@@ -2429,38 +3767,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2429
3767
|
}
|
|
2430
3768
|
}
|
|
2431
3769
|
),
|
|
2432
|
-
activeTab === "inspect" && /* @__PURE__ */ (0,
|
|
3770
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
|
|
2433
3771
|
setCurrentResult(g);
|
|
2434
3772
|
} })
|
|
2435
3773
|
] })
|
|
2436
3774
|
] }),
|
|
2437
|
-
/* @__PURE__ */ (0,
|
|
2438
|
-
/* @__PURE__ */ (0,
|
|
2439
|
-
workspaceTags && /* @__PURE__ */ (0,
|
|
3775
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
|
|
3776
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
|
|
3777
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => {
|
|
2440
3778
|
setLeftTab("prompt");
|
|
2441
3779
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2442
3780
|
}, 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: [
|
|
2443
|
-
/* @__PURE__ */ (0,
|
|
3781
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
|
|
2444
3782
|
"Prompt"
|
|
2445
3783
|
] }),
|
|
2446
|
-
/* @__PURE__ */ (0,
|
|
3784
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => {
|
|
2447
3785
|
setLeftTab("hierarchy");
|
|
2448
3786
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2449
3787
|
}, 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: [
|
|
2450
|
-
/* @__PURE__ */ (0,
|
|
3788
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
|
|
2451
3789
|
"Hierarchie"
|
|
2452
3790
|
] }),
|
|
2453
|
-
/* @__PURE__ */ (0,
|
|
2454
|
-
/* @__PURE__ */ (0,
|
|
3791
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3792
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
|
|
2455
3793
|
"Setup"
|
|
2456
3794
|
] }),
|
|
2457
|
-
/* @__PURE__ */ (0,
|
|
2458
|
-
/* @__PURE__ */ (0,
|
|
3795
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3796
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
|
|
2459
3797
|
"Sync"
|
|
2460
|
-
] })
|
|
3798
|
+
] }),
|
|
3799
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
|
|
2461
3800
|
] }),
|
|
2462
|
-
/* @__PURE__ */ (0,
|
|
2463
|
-
leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0,
|
|
3801
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
3802
|
+
leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2464
3803
|
ListView,
|
|
2465
3804
|
{
|
|
2466
3805
|
nodes,
|
|
@@ -2491,12 +3830,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2491
3830
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2492
3831
|
}
|
|
2493
3832
|
) }),
|
|
2494
|
-
workspaceTags && /* @__PURE__ */ (0,
|
|
3833
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
|
|
2495
3834
|
handleGenerateImage(prompt);
|
|
2496
3835
|
setMobileTab("stage");
|
|
2497
3836
|
}, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
|
|
2498
|
-
activeTab === "setup" && /* @__PURE__ */ (0,
|
|
2499
|
-
activeTab === "sync" && /* @__PURE__ */ (0,
|
|
3837
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
3838
|
+
activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2500
3839
|
ProjectSyncTab,
|
|
2501
3840
|
{
|
|
2502
3841
|
onProjectExport: handleProjectExport,
|
|
@@ -2511,20 +3850,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2511
3850
|
onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
|
|
2512
3851
|
onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
|
|
2513
3852
|
}
|
|
3853
|
+
),
|
|
3854
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3855
|
+
TagManagerPanel,
|
|
3856
|
+
{
|
|
3857
|
+
workspaceTags,
|
|
3858
|
+
onTagCreate: handleTagCreate,
|
|
3859
|
+
onTagUpdate: handleTagUpdate,
|
|
3860
|
+
onTagDelete: handleTagDelete,
|
|
3861
|
+
onTagReorder: handleTagReorder,
|
|
3862
|
+
onTagMove: handleTagMove
|
|
3863
|
+
}
|
|
2514
3864
|
)
|
|
2515
3865
|
] })
|
|
2516
3866
|
] }),
|
|
2517
|
-
/* @__PURE__ */ (0,
|
|
3867
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
|
|
2518
3868
|
{ id: "tools", icon: "auto_fix_high", label: "Prompt" },
|
|
2519
3869
|
{ id: "stage", icon: "palette", label: "Stage" },
|
|
3870
|
+
{ id: "labs", icon: "science", label: "Labs" },
|
|
3871
|
+
...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
|
|
2520
3872
|
{ id: "browse", icon: "photo_library", label: "Galerie" }
|
|
2521
|
-
].map((tab) => /* @__PURE__ */ (0,
|
|
2522
|
-
/* @__PURE__ */ (0,
|
|
2523
|
-
/* @__PURE__ */ (0,
|
|
3873
|
+
].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3874
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
|
|
3875
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
|
|
2524
3876
|
] }, tab.id)) })
|
|
2525
3877
|
] });
|
|
2526
3878
|
if (mdMode) {
|
|
2527
|
-
return /* @__PURE__ */ (0,
|
|
3879
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
|
|
2528
3880
|
}
|
|
2529
3881
|
return mobileRoot;
|
|
2530
3882
|
}
|
|
@@ -2532,17 +3884,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2532
3884
|
const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
|
|
2533
3885
|
const tlW = 920;
|
|
2534
3886
|
const tlH = 520;
|
|
2535
|
-
return /* @__PURE__ */ (0,
|
|
2536
|
-
/* @__PURE__ */ (0,
|
|
2537
|
-
/* @__PURE__ */ (0,
|
|
2538
|
-
/* @__PURE__ */ (0,
|
|
2539
|
-
/* @__PURE__ */ (0,
|
|
2540
|
-
/* @__PURE__ */ (0,
|
|
2541
|
-
/* @__PURE__ */ (0,
|
|
2542
|
-
/* @__PURE__ */ (0,
|
|
3887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
|
|
3888
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
|
|
3889
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3890
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
3891
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
|
|
3892
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1 } }),
|
|
3893
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
3894
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
|
|
2543
3895
|
] }),
|
|
2544
|
-
/* @__PURE__ */ (0,
|
|
2545
|
-
/* @__PURE__ */ (0,
|
|
3896
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3897
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2546
3898
|
"textarea",
|
|
2547
3899
|
{
|
|
2548
3900
|
value: activePrompt,
|
|
@@ -2551,27 +3903,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2551
3903
|
placeholder: "Prompt eingeben..."
|
|
2552
3904
|
}
|
|
2553
3905
|
),
|
|
2554
|
-
activePrompt && /* @__PURE__ */ (0,
|
|
3906
|
+
activePrompt && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
|
|
2555
3907
|
] }) }),
|
|
2556
|
-
/* @__PURE__ */ (0,
|
|
3908
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2557
3909
|
"button",
|
|
2558
3910
|
{
|
|
2559
3911
|
onClick: () => handleGenerateImage(),
|
|
2560
3912
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2561
3913
|
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" },
|
|
2562
|
-
children: isGenerating ? /* @__PURE__ */ (0,
|
|
2563
|
-
/* @__PURE__ */ (0,
|
|
2564
|
-
/* @__PURE__ */ (0,
|
|
2565
|
-
] }) : /* @__PURE__ */ (0,
|
|
2566
|
-
/* @__PURE__ */ (0,
|
|
2567
|
-
/* @__PURE__ */ (0,
|
|
3914
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3915
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
3916
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generiere..." })
|
|
3917
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3918
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
|
|
3919
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generieren" })
|
|
2568
3920
|
] })
|
|
2569
3921
|
}
|
|
2570
3922
|
) }),
|
|
2571
|
-
/* @__PURE__ */ (0,
|
|
3923
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
|
|
2572
3924
|
] }),
|
|
2573
|
-
/* @__PURE__ */ (0,
|
|
2574
|
-
/* @__PURE__ */ (0,
|
|
3925
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
|
|
3926
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2575
3927
|
"div",
|
|
2576
3928
|
{
|
|
2577
3929
|
style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
|
|
@@ -2583,26 +3935,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2583
3935
|
else if (dx > 50) goToPrev();
|
|
2584
3936
|
setTouchStartX(null);
|
|
2585
3937
|
},
|
|
2586
|
-
children: /* @__PURE__ */ (0,
|
|
2587
|
-
currentResult?.status === "processing" && /* @__PURE__ */ (0,
|
|
2588
|
-
/* @__PURE__ */ (0,
|
|
2589
|
-
/* @__PURE__ */ (0,
|
|
3938
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3939
|
+
currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
3940
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
3941
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
|
|
2590
3942
|
] }),
|
|
2591
|
-
currentResult?.status === "error" && /* @__PURE__ */ (0,
|
|
2592
|
-
/* @__PURE__ */ (0,
|
|
2593
|
-
/* @__PURE__ */ (0,
|
|
2594
|
-
/* @__PURE__ */ (0,
|
|
3943
|
+
currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
3944
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
|
|
3945
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
|
|
3946
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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" })
|
|
2595
3947
|
] }),
|
|
2596
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0,
|
|
2597
|
-
!currentResult && /* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
3948
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
|
|
3949
|
+
!currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
|
|
3950
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
|
|
3951
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
|
|
2600
3952
|
] }),
|
|
2601
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2602
|
-
history.length > 1 && currentResult && /* @__PURE__ */ (0,
|
|
2603
|
-
/* @__PURE__ */ (0,
|
|
2604
|
-
/* @__PURE__ */ (0,
|
|
2605
|
-
/* @__PURE__ */ (0,
|
|
3953
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
|
|
3954
|
+
history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3955
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
|
|
3956
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
|
|
3957
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
2606
3958
|
currentIndex + 1,
|
|
2607
3959
|
" / ",
|
|
2608
3960
|
history.length
|
|
@@ -2611,41 +3963,57 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2611
3963
|
] })
|
|
2612
3964
|
}
|
|
2613
3965
|
),
|
|
2614
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2615
|
-
/* @__PURE__ */ (0,
|
|
2616
|
-
/* @__PURE__ */ (0,
|
|
2617
|
-
/* @__PURE__ */ (0,
|
|
3966
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
|
|
3967
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3968
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
|
|
3969
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Prompt" })
|
|
2618
3970
|
] }),
|
|
2619
|
-
/* @__PURE__ */ (0,
|
|
2620
|
-
/* @__PURE__ */ (0,
|
|
2621
|
-
/* @__PURE__ */ (0,
|
|
3971
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3972
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
|
|
3973
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Referenz" })
|
|
2622
3974
|
] }),
|
|
2623
|
-
/* @__PURE__ */ (0,
|
|
2624
|
-
/* @__PURE__ */ (0,
|
|
2625
|
-
/* @__PURE__ */ (0,
|
|
3975
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3976
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
|
|
3977
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Laden" })
|
|
2626
3978
|
] })
|
|
2627
3979
|
] })
|
|
2628
3980
|
] })
|
|
2629
3981
|
] }) });
|
|
2630
3982
|
}
|
|
2631
|
-
return /* @__PURE__ */ (0,
|
|
2632
|
-
/* @__PURE__ */ (0,
|
|
2633
|
-
/* @__PURE__ */ (0,
|
|
2634
|
-
/* @__PURE__ */ (0,
|
|
2635
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
2636
|
-
workspaceTags && /* @__PURE__ */ (0,
|
|
2637
|
-
/* @__PURE__ */ (0,
|
|
3983
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
|
|
3984
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
|
|
3985
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
|
|
3986
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
|
|
3987
|
+
!isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-1 gap-1", children: [
|
|
3988
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
|
|
3989
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
|
|
2638
3990
|
"Prompt"
|
|
2639
3991
|
] }),
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2641
|
-
/* @__PURE__ */ (0,
|
|
3992
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("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: [
|
|
3993
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
|
|
2642
3994
|
"Hierarchie"
|
|
2643
|
-
] })
|
|
3995
|
+
] }),
|
|
3996
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
|
|
2644
3997
|
] }),
|
|
2645
|
-
/* @__PURE__ */ (0,
|
|
3998
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
|
|
2646
3999
|
] }),
|
|
2647
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
2648
|
-
|
|
4000
|
+
!isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
4001
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4002
|
+
TagManagerPanel,
|
|
4003
|
+
{
|
|
4004
|
+
workspaceTags,
|
|
4005
|
+
onTagCreate: handleTagCreate,
|
|
4006
|
+
onTagUpdate: handleTagUpdate,
|
|
4007
|
+
onTagDelete: handleTagDelete,
|
|
4008
|
+
onTagReorder: handleTagReorder,
|
|
4009
|
+
onTagMove: handleTagMove
|
|
4010
|
+
}
|
|
4011
|
+
),
|
|
4012
|
+
activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
|
|
4013
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
|
|
4014
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
|
|
4015
|
+
] }) }),
|
|
4016
|
+
leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2649
4017
|
ListView,
|
|
2650
4018
|
{
|
|
2651
4019
|
nodes,
|
|
@@ -2670,75 +4038,111 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2670
4038
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2671
4039
|
}
|
|
2672
4040
|
) }),
|
|
2673
|
-
leftTab === "prompt" && workspaceTags && /* @__PURE__ */ (0,
|
|
4041
|
+
leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
|
|
2674
4042
|
] })
|
|
2675
4043
|
] }),
|
|
2676
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
2677
|
-
/* @__PURE__ */ (0,
|
|
2678
|
-
/* @__PURE__ */ (0,
|
|
2679
|
-
/* @__PURE__ */ (0,
|
|
2680
|
-
/* @__PURE__ */ (0,
|
|
2681
|
-
/* @__PURE__ */ (0,
|
|
4044
|
+
!isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
|
|
4045
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
|
|
4046
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
|
|
4047
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
4048
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
|
|
4049
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
|
|
2682
4050
|
] }),
|
|
2683
|
-
/* @__PURE__ */ (0,
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
4051
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 mx-auto", children: [
|
|
4052
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4053
|
+
"button",
|
|
4054
|
+
{
|
|
4055
|
+
onClick: () => setMiddlePanel("stage"),
|
|
4056
|
+
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"}`,
|
|
4057
|
+
children: "Stage"
|
|
4058
|
+
}
|
|
4059
|
+
),
|
|
4060
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4061
|
+
"button",
|
|
4062
|
+
{
|
|
4063
|
+
onClick: () => setMiddlePanel("labs"),
|
|
4064
|
+
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"}`,
|
|
4065
|
+
children: "Labs"
|
|
4066
|
+
}
|
|
4067
|
+
)
|
|
4068
|
+
] }),
|
|
4069
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
4070
|
+
activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
|
|
4071
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
|
|
4072
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
|
|
4073
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
4074
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
|
|
4075
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
|
|
4076
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Ref" })
|
|
2691
4077
|
] }),
|
|
2692
|
-
/* @__PURE__ */ (0,
|
|
2693
|
-
/* @__PURE__ */ (0,
|
|
4078
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
|
|
4079
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
|
|
2694
4080
|
] })
|
|
2695
4081
|
] }),
|
|
2696
|
-
/* @__PURE__ */ (0,
|
|
2697
|
-
!isPromptCollapsed && /* @__PURE__ */ (0,
|
|
2698
|
-
/* @__PURE__ */ (0,
|
|
2699
|
-
activePrompt && !isSynthesizing && /* @__PURE__ */ (0,
|
|
4082
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
|
|
4083
|
+
!isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
4084
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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..." }),
|
|
4085
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
2700
4086
|
] }) }),
|
|
2701
|
-
/* @__PURE__ */ (0,
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
4087
|
+
middlePanel === "labs" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LabsTab, { services: labServices, onResult: (item) => {
|
|
4088
|
+
const frame = item.frames[0];
|
|
4089
|
+
if (frame?.base64) setCurrentResult(frameToGeneration(frame, item));
|
|
4090
|
+
} }) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-full w-full max-w-4xl aspect-square rounded-3xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center group shadow-2xl", children: [
|
|
4091
|
+
isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute top-6 right-6 z-30 bg-black/60 backdrop-blur-md px-4 py-2 rounded-full border border-white/10 flex items-center gap-3", children: [
|
|
4092
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
|
|
4093
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
|
|
2705
4094
|
] }),
|
|
2706
|
-
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0,
|
|
2707
|
-
/* @__PURE__ */ (0,
|
|
2708
|
-
/* @__PURE__ */ (0,
|
|
2709
|
-
] }) : currentResult.status === "error" ? /* @__PURE__ */ (0,
|
|
2710
|
-
/* @__PURE__ */ (0,
|
|
2711
|
-
/* @__PURE__ */ (0,
|
|
2712
|
-
/* @__PURE__ */ (0,
|
|
2713
|
-
/* @__PURE__ */ (0,
|
|
4095
|
+
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
4096
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
4097
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
4098
|
+
] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
|
|
4099
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
|
|
4100
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
4101
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
|
|
4102
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
|
|
2714
4103
|
] }),
|
|
2715
|
-
/* @__PURE__ */ (0,
|
|
2716
|
-
] }) : /* @__PURE__ */ (0,
|
|
2717
|
-
/* @__PURE__ */ (0,
|
|
2718
|
-
/* @__PURE__ */ (0,
|
|
2719
|
-
/* @__PURE__ */ (0,
|
|
2720
|
-
/* @__PURE__ */ (0,
|
|
2721
|
-
/* @__PURE__ */ (0,
|
|
4104
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
|
|
4105
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
|
|
4106
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
|
|
4107
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute bottom-6 flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-4 group-hover:translate-y-0 z-20", children: [
|
|
4108
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
|
|
4109
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
|
|
4110
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
|
|
2722
4111
|
] })
|
|
2723
|
-
] }) : /* @__PURE__ */ (0,
|
|
2724
|
-
/* @__PURE__ */ (0,
|
|
2725
|
-
/* @__PURE__ */ (0,
|
|
4112
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
4113
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
|
|
4114
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
2726
4115
|
] })
|
|
2727
4116
|
] }) })
|
|
2728
4117
|
] })
|
|
2729
4118
|
] }),
|
|
2730
|
-
!isRightCollapsed && /* @__PURE__ */ (0,
|
|
2731
|
-
/* @__PURE__ */ (0,
|
|
2732
|
-
/* @__PURE__ */ (0,
|
|
2733
|
-
/* @__PURE__ */ (0,
|
|
4119
|
+
!isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
|
|
4120
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
|
|
4121
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
|
|
4122
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
|
|
2734
4123
|
setActiveTab(tab);
|
|
2735
4124
|
setIsRightCollapsed(false);
|
|
2736
|
-
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0,
|
|
2737
|
-
/* @__PURE__ */ (0,
|
|
4125
|
+
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("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)) }),
|
|
4126
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
|
|
2738
4127
|
] }),
|
|
2739
|
-
!isRightCollapsed && /* @__PURE__ */ (0,
|
|
2740
|
-
activeTab === "
|
|
2741
|
-
|
|
4128
|
+
!isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
4129
|
+
activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
4130
|
+
TagManagerPanel,
|
|
4131
|
+
{
|
|
4132
|
+
workspaceTags,
|
|
4133
|
+
onTagCreate: handleTagCreate,
|
|
4134
|
+
onTagUpdate: handleTagUpdate,
|
|
4135
|
+
onTagDelete: handleTagDelete,
|
|
4136
|
+
onTagReorder: handleTagReorder,
|
|
4137
|
+
onTagMove: handleTagMove
|
|
4138
|
+
}
|
|
4139
|
+
),
|
|
4140
|
+
activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
|
|
4141
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
|
|
4142
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
|
|
4143
|
+
] }) }),
|
|
4144
|
+
activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
4145
|
+
activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2742
4146
|
MediaLibrary,
|
|
2743
4147
|
{
|
|
2744
4148
|
items: galleryItems,
|
|
@@ -2752,9 +4156,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2752
4156
|
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
2753
4157
|
}
|
|
2754
4158
|
),
|
|
2755
|
-
activeTab === "inspect" && /* @__PURE__ */ (0,
|
|
2756
|
-
activeTab === "setup" && /* @__PURE__ */ (0,
|
|
2757
|
-
activeTab === "sync" && /* @__PURE__ */ (0,
|
|
4159
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
|
|
4160
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
4161
|
+
activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2758
4162
|
ProjectSyncTab,
|
|
2759
4163
|
{
|
|
2760
4164
|
onProjectExport: handleProjectExport,
|
|
@@ -2776,7 +4180,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2776
4180
|
}
|
|
2777
4181
|
|
|
2778
4182
|
// src/index.ts
|
|
2779
|
-
var LIB_VERSION = "1.
|
|
4183
|
+
var LIB_VERSION = "1.2.2";
|
|
2780
4184
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2781
4185
|
0 && (module.exports = {
|
|
2782
4186
|
AvatarArchitectApp,
|
|
@@ -2787,6 +4191,12 @@ var LIB_VERSION = "1.1.4";
|
|
|
2787
4191
|
HistoryPanel,
|
|
2788
4192
|
InspectPanel,
|
|
2789
4193
|
LIB_VERSION,
|
|
4194
|
+
LabBlend,
|
|
4195
|
+
LabCompare,
|
|
4196
|
+
LabImagePicker,
|
|
4197
|
+
LabLoop,
|
|
4198
|
+
LabRemix,
|
|
4199
|
+
LabsTab,
|
|
2790
4200
|
ListView,
|
|
2791
4201
|
MediaLibrary,
|
|
2792
4202
|
PillButton,
|
|
@@ -2794,15 +4204,25 @@ var LIB_VERSION = "1.1.4";
|
|
|
2794
4204
|
PromptTab,
|
|
2795
4205
|
SectionLabel,
|
|
2796
4206
|
SetupPanel,
|
|
4207
|
+
TagManagerPanel,
|
|
4208
|
+
autoLabel,
|
|
4209
|
+
buildBlendInstruction,
|
|
4210
|
+
buildCompareInstruction,
|
|
2797
4211
|
buildFallbackPrompt,
|
|
2798
4212
|
buildGenerationPrompt,
|
|
2799
4213
|
buildImageGenerationOptions,
|
|
4214
|
+
buildLoopInstruction,
|
|
2800
4215
|
buildPromptTabPayload,
|
|
4216
|
+
buildReferenceImageMediaIds,
|
|
4217
|
+
buildRemixInstruction,
|
|
4218
|
+
buildScanInstruction,
|
|
2801
4219
|
cleanAiResponse,
|
|
2802
4220
|
createFlowServices,
|
|
2803
4221
|
exportProjectToZip,
|
|
2804
4222
|
formatTreeToMarkdown,
|
|
4223
|
+
frameToGeneration,
|
|
2805
4224
|
getFormattedTimestamp,
|
|
4225
|
+
groupGenerationsToLabItems,
|
|
2806
4226
|
importProjectFromZip,
|
|
2807
4227
|
injectXMPMetadata,
|
|
2808
4228
|
interpretSdkError,
|