@rslsp1/fa-app-tools 1.1.3 → 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 +1786 -315
- package/dist/index.mjs +1782 -327
- 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,50 +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 [
|
|
1725
|
-
const [
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
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)(() => {
|
|
2989
|
+
try {
|
|
2990
|
+
return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
|
|
2991
|
+
} catch {
|
|
2992
|
+
return 260;
|
|
2993
|
+
}
|
|
2994
|
+
});
|
|
2995
|
+
const [rightPanelWidth, setRightPanelWidth] = (0, import_react21.useState)(() => {
|
|
2996
|
+
try {
|
|
2997
|
+
return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
|
|
2998
|
+
} catch {
|
|
2999
|
+
return 320;
|
|
3000
|
+
}
|
|
3001
|
+
});
|
|
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)(() => {
|
|
1731
3009
|
try {
|
|
1732
3010
|
return localStorage.getItem("aa-contrast") === "high";
|
|
1733
3011
|
} catch {
|
|
1734
3012
|
return false;
|
|
1735
3013
|
}
|
|
1736
3014
|
});
|
|
1737
|
-
const [activeReferenceId, setActiveReferenceId] = (0,
|
|
1738
|
-
const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0,
|
|
1739
|
-
const [isScanningImage, setIsScanningImage] = (0,
|
|
1740
|
-
const [touchStartX, setTouchStartX] = (0,
|
|
1741
|
-
const [isFullscreen, setIsFullscreen] = (0,
|
|
1742
|
-
const [zoomScale, setZoomScale] = (0,
|
|
1743
|
-
const [zoomOffset, setZoomOffset] = (0,
|
|
1744
|
-
const lastPinchDist = (0,
|
|
1745
|
-
const lastTapTime = (0,
|
|
1746
|
-
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);
|
|
1747
3025
|
const openFullscreen = () => {
|
|
1748
3026
|
setIsFullscreen(true);
|
|
1749
3027
|
setZoomScale(1);
|
|
@@ -1806,6 +3084,65 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1806
3084
|
setActiveReferenceId(null);
|
|
1807
3085
|
setActiveReferenceThumbnail(null);
|
|
1808
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]);
|
|
3108
|
+
const startLeftResize = (e) => {
|
|
3109
|
+
e.preventDefault();
|
|
3110
|
+
const startX = e.clientX;
|
|
3111
|
+
const startW = leftPanelWidth;
|
|
3112
|
+
const onMove = (ev) => {
|
|
3113
|
+
const w = Math.max(180, Math.min(520, startW + ev.clientX - startX));
|
|
3114
|
+
setLeftPanelWidth(w);
|
|
3115
|
+
try {
|
|
3116
|
+
localStorage.setItem("aa-left-width", String(w));
|
|
3117
|
+
} catch {
|
|
3118
|
+
}
|
|
3119
|
+
};
|
|
3120
|
+
const onUp = () => {
|
|
3121
|
+
document.removeEventListener("mousemove", onMove);
|
|
3122
|
+
document.removeEventListener("mouseup", onUp);
|
|
3123
|
+
};
|
|
3124
|
+
document.addEventListener("mousemove", onMove);
|
|
3125
|
+
document.addEventListener("mouseup", onUp);
|
|
3126
|
+
};
|
|
3127
|
+
const startRightResize = (e) => {
|
|
3128
|
+
e.preventDefault();
|
|
3129
|
+
const startX = e.clientX;
|
|
3130
|
+
const startW = rightPanelWidth;
|
|
3131
|
+
const onMove = (ev) => {
|
|
3132
|
+
const w = Math.max(180, Math.min(520, startW - (ev.clientX - startX)));
|
|
3133
|
+
setRightPanelWidth(w);
|
|
3134
|
+
try {
|
|
3135
|
+
localStorage.setItem("aa-right-width", String(w));
|
|
3136
|
+
} catch {
|
|
3137
|
+
}
|
|
3138
|
+
};
|
|
3139
|
+
const onUp = () => {
|
|
3140
|
+
document.removeEventListener("mousemove", onMove);
|
|
3141
|
+
document.removeEventListener("mouseup", onUp);
|
|
3142
|
+
};
|
|
3143
|
+
document.addEventListener("mousemove", onMove);
|
|
3144
|
+
document.addEventListener("mouseup", onUp);
|
|
3145
|
+
};
|
|
1809
3146
|
const handleScanImage = async (file) => {
|
|
1810
3147
|
setIsScanningImage(true);
|
|
1811
3148
|
try {
|
|
@@ -1815,7 +3152,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1815
3152
|
reader.onerror = reject;
|
|
1816
3153
|
reader.readAsDataURL(file);
|
|
1817
3154
|
});
|
|
1818
|
-
const
|
|
3155
|
+
const { text, systemInstruction } = buildScanInstruction();
|
|
3156
|
+
const prompt = await onGeneratePrompt(text, {
|
|
3157
|
+
images: [{ base64, mimeType: file.type || "image/png" }],
|
|
3158
|
+
systemInstruction
|
|
3159
|
+
});
|
|
1819
3160
|
setActivePrompt(prompt);
|
|
1820
3161
|
} catch (err) {
|
|
1821
3162
|
console.error("Scan fehlgeschlagen:", err);
|
|
@@ -1823,17 +3164,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1823
3164
|
setIsScanningImage(false);
|
|
1824
3165
|
}
|
|
1825
3166
|
};
|
|
1826
|
-
const currentIndex = (0,
|
|
1827
|
-
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)(() => {
|
|
1828
3169
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
1829
3170
|
}, [currentIndex, history]);
|
|
1830
|
-
const goToNext = (0,
|
|
3171
|
+
const goToNext = (0, import_react21.useCallback)(() => {
|
|
1831
3172
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
1832
3173
|
}, [currentIndex, history]);
|
|
1833
3174
|
const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
|
|
1834
3175
|
const isGenerating = activeGenerationsCount > 0;
|
|
1835
3176
|
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
1836
|
-
const getSubtreeFormat = (0,
|
|
3177
|
+
const getSubtreeFormat = (0, import_react21.useCallback)((nodeId, depth = 0) => {
|
|
1837
3178
|
const node = nodes.find((n) => n.id === nodeId);
|
|
1838
3179
|
if (!node) return "";
|
|
1839
3180
|
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
@@ -1841,7 +3182,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1841
3182
|
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
1842
3183
|
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
1843
3184
|
}, [nodes, edges]);
|
|
1844
|
-
const activePath = (0,
|
|
3185
|
+
const activePath = (0, import_react21.useMemo)(() => {
|
|
1845
3186
|
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
1846
3187
|
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
1847
3188
|
let currId = focusedNodeId;
|
|
@@ -1931,11 +3272,32 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1931
3272
|
return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
|
|
1932
3273
|
});
|
|
1933
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
|
+
};
|
|
1934
3296
|
const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
|
|
1935
3297
|
setIsPromptTabGenerating(true);
|
|
1936
3298
|
setPromptFeedback(null);
|
|
1937
3299
|
try {
|
|
1938
|
-
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) :
|
|
3300
|
+
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : nodes.map((n) => n.data.label || "").filter(Boolean).join("\n");
|
|
1939
3301
|
const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
|
|
1940
3302
|
setLastPromptPayload(payload);
|
|
1941
3303
|
const raw = await onGeneratePrompt(payload);
|
|
@@ -1963,7 +3325,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1963
3325
|
const handleProjectExport = async () => {
|
|
1964
3326
|
setProjectActionState("working-full");
|
|
1965
3327
|
try {
|
|
1966
|
-
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));
|
|
1967
3329
|
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
1968
3330
|
setProjectActionState("done");
|
|
1969
3331
|
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
@@ -1991,6 +3353,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1991
3353
|
setSeedMode(data.settings.seedMode || "random");
|
|
1992
3354
|
}
|
|
1993
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
|
+
}
|
|
1994
3362
|
setProjectActionState("done");
|
|
1995
3363
|
setProjectLoaded(true);
|
|
1996
3364
|
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
@@ -2031,7 +3399,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2031
3399
|
if (!onServerSave) return;
|
|
2032
3400
|
setProjectActionState("working-full");
|
|
2033
3401
|
try {
|
|
2034
|
-
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));
|
|
2035
3403
|
await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
|
|
2036
3404
|
await fetchServerProjects();
|
|
2037
3405
|
setProjectActionState("done");
|
|
@@ -2075,7 +3443,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2075
3443
|
mergedHistory,
|
|
2076
3444
|
serverData.galleryItems || galleryItems,
|
|
2077
3445
|
serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
|
|
2078
|
-
serverData.workspaceTags || workspaceTags
|
|
3446
|
+
serverData.workspaceTags || workspaceTags,
|
|
3447
|
+
serverData.recentLabItemIds || recentLabItems.map((i) => i.id)
|
|
2079
3448
|
);
|
|
2080
3449
|
await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
|
|
2081
3450
|
await fetchServerProjects();
|
|
@@ -2086,12 +3455,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2086
3455
|
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
2087
3456
|
}
|
|
2088
3457
|
};
|
|
2089
|
-
(0,
|
|
3458
|
+
(0, import_react21.useEffect)(() => {
|
|
2090
3459
|
if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
|
|
2091
3460
|
}, [activeTab]);
|
|
2092
3461
|
if (isFullscreen && currentResult?.base64) {
|
|
2093
3462
|
const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
|
|
2094
|
-
return /* @__PURE__ */ (0,
|
|
3463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2095
3464
|
"div",
|
|
2096
3465
|
{
|
|
2097
3466
|
className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
|
|
@@ -2099,7 +3468,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2099
3468
|
onTouchMove: handleFsTouchMove,
|
|
2100
3469
|
onTouchEnd: handleFsTouchEnd,
|
|
2101
3470
|
children: [
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
3471
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2103
3472
|
"img",
|
|
2104
3473
|
{
|
|
2105
3474
|
src: fsBase64,
|
|
@@ -2116,76 +3485,76 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2116
3485
|
}
|
|
2117
3486
|
}
|
|
2118
3487
|
),
|
|
2119
|
-
/* @__PURE__ */ (0,
|
|
2120
|
-
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: () => {
|
|
2121
3490
|
setZoomScale(1);
|
|
2122
3491
|
setZoomOffset({ x: 0, y: 0 });
|
|
2123
|
-
}, 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,
|
|
2124
|
-
history.length > 1 && /* @__PURE__ */ (0,
|
|
2125
|
-
/* @__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: () => {
|
|
2126
3495
|
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
2127
|
-
}, 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,
|
|
2128
|
-
/* @__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: () => {
|
|
2129
3498
|
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
2130
|
-
}, 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,
|
|
2131
|
-
/* @__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: [
|
|
2132
3501
|
currentIndex + 1,
|
|
2133
3502
|
" / ",
|
|
2134
3503
|
history.length
|
|
2135
3504
|
] })
|
|
2136
3505
|
] }),
|
|
2137
|
-
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" })
|
|
2138
3507
|
]
|
|
2139
3508
|
}
|
|
2140
3509
|
);
|
|
2141
3510
|
}
|
|
2142
3511
|
if (showStart) {
|
|
2143
|
-
return /* @__PURE__ */ (0,
|
|
2144
|
-
/* @__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) => {
|
|
2145
3514
|
const f = e.target.files?.[0];
|
|
2146
3515
|
if (f) handleProjectImport(f);
|
|
2147
3516
|
e.target.value = "";
|
|
2148
3517
|
} }),
|
|
2149
|
-
/* @__PURE__ */ (0,
|
|
2150
|
-
/* @__PURE__ */ (0,
|
|
2151
|
-
/* @__PURE__ */ (0,
|
|
2152
|
-
/* @__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: [
|
|
2153
3522
|
"v",
|
|
2154
3523
|
LIB_VERSION
|
|
2155
3524
|
] })
|
|
2156
3525
|
] }),
|
|
2157
|
-
/* @__PURE__ */ (0,
|
|
3526
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2158
3527
|
"button",
|
|
2159
3528
|
{
|
|
2160
3529
|
onClick: toggleContrast,
|
|
2161
3530
|
className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
|
|
2162
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" },
|
|
2163
3532
|
children: [
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
/* @__PURE__ */ (0,
|
|
2166
|
-
/* @__PURE__ */ (0,
|
|
2167
|
-
/* @__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" })
|
|
2168
3537
|
] })
|
|
2169
3538
|
]
|
|
2170
3539
|
}
|
|
2171
3540
|
),
|
|
2172
|
-
/* @__PURE__ */ (0,
|
|
2173
|
-
/* @__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)(
|
|
2174
3543
|
"button",
|
|
2175
3544
|
{
|
|
2176
3545
|
onClick: () => wsInputRef.current?.click(),
|
|
2177
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",
|
|
2178
3547
|
style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
|
|
2179
3548
|
children: [
|
|
2180
|
-
/* @__PURE__ */ (0,
|
|
3549
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
|
|
2181
3550
|
projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
|
|
2182
3551
|
]
|
|
2183
3552
|
}
|
|
2184
3553
|
),
|
|
2185
|
-
!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" })
|
|
2186
3555
|
] }),
|
|
2187
|
-
onFetchServerProjects && /* @__PURE__ */ (0,
|
|
2188
|
-
/* @__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)(
|
|
2189
3558
|
"button",
|
|
2190
3559
|
{
|
|
2191
3560
|
disabled: isLoadingFromServer,
|
|
@@ -2206,35 +3575,35 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2206
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",
|
|
2207
3576
|
style: { height: 56, background: "#7c3aed" },
|
|
2208
3577
|
children: [
|
|
2209
|
-
/* @__PURE__ */ (0,
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
|
|
2210
3579
|
isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
|
|
2211
3580
|
]
|
|
2212
3581
|
}
|
|
2213
3582
|
),
|
|
2214
|
-
/* @__PURE__ */ (0,
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
|
|
2215
3584
|
] }),
|
|
2216
|
-
/* @__PURE__ */ (0,
|
|
2217
|
-
/* @__PURE__ */ (0,
|
|
2218
|
-
/* @__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: [
|
|
2219
3588
|
{ id: "mobile", icon: "smartphone", label: "Mobile" },
|
|
2220
3589
|
{ id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
|
|
2221
3590
|
{ id: "desktop", icon: "desktop_windows", label: "Desktop" },
|
|
2222
3591
|
{ id: "tablet-landscape", icon: "tablet", label: "Landscape" }
|
|
2223
|
-
].map((opt) => /* @__PURE__ */ (0,
|
|
3592
|
+
].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
2224
3593
|
"button",
|
|
2225
3594
|
{
|
|
2226
3595
|
onClick: () => startApp(opt.id),
|
|
2227
3596
|
className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
|
|
2228
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" },
|
|
2229
3598
|
children: [
|
|
2230
|
-
/* @__PURE__ */ (0,
|
|
2231
|
-
/* @__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 })
|
|
2232
3601
|
]
|
|
2233
3602
|
},
|
|
2234
3603
|
opt.id
|
|
2235
3604
|
)) }),
|
|
2236
|
-
layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0,
|
|
2237
|
-
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" })
|
|
2238
3607
|
] })
|
|
2239
3608
|
] });
|
|
2240
3609
|
}
|
|
@@ -2243,28 +3612,46 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2243
3612
|
const mdScale = mdMode ? window.innerWidth / 430 : 1;
|
|
2244
3613
|
const mdW = mdMode ? 430 : void 0;
|
|
2245
3614
|
const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
|
|
2246
|
-
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: {
|
|
2247
3616
|
width: mdMode ? mdW : "100vw",
|
|
2248
3617
|
height: mdMode ? mdH : "100dvh",
|
|
2249
3618
|
transform: mdMode ? `scale(${mdScale})` : void 0,
|
|
2250
3619
|
transformOrigin: mdMode ? "top left" : void 0,
|
|
2251
3620
|
...hcStyle || {}
|
|
2252
3621
|
}, children: [
|
|
2253
|
-
mobileTab === "
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
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" }) })
|
|
2265
3652
|
] }),
|
|
2266
|
-
/* @__PURE__ */ (0,
|
|
2267
|
-
/* @__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)(
|
|
2268
3655
|
"textarea",
|
|
2269
3656
|
{
|
|
2270
3657
|
value: activePrompt,
|
|
@@ -2274,26 +3661,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2274
3661
|
placeholder: "Prompt eingeben..."
|
|
2275
3662
|
}
|
|
2276
3663
|
),
|
|
2277
|
-
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" }) })
|
|
2278
3665
|
] }) }),
|
|
2279
|
-
/* @__PURE__ */ (0,
|
|
3666
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2280
3667
|
"button",
|
|
2281
3668
|
{
|
|
2282
3669
|
onClick: () => handleGenerateImage(),
|
|
2283
3670
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2284
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",
|
|
2285
3672
|
style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
|
|
2286
|
-
children: isGenerating ? /* @__PURE__ */ (0,
|
|
2287
|
-
/* @__PURE__ */ (0,
|
|
2288
|
-
/* @__PURE__ */ (0,
|
|
2289
|
-
] }) : /* @__PURE__ */ (0,
|
|
2290
|
-
/* @__PURE__ */ (0,
|
|
2291
|
-
/* @__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" })
|
|
2292
3679
|
] })
|
|
2293
3680
|
}
|
|
2294
3681
|
) }),
|
|
2295
|
-
/* @__PURE__ */ (0,
|
|
2296
|
-
/* @__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)(
|
|
2297
3684
|
"div",
|
|
2298
3685
|
{
|
|
2299
3686
|
className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
|
|
@@ -2307,25 +3694,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2307
3694
|
setTouchStartX(null);
|
|
2308
3695
|
},
|
|
2309
3696
|
children: [
|
|
2310
|
-
currentResult?.status === "processing" && /* @__PURE__ */ (0,
|
|
2311
|
-
/* @__PURE__ */ (0,
|
|
2312
|
-
/* @__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..." })
|
|
2313
3700
|
] }),
|
|
2314
|
-
currentResult?.status === "error" && /* @__PURE__ */ (0,
|
|
2315
|
-
/* @__PURE__ */ (0,
|
|
2316
|
-
/* @__PURE__ */ (0,
|
|
2317
|
-
/* @__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" })
|
|
2318
3705
|
] }),
|
|
2319
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0,
|
|
2320
|
-
!currentResult && /* @__PURE__ */ (0,
|
|
2321
|
-
/* @__PURE__ */ (0,
|
|
2322
|
-
/* @__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" })
|
|
2323
3710
|
] }),
|
|
2324
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2325
|
-
history.length > 1 && currentResult && /* @__PURE__ */ (0,
|
|
2326
|
-
/* @__PURE__ */ (0,
|
|
2327
|
-
/* @__PURE__ */ (0,
|
|
2328
|
-
/* @__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: [
|
|
2329
3716
|
currentIndex + 1,
|
|
2330
3717
|
" / ",
|
|
2331
3718
|
history.length
|
|
@@ -2334,30 +3721,30 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2334
3721
|
]
|
|
2335
3722
|
}
|
|
2336
3723
|
),
|
|
2337
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2338
|
-
/* @__PURE__ */ (0,
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2340
|
-
/* @__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" })
|
|
2341
3728
|
] }),
|
|
2342
|
-
/* @__PURE__ */ (0,
|
|
2343
|
-
/* @__PURE__ */ (0,
|
|
2344
|
-
/* @__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" })
|
|
2345
3732
|
] }),
|
|
2346
|
-
/* @__PURE__ */ (0,
|
|
2347
|
-
/* @__PURE__ */ (0,
|
|
2348
|
-
/* @__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" })
|
|
2349
3736
|
] })
|
|
2350
3737
|
] })
|
|
2351
3738
|
] })
|
|
2352
3739
|
] }),
|
|
2353
|
-
mobileTab === "browse" && /* @__PURE__ */ (0,
|
|
2354
|
-
/* @__PURE__ */ (0,
|
|
2355
|
-
/* @__PURE__ */ (0,
|
|
2356
|
-
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) => {
|
|
2357
3744
|
setCurrentResult(g);
|
|
2358
3745
|
setMobileTab("stage");
|
|
2359
3746
|
}, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
2360
|
-
activeTab === "gallery" && /* @__PURE__ */ (0,
|
|
3747
|
+
activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2361
3748
|
MediaLibrary,
|
|
2362
3749
|
{
|
|
2363
3750
|
items: galleryItems,
|
|
@@ -2377,38 +3764,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2377
3764
|
}
|
|
2378
3765
|
}
|
|
2379
3766
|
),
|
|
2380
|
-
activeTab === "inspect" && /* @__PURE__ */ (0,
|
|
3767
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
|
|
2381
3768
|
setCurrentResult(g);
|
|
2382
3769
|
} })
|
|
2383
3770
|
] })
|
|
2384
3771
|
] }),
|
|
2385
|
-
|
|
2386
|
-
/* @__PURE__ */ (0,
|
|
2387
|
-
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: () => {
|
|
2388
3775
|
setLeftTab("prompt");
|
|
2389
3776
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2390
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: [
|
|
2391
|
-
/* @__PURE__ */ (0,
|
|
3778
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
|
|
2392
3779
|
"Prompt"
|
|
2393
3780
|
] }),
|
|
2394
|
-
/* @__PURE__ */ (0,
|
|
3781
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => {
|
|
2395
3782
|
setLeftTab("hierarchy");
|
|
2396
3783
|
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2397
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: [
|
|
2398
|
-
/* @__PURE__ */ (0,
|
|
3785
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
|
|
2399
3786
|
"Hierarchie"
|
|
2400
3787
|
] }),
|
|
2401
|
-
/* @__PURE__ */ (0,
|
|
2402
|
-
/* @__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" }),
|
|
2403
3790
|
"Setup"
|
|
2404
3791
|
] }),
|
|
2405
|
-
/* @__PURE__ */ (0,
|
|
2406
|
-
/* @__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" }),
|
|
2407
3794
|
"Sync"
|
|
2408
|
-
] })
|
|
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" }) })
|
|
2409
3797
|
] }),
|
|
2410
|
-
/* @__PURE__ */ (0,
|
|
2411
|
-
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)(
|
|
2412
3800
|
ListView,
|
|
2413
3801
|
{
|
|
2414
3802
|
nodes,
|
|
@@ -2439,12 +3827,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2439
3827
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2440
3828
|
}
|
|
2441
3829
|
) }),
|
|
2442
|
-
leftTab === "prompt" &&
|
|
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) => {
|
|
2443
3831
|
handleGenerateImage(prompt);
|
|
2444
3832
|
setMobileTab("stage");
|
|
2445
|
-
}, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }),
|
|
2446
|
-
activeTab === "setup" && /* @__PURE__ */ (0,
|
|
2447
|
-
activeTab === "sync" && /* @__PURE__ */ (0,
|
|
3833
|
+
}, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
|
|
3834
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
3835
|
+
activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2448
3836
|
ProjectSyncTab,
|
|
2449
3837
|
{
|
|
2450
3838
|
onProjectExport: handleProjectExport,
|
|
@@ -2459,20 +3847,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2459
3847
|
onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
|
|
2460
3848
|
onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
|
|
2461
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
|
+
}
|
|
2462
3861
|
)
|
|
2463
3862
|
] })
|
|
2464
3863
|
] }),
|
|
2465
|
-
/* @__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: [
|
|
2466
3865
|
{ id: "tools", icon: "auto_fix_high", label: "Prompt" },
|
|
2467
3866
|
{ id: "stage", icon: "palette", label: "Stage" },
|
|
3867
|
+
{ id: "labs", icon: "science", label: "Labs" },
|
|
3868
|
+
...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
|
|
2468
3869
|
{ id: "browse", icon: "photo_library", label: "Galerie" }
|
|
2469
|
-
].map((tab) => /* @__PURE__ */ (0,
|
|
2470
|
-
/* @__PURE__ */ (0,
|
|
2471
|
-
/* @__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 })
|
|
2472
3873
|
] }, tab.id)) })
|
|
2473
3874
|
] });
|
|
2474
3875
|
if (mdMode) {
|
|
2475
|
-
return /* @__PURE__ */ (0,
|
|
3876
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
|
|
2476
3877
|
}
|
|
2477
3878
|
return mobileRoot;
|
|
2478
3879
|
}
|
|
@@ -2480,17 +3881,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2480
3881
|
const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
|
|
2481
3882
|
const tlW = 920;
|
|
2482
3883
|
const tlH = 520;
|
|
2483
|
-
return /* @__PURE__ */ (0,
|
|
2484
|
-
/* @__PURE__ */ (0,
|
|
2485
|
-
/* @__PURE__ */ (0,
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2487
|
-
/* @__PURE__ */ (0,
|
|
2488
|
-
/* @__PURE__ */ (0,
|
|
2489
|
-
/* @__PURE__ */ (0,
|
|
2490
|
-
/* @__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" }) })
|
|
2491
3892
|
] }),
|
|
2492
|
-
/* @__PURE__ */ (0,
|
|
2493
|
-
/* @__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)(
|
|
2494
3895
|
"textarea",
|
|
2495
3896
|
{
|
|
2496
3897
|
value: activePrompt,
|
|
@@ -2499,27 +3900,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2499
3900
|
placeholder: "Prompt eingeben..."
|
|
2500
3901
|
}
|
|
2501
3902
|
),
|
|
2502
|
-
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" }) })
|
|
2503
3904
|
] }) }),
|
|
2504
|
-
/* @__PURE__ */ (0,
|
|
3905
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2505
3906
|
"button",
|
|
2506
3907
|
{
|
|
2507
3908
|
onClick: () => handleGenerateImage(),
|
|
2508
3909
|
disabled: !activePrompt.trim() || isGenerating,
|
|
2509
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" },
|
|
2510
|
-
children: isGenerating ? /* @__PURE__ */ (0,
|
|
2511
|
-
/* @__PURE__ */ (0,
|
|
2512
|
-
/* @__PURE__ */ (0,
|
|
2513
|
-
] }) : /* @__PURE__ */ (0,
|
|
2514
|
-
/* @__PURE__ */ (0,
|
|
2515
|
-
/* @__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" })
|
|
2516
3917
|
] })
|
|
2517
3918
|
}
|
|
2518
3919
|
) }),
|
|
2519
|
-
/* @__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)) }) })
|
|
2520
3921
|
] }),
|
|
2521
|
-
/* @__PURE__ */ (0,
|
|
2522
|
-
/* @__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)(
|
|
2523
3924
|
"div",
|
|
2524
3925
|
{
|
|
2525
3926
|
style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
|
|
@@ -2531,26 +3932,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2531
3932
|
else if (dx > 50) goToPrev();
|
|
2532
3933
|
setTouchStartX(null);
|
|
2533
3934
|
},
|
|
2534
|
-
children: /* @__PURE__ */ (0,
|
|
2535
|
-
currentResult?.status === "processing" && /* @__PURE__ */ (0,
|
|
2536
|
-
/* @__PURE__ */ (0,
|
|
2537
|
-
/* @__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..." })
|
|
2538
3939
|
] }),
|
|
2539
|
-
currentResult?.status === "error" && /* @__PURE__ */ (0,
|
|
2540
|
-
/* @__PURE__ */ (0,
|
|
2541
|
-
/* @__PURE__ */ (0,
|
|
2542
|
-
/* @__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" })
|
|
2543
3944
|
] }),
|
|
2544
|
-
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0,
|
|
2545
|
-
!currentResult && /* @__PURE__ */ (0,
|
|
2546
|
-
/* @__PURE__ */ (0,
|
|
2547
|
-
/* @__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" })
|
|
2548
3949
|
] }),
|
|
2549
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2550
|
-
history.length > 1 && currentResult && /* @__PURE__ */ (0,
|
|
2551
|
-
/* @__PURE__ */ (0,
|
|
2552
|
-
/* @__PURE__ */ (0,
|
|
2553
|
-
/* @__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: [
|
|
2554
3955
|
currentIndex + 1,
|
|
2555
3956
|
" / ",
|
|
2556
3957
|
history.length
|
|
@@ -2559,41 +3960,57 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2559
3960
|
] })
|
|
2560
3961
|
}
|
|
2561
3962
|
),
|
|
2562
|
-
currentResult?.status === "done" && /* @__PURE__ */ (0,
|
|
2563
|
-
/* @__PURE__ */ (0,
|
|
2564
|
-
/* @__PURE__ */ (0,
|
|
2565
|
-
/* @__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" })
|
|
2566
3967
|
] }),
|
|
2567
|
-
/* @__PURE__ */ (0,
|
|
2568
|
-
/* @__PURE__ */ (0,
|
|
2569
|
-
/* @__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" })
|
|
2570
3971
|
] }),
|
|
2571
|
-
/* @__PURE__ */ (0,
|
|
2572
|
-
/* @__PURE__ */ (0,
|
|
2573
|
-
/* @__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" })
|
|
2574
3975
|
] })
|
|
2575
3976
|
] })
|
|
2576
3977
|
] })
|
|
2577
3978
|
] }) });
|
|
2578
3979
|
}
|
|
2579
|
-
return /* @__PURE__ */ (0,
|
|
2580
|
-
/* @__PURE__ */ (0,
|
|
2581
|
-
/* @__PURE__ */ (0,
|
|
2582
|
-
/* @__PURE__ */ (0,
|
|
2583
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
2584
|
-
workspaceTags && /* @__PURE__ */ (0,
|
|
2585
|
-
/* @__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" }),
|
|
2586
3987
|
"Prompt"
|
|
2587
3988
|
] }),
|
|
2588
|
-
/* @__PURE__ */ (0,
|
|
2589
|
-
/* @__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" }),
|
|
2590
3991
|
"Hierarchie"
|
|
2591
|
-
] })
|
|
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" }) })
|
|
2592
3994
|
] }),
|
|
2593
|
-
/* @__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" })
|
|
2594
3996
|
] }),
|
|
2595
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
2596
|
-
|
|
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)(
|
|
2597
4014
|
ListView,
|
|
2598
4015
|
{
|
|
2599
4016
|
nodes,
|
|
@@ -2618,73 +4035,111 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2618
4035
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2619
4036
|
}
|
|
2620
4037
|
) }),
|
|
2621
|
-
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 })
|
|
2622
4039
|
] })
|
|
2623
4040
|
] }),
|
|
2624
|
-
/* @__PURE__ */ (0,
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
/* @__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" }] })
|
|
2629
4047
|
] }),
|
|
2630
|
-
/* @__PURE__ */ (0,
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
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" })
|
|
2638
4074
|
] }),
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2640
|
-
/* @__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" })
|
|
2641
4077
|
] })
|
|
2642
4078
|
] }),
|
|
2643
|
-
/* @__PURE__ */ (0,
|
|
2644
|
-
!isPromptCollapsed && /* @__PURE__ */ (0,
|
|
2645
|
-
/* @__PURE__ */ (0,
|
|
2646
|
-
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" }) })
|
|
2647
4083
|
] }) }),
|
|
2648
|
-
/* @__PURE__ */ (0,
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
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..." })
|
|
2652
4091
|
] }),
|
|
2653
|
-
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0,
|
|
2654
|
-
/* @__PURE__ */ (0,
|
|
2655
|
-
/* @__PURE__ */ (0,
|
|
2656
|
-
] }) : currentResult.status === "error" ? /* @__PURE__ */ (0,
|
|
2657
|
-
/* @__PURE__ */ (0,
|
|
2658
|
-
/* @__PURE__ */ (0,
|
|
2659
|
-
/* @__PURE__ */ (0,
|
|
2660
|
-
/* @__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 })
|
|
2661
4100
|
] }),
|
|
2662
|
-
/* @__PURE__ */ (0,
|
|
2663
|
-
] }) : /* @__PURE__ */ (0,
|
|
2664
|
-
/* @__PURE__ */ (0,
|
|
2665
|
-
/* @__PURE__ */ (0,
|
|
2666
|
-
/* @__PURE__ */ (0,
|
|
2667
|
-
/* @__PURE__ */ (0,
|
|
2668
|
-
/* @__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" })
|
|
2669
4108
|
] })
|
|
2670
|
-
] }) : /* @__PURE__ */ (0,
|
|
2671
|
-
/* @__PURE__ */ (0,
|
|
2672
|
-
/* @__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" })
|
|
2673
4112
|
] })
|
|
2674
4113
|
] }) })
|
|
2675
4114
|
] })
|
|
2676
4115
|
] }),
|
|
2677
|
-
/* @__PURE__ */ (0,
|
|
2678
|
-
|
|
2679
|
-
|
|
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: () => {
|
|
2680
4120
|
setActiveTab(tab);
|
|
2681
4121
|
setIsRightCollapsed(false);
|
|
2682
|
-
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0,
|
|
2683
|
-
/* @__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" }) })
|
|
2684
4124
|
] }),
|
|
2685
|
-
!isRightCollapsed && /* @__PURE__ */ (0,
|
|
2686
|
-
activeTab === "
|
|
2687
|
-
|
|
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)(
|
|
2688
4143
|
MediaLibrary,
|
|
2689
4144
|
{
|
|
2690
4145
|
items: galleryItems,
|
|
@@ -2698,9 +4153,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2698
4153
|
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
2699
4154
|
}
|
|
2700
4155
|
),
|
|
2701
|
-
activeTab === "inspect" && /* @__PURE__ */ (0,
|
|
2702
|
-
activeTab === "setup" && /* @__PURE__ */ (0,
|
|
2703
|
-
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)(
|
|
2704
4159
|
ProjectSyncTab,
|
|
2705
4160
|
{
|
|
2706
4161
|
onProjectExport: handleProjectExport,
|
|
@@ -2722,7 +4177,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
2722
4177
|
}
|
|
2723
4178
|
|
|
2724
4179
|
// src/index.ts
|
|
2725
|
-
var LIB_VERSION = "1.1
|
|
4180
|
+
var LIB_VERSION = "1.2.1";
|
|
2726
4181
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2727
4182
|
0 && (module.exports = {
|
|
2728
4183
|
AvatarArchitectApp,
|
|
@@ -2733,6 +4188,12 @@ var LIB_VERSION = "1.1.3";
|
|
|
2733
4188
|
HistoryPanel,
|
|
2734
4189
|
InspectPanel,
|
|
2735
4190
|
LIB_VERSION,
|
|
4191
|
+
LabBlend,
|
|
4192
|
+
LabCompare,
|
|
4193
|
+
LabImagePicker,
|
|
4194
|
+
LabLoop,
|
|
4195
|
+
LabRemix,
|
|
4196
|
+
LabsTab,
|
|
2736
4197
|
ListView,
|
|
2737
4198
|
MediaLibrary,
|
|
2738
4199
|
PillButton,
|
|
@@ -2740,15 +4201,25 @@ var LIB_VERSION = "1.1.3";
|
|
|
2740
4201
|
PromptTab,
|
|
2741
4202
|
SectionLabel,
|
|
2742
4203
|
SetupPanel,
|
|
4204
|
+
TagManagerPanel,
|
|
4205
|
+
autoLabel,
|
|
4206
|
+
buildBlendInstruction,
|
|
4207
|
+
buildCompareInstruction,
|
|
2743
4208
|
buildFallbackPrompt,
|
|
2744
4209
|
buildGenerationPrompt,
|
|
2745
4210
|
buildImageGenerationOptions,
|
|
4211
|
+
buildLoopInstruction,
|
|
2746
4212
|
buildPromptTabPayload,
|
|
4213
|
+
buildReferenceImageMediaIds,
|
|
4214
|
+
buildRemixInstruction,
|
|
4215
|
+
buildScanInstruction,
|
|
2747
4216
|
cleanAiResponse,
|
|
2748
4217
|
createFlowServices,
|
|
2749
4218
|
exportProjectToZip,
|
|
2750
4219
|
formatTreeToMarkdown,
|
|
4220
|
+
frameToGeneration,
|
|
2751
4221
|
getFormattedTimestamp,
|
|
4222
|
+
groupGenerationsToLabItems,
|
|
2752
4223
|
importProjectFromZip,
|
|
2753
4224
|
injectXMPMetadata,
|
|
2754
4225
|
interpretSdkError,
|