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