@rslsp1/fa-app-tools 1.0.2 → 1.0.4
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 +102 -9
- package/dist/index.d.ts +102 -9
- package/dist/index.js +1599 -261
- package/dist/index.mjs +1581 -246
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AvatarArchitectApp: () => AvatarArchitectApp,
|
|
34
|
+
CollapsibleCard: () => CollapsibleCard,
|
|
34
35
|
CompactDropdown: () => CompactDropdown,
|
|
35
36
|
FaToolsBadge: () => FaToolsBadge,
|
|
36
37
|
GLOBAL_STYLES: () => GLOBAL_STYLES,
|
|
@@ -39,6 +40,7 @@ __export(index_exports, {
|
|
|
39
40
|
ListView: () => ListView,
|
|
40
41
|
MediaLibrary: () => MediaLibrary,
|
|
41
42
|
PillButton: () => PillButton,
|
|
43
|
+
ProjectSyncTab: () => ProjectSyncTab,
|
|
42
44
|
PromptTab: () => PromptTab,
|
|
43
45
|
SectionLabel: () => SectionLabel,
|
|
44
46
|
SetupPanel: () => SetupPanel,
|
|
@@ -47,6 +49,7 @@ __export(index_exports, {
|
|
|
47
49
|
buildImageGenerationOptions: () => buildImageGenerationOptions,
|
|
48
50
|
buildPromptTabPayload: () => buildPromptTabPayload,
|
|
49
51
|
cleanAiResponse: () => cleanAiResponse,
|
|
52
|
+
createFlowServices: () => createFlowServices,
|
|
50
53
|
exportProjectToZip: () => exportProjectToZip,
|
|
51
54
|
formatTreeToMarkdown: () => formatTreeToMarkdown,
|
|
52
55
|
getFormattedTimestamp: () => getFormattedTimestamp,
|
|
@@ -259,7 +262,7 @@ function generateMarkdownReport(nodes, history) {
|
|
|
259
262
|
});
|
|
260
263
|
return md;
|
|
261
264
|
}
|
|
262
|
-
async function exportProjectToZip(nodes, edges, history, galleryItems, settings) {
|
|
265
|
+
async function exportProjectToZip(nodes, edges, history, galleryItems, settings, workspaceTags) {
|
|
263
266
|
const zip = new import_jszip.default();
|
|
264
267
|
const projectData = {
|
|
265
268
|
nodes,
|
|
@@ -267,6 +270,7 @@ async function exportProjectToZip(nodes, edges, history, galleryItems, settings)
|
|
|
267
270
|
history: history.map((h) => ({ ...h, base64: void 0 })),
|
|
268
271
|
galleryItems: galleryItems.map((g) => ({ ...g, base64: void 0 })),
|
|
269
272
|
settings,
|
|
273
|
+
workspaceTags: workspaceTags || null,
|
|
270
274
|
version: "1.2.6"
|
|
271
275
|
};
|
|
272
276
|
zip.file("project.json", JSON.stringify(projectData, null, 2));
|
|
@@ -355,14 +359,44 @@ function buildImageGenerationOptions(prompt, aspectRatio, model, referenceMediaI
|
|
|
355
359
|
}
|
|
356
360
|
return options;
|
|
357
361
|
}
|
|
358
|
-
|
|
362
|
+
var CONTENT_FIRST = ["subject", "clothing", "environment", "mood", "action", "content", "general"];
|
|
363
|
+
var RULES_LAST = ["system", "rules", "safety", "generated", "source", "status", "type", "model"];
|
|
364
|
+
function categoryZone(cat) {
|
|
365
|
+
const c = cat.toLowerCase();
|
|
366
|
+
if (CONTENT_FIRST.some((k) => c.includes(k))) return 0;
|
|
367
|
+
if (RULES_LAST.some((k) => c.includes(k))) return 2;
|
|
368
|
+
return 1;
|
|
369
|
+
}
|
|
370
|
+
var ZONE_SEPARATORS = { 1: "\u2500\u2500\u2500\u2500 STYLE \u2500\u2500\u2500\u2500", 2: "\u2500\u2500\u2500\u2500 SYSTEM / RULES \u2500\u2500\u2500\u2500" };
|
|
371
|
+
function buildPromptTabPayload(selectedTags, instructions, rules, treeText) {
|
|
359
372
|
const parts = [];
|
|
360
373
|
if (treeText) parts.push(`HIERARCHY:
|
|
361
374
|
${treeText}`);
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
375
|
+
if (selectedTags.length > 0) {
|
|
376
|
+
const sorted = [...selectedTags].sort((a, b) => categoryZone(a.category) - categoryZone(b.category) || a.category.localeCompare(b.category));
|
|
377
|
+
const byCategory = sorted.reduce((acc, t) => {
|
|
378
|
+
(acc[t.category] = acc[t.category] || []).push(t);
|
|
379
|
+
return acc;
|
|
380
|
+
}, {});
|
|
381
|
+
const tagParts = [];
|
|
382
|
+
let lastZone = -1;
|
|
383
|
+
for (const [cat, tags] of Object.entries(byCategory)) {
|
|
384
|
+
const zone = categoryZone(cat);
|
|
385
|
+
if (lastZone >= 0 && zone > lastZone && ZONE_SEPARATORS[zone]) {
|
|
386
|
+
tagParts.push(ZONE_SEPARATORS[zone]);
|
|
387
|
+
}
|
|
388
|
+
tagParts.push(...tags.map((t) => `[${cat.toUpperCase()}]
|
|
389
|
+
${t.value}`));
|
|
390
|
+
lastZone = zone;
|
|
391
|
+
}
|
|
392
|
+
parts.push(`SELECTED TAGS:
|
|
393
|
+
|
|
394
|
+
${tagParts.join("\n\n")}`);
|
|
395
|
+
}
|
|
396
|
+
if (instructions.trim()) parts.push(`DESCRIPTION:
|
|
365
397
|
${instructions.trim()}`);
|
|
398
|
+
if (rules.trim()) parts.push(`GENERATION RULES:
|
|
399
|
+
${rules.trim()}`);
|
|
366
400
|
return [
|
|
367
401
|
"Create a high-quality image generation prompt based on the following inputs. Translate everything to English.",
|
|
368
402
|
`Return a JSON object with exactly two keys: "prompt" (the prompt string) and "feedback" (a short note on your choices in the user's language, or null).`,
|
|
@@ -382,6 +416,33 @@ function parsePromptResponse(raw) {
|
|
|
382
416
|
}
|
|
383
417
|
return { prompt: cleaned, feedback: null };
|
|
384
418
|
}
|
|
419
|
+
function createFlowServices(Flow) {
|
|
420
|
+
return {
|
|
421
|
+
generateImage: async (options) => {
|
|
422
|
+
try {
|
|
423
|
+
const result = await Flow.generate.image(options);
|
|
424
|
+
if (!result?.base64) throw new Error("Keine Bilddaten vom SDK erhalten.");
|
|
425
|
+
return { base64: result.base64, mediaId: result.mediaId };
|
|
426
|
+
} catch (err) {
|
|
427
|
+
throw interpretSdkError(err);
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
generateText: async (hierarchyText, mode = "creative") => {
|
|
431
|
+
const prompt = buildGenerationPrompt(hierarchyText, mode);
|
|
432
|
+
try {
|
|
433
|
+
const response = await Flow.generate.text(prompt, {
|
|
434
|
+
thinkingLevel: "low",
|
|
435
|
+
systemInstruction: "You are an expert prompt engineer. Always return ONLY the generated prompt text, no headers, no quotes, no conversational filler."
|
|
436
|
+
});
|
|
437
|
+
const result = response?.text?.trim() || "";
|
|
438
|
+
if (!result) throw new Error("Empty AI response");
|
|
439
|
+
return cleanAiResponse(result);
|
|
440
|
+
} catch {
|
|
441
|
+
return buildFallbackPrompt(hierarchyText);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
}
|
|
385
446
|
function interpretSdkError(err) {
|
|
386
447
|
const msg = err?.message || "Generierung fehlgeschlagen";
|
|
387
448
|
if (msg.includes("PUBLIC_ERROR_SOMETHING_WENT_WRONG")) {
|
|
@@ -591,9 +652,11 @@ var HistoryPanel = ({ history, currentResultId, onSelect, onDelete }) => {
|
|
|
591
652
|
};
|
|
592
653
|
|
|
593
654
|
// src/components/InspectPanel.tsx
|
|
594
|
-
var import_react5 = require("
|
|
655
|
+
var import_react5 = require("react");
|
|
656
|
+
var import_react6 = require("motion/react");
|
|
595
657
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
596
658
|
var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagToggle }) => {
|
|
659
|
+
const currentIndex = (0, import_react5.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
|
|
597
660
|
if (!currentResult) {
|
|
598
661
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col items-center justify-center py-20 text-center gap-4 opacity-10", children: [
|
|
599
662
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "info" }),
|
|
@@ -601,11 +664,22 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
|
|
|
601
664
|
] });
|
|
602
665
|
}
|
|
603
666
|
const fullDateStr = new Date(currentResult.timestamp).toLocaleString([], { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
604
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
667
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react6.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 flex flex-col overflow-hidden", children: [
|
|
605
668
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-shrink-0 border-b border-white/5 bg-black/20 py-3 px-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex gap-2 overflow-x-auto no-scrollbar pb-1", children: history.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => onSelect(gen), className: `w-10 h-10 rounded-lg overflow-hidden border shrink-0 transition-all ${currentResult.id === gen.id ? "border-white scale-105 shadow-lg" : "border-white/5 opacity-40 hover:opacity-100"}`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: gen.base64 ? gen.base64.startsWith("data:") ? gen.base64 : `data:image/png;base64,${gen.base64}` : "", className: "w-full h-full object-cover", alt: "Thumbnail" }) }, gen.id)) }) }),
|
|
606
669
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 overflow-y-auto p-4 flex flex-col gap-6 dark-scrollbar pb-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
607
670
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Vorschau" }),
|
|
608
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.
|
|
671
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "aspect-square w-full rounded-2xl bg-black overflow-hidden border border-white/10 shadow-2xl relative", children: [
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: currentResult.base64 ? currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}` : "", className: "w-full h-full object-cover", alt: "Preview" }),
|
|
673
|
+
history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => currentIndex > 0 && onSelect(history[currentIndex - 1]), 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_runtime6.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: () => currentIndex < history.length - 1 && onSelect(history[currentIndex + 1]), 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_runtime6.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.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: [
|
|
677
|
+
currentIndex + 1,
|
|
678
|
+
" / ",
|
|
679
|
+
history.length
|
|
680
|
+
] })
|
|
681
|
+
] })
|
|
682
|
+
] }),
|
|
609
683
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-4 bg-white/5 rounded-2xl border border-white/5", children: [
|
|
610
684
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[8px] font-bold text-white/20 uppercase mb-2 tracking-widest", children: "Prompt Details" }),
|
|
611
685
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "text-[10px] text-white/70 italic leading-relaxed font-medium", children: [
|
|
@@ -643,13 +717,12 @@ var InspectPanel = ({ currentResult, history, onSelect, workspaceTags, onTagTogg
|
|
|
643
717
|
};
|
|
644
718
|
|
|
645
719
|
// src/components/SetupPanel.tsx
|
|
646
|
-
var
|
|
647
|
-
var
|
|
720
|
+
var import_react7 = require("react");
|
|
721
|
+
var import_react8 = require("motion/react");
|
|
648
722
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
649
|
-
var SetupPanel = ({ onWorkspaceImport,
|
|
650
|
-
const workspaceInputRef = (0,
|
|
651
|
-
|
|
652
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_react7.motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, className: "absolute inset-0 p-6 flex flex-col gap-10", children: [
|
|
723
|
+
var SetupPanel = ({ onWorkspaceImport, buildInfo }) => {
|
|
724
|
+
const workspaceInputRef = (0, import_react7.useRef)(null);
|
|
725
|
+
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", children: [
|
|
653
726
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
654
727
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
655
728
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Workspace Management" }),
|
|
@@ -662,37 +735,22 @@ var SetupPanel = ({ onWorkspaceImport, onProjectExport, onProjectImport, project
|
|
|
662
735
|
e.target.value = "";
|
|
663
736
|
} })
|
|
664
737
|
] }),
|
|
665
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
666
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-1", children: [
|
|
667
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(SectionLabel, { children: "Projekt Management" }),
|
|
668
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-[9px] text-white/30 px-2 italic", children: "Speichere den gesamten Baum und alle Assets." })
|
|
669
|
-
] }),
|
|
670
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
671
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "filled", icon: "download", onClick: onProjectExport, loading: projectActionState === "working", children: "Exportieren (.zip)" }),
|
|
672
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "Projekt laden (.zip)" }),
|
|
673
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref: projectInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
|
|
674
|
-
const f = e.target.files?.[0];
|
|
675
|
-
if (f) onProjectImport(f);
|
|
676
|
-
e.target.value = "";
|
|
677
|
-
} })
|
|
678
|
-
] })
|
|
679
|
-
] }),
|
|
680
738
|
/* @__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: [
|
|
681
739
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-[8px] font-bold text-white/20 uppercase tracking-widest", children: "System Info" }),
|
|
682
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
|
|
683
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Version" }),
|
|
684
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "1.2.0" })
|
|
685
|
-
] }),
|
|
686
740
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
|
|
687
741
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Engine" }),
|
|
688
742
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: "Avatar Architect" })
|
|
743
|
+
] }),
|
|
744
|
+
buildInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center justify-between text-[9px] text-white/40", children: [
|
|
745
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: "Build" }),
|
|
746
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "font-mono", children: buildInfo })
|
|
689
747
|
] })
|
|
690
748
|
] })
|
|
691
749
|
] });
|
|
692
750
|
};
|
|
693
751
|
|
|
694
752
|
// src/components/MediaLibrary.tsx
|
|
695
|
-
var
|
|
753
|
+
var import_react9 = require("motion/react");
|
|
696
754
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
697
755
|
var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, onBatchDownload, onGenerateReference }) => {
|
|
698
756
|
const selectedCount = items.filter((i) => i.selectedForExport).length;
|
|
@@ -711,7 +769,7 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
|
|
|
711
769
|
"Laden"
|
|
712
770
|
] })
|
|
713
771
|
] }),
|
|
714
|
-
selectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
772
|
+
selectedCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react9.motion.div, { initial: { opacity: 0, y: -10 }, animate: { opacity: 1, y: 0 }, className: "flex items-center justify-between bg-blue-500/10 border border-blue-500/20 p-2 rounded-xl", children: [
|
|
715
773
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[9px] font-bold uppercase text-blue-400 ml-1", children: [
|
|
716
774
|
selectedCount,
|
|
717
775
|
" ausgew\xE4hlt"
|
|
@@ -728,7 +786,7 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
|
|
|
728
786
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[12px] font-bold uppercase tracking-widest", children: "Keine Medien" }),
|
|
729
787
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[10px] italic", children: "Importiere Assets aus deinem Projekt." })
|
|
730
788
|
] })
|
|
731
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
789
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "grid grid-cols-2 gap-3 pb-10", children: items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react9.motion.div, { initial: { opacity: 0, scale: 0.9 }, animate: { opacity: 1, scale: 1 }, className: `relative aspect-square group/item rounded-xl overflow-hidden border transition-all bg-white/5 cursor-pointer shadow-lg ${item.selectedForExport ? "border-blue-500 shadow-[0_0_15px_rgba(59,130,246,0.2)]" : "border-white/10 opacity-70 hover:opacity-100"}`, onClick: () => onSelect?.(item), children: [
|
|
732
790
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: item.base64 ? item.base64.startsWith("data:") ? item.base64 : `data:image/png;base64,${item.base64}` : "", className: "w-full h-full object-cover transition-transform duration-500 group-hover/item:scale-110", alt: item.prompt }),
|
|
733
791
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: `absolute top-2 left-2 w-5 h-5 rounded-md border flex items-center justify-center transition-all z-30 pointer-events-auto ${item.selectedForExport ? "bg-blue-500 border-blue-400" : "bg-black/60 border-white/20"}`, onClick: (e) => {
|
|
734
792
|
e.stopPropagation();
|
|
@@ -753,10 +811,10 @@ var MediaLibrary = ({ items, onImport, onDelete, onSelect, onToggleSelection, on
|
|
|
753
811
|
};
|
|
754
812
|
|
|
755
813
|
// src/components/ListView.tsx
|
|
756
|
-
var
|
|
814
|
+
var import_react10 = require("react");
|
|
757
815
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
758
816
|
var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, isActive, isInPath, onFocus, onGenerate, onGenerateBranch, onGenerateSubtree, isGenerating, isCollapsed, toggleCollapse, renderNode, children }) => {
|
|
759
|
-
const inputRef = (0,
|
|
817
|
+
const inputRef = (0, import_react10.useRef)(null);
|
|
760
818
|
const hasChildren = children && children.length > 0;
|
|
761
819
|
const handleKeyDown = (e) => {
|
|
762
820
|
if (e.key === "Tab") {
|
|
@@ -784,7 +842,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
|
|
|
784
842
|
onMoveNode(node.id, "down");
|
|
785
843
|
}
|
|
786
844
|
};
|
|
787
|
-
(0,
|
|
845
|
+
(0, import_react10.useEffect)(() => {
|
|
788
846
|
if (isActive && inputRef.current) inputRef.current.focus();
|
|
789
847
|
}, [isActive]);
|
|
790
848
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col w-full", children: [
|
|
@@ -818,7 +876,7 @@ var ListNode = ({ node, depth, onNodeChange, onAddChild, onDeleteNode, onMoveNod
|
|
|
818
876
|
] });
|
|
819
877
|
};
|
|
820
878
|
function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMoveNode, onIndentNode, onOutdentNode, onAddSibling, focusedNodeId, onFocus, activePath, onGenerate, onGenerateBranch, onGenerateSubtree, isGeneratingNodeId }) {
|
|
821
|
-
const [collapsed, setCollapsed] = (0,
|
|
879
|
+
const [collapsed, setCollapsed] = (0, import_react10.useState)(/* @__PURE__ */ new Set());
|
|
822
880
|
const toggleCollapse = (id) => {
|
|
823
881
|
setCollapsed((prev) => {
|
|
824
882
|
const next = new Set(prev);
|
|
@@ -846,17 +904,84 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
|
|
|
846
904
|
}
|
|
847
905
|
|
|
848
906
|
// src/components/AvatarArchitectApp.tsx
|
|
849
|
-
var
|
|
850
|
-
var import_react13 = require("motion/react");
|
|
907
|
+
var import_react14 = require("react");
|
|
851
908
|
|
|
852
909
|
// src/components/PromptTab.tsx
|
|
853
|
-
var
|
|
854
|
-
|
|
910
|
+
var import_react12 = require("react");
|
|
911
|
+
|
|
912
|
+
// src/components/CollapsibleCard.tsx
|
|
913
|
+
var import_react11 = require("react");
|
|
855
914
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
856
|
-
var
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
915
|
+
var CollapsibleCard = ({
|
|
916
|
+
title,
|
|
917
|
+
icon,
|
|
918
|
+
actions,
|
|
919
|
+
children,
|
|
920
|
+
defaultOpen = true,
|
|
921
|
+
collapsible = true,
|
|
922
|
+
className = ""
|
|
923
|
+
}) => {
|
|
924
|
+
const [isOpen, setIsOpen] = (0, import_react11.useState)(defaultOpen);
|
|
925
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: `border border-neutral-800 rounded-lg ${className}`, children: [
|
|
926
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
927
|
+
"div",
|
|
928
|
+
{
|
|
929
|
+
className: `flex items-center justify-between px-4 py-3 bg-neutral-900/50 ${collapsible ? "cursor-pointer active:bg-neutral-800/70 select-none" : ""}`,
|
|
930
|
+
onClick: () => collapsible && setIsOpen((o) => !o),
|
|
931
|
+
children: [
|
|
932
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
933
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-neutral-400", children: icon }),
|
|
934
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-sm font-bold uppercase tracking-widest text-neutral-300", children: title })
|
|
935
|
+
] }),
|
|
936
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
937
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { onClick: (e) => e.stopPropagation(), children: actions }),
|
|
938
|
+
collapsible && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "material-symbols-outlined text-[16px] text-neutral-500", children: isOpen ? "expand_less" : "expand_more" })
|
|
939
|
+
] })
|
|
940
|
+
]
|
|
941
|
+
}
|
|
942
|
+
),
|
|
943
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { children })
|
|
944
|
+
] });
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// src/components/PromptTab.tsx
|
|
948
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
949
|
+
var EXCLUDED = ["pdf-export", "book.micro-edit-rules"];
|
|
950
|
+
var LONG_PRESS_MS = 500;
|
|
951
|
+
var PromptTab = ({
|
|
952
|
+
workspaceTags,
|
|
953
|
+
onGenerate,
|
|
954
|
+
isGenerating,
|
|
955
|
+
feedback,
|
|
956
|
+
promptResult,
|
|
957
|
+
lastPayload,
|
|
958
|
+
onGenerateImage,
|
|
959
|
+
isGeneratingImage,
|
|
960
|
+
onRandom,
|
|
961
|
+
onTokenize,
|
|
962
|
+
isTokenizing,
|
|
963
|
+
onScanImage,
|
|
964
|
+
isScanning,
|
|
965
|
+
onExpand,
|
|
966
|
+
isExpanding,
|
|
967
|
+
onTagCreate,
|
|
968
|
+
onTagUpdate,
|
|
969
|
+
onTagDelete
|
|
970
|
+
}) => {
|
|
971
|
+
const [selectedLabels, setSelectedLabels] = (0, import_react12.useState)(/* @__PURE__ */ new Set());
|
|
972
|
+
const [instructions, setInstructions] = (0, import_react12.useState)("");
|
|
973
|
+
const [rules, setRules] = (0, import_react12.useState)("");
|
|
974
|
+
const [activeCategory, setActiveCategory] = (0, import_react12.useState)(null);
|
|
975
|
+
const [copied, setCopied] = (0, import_react12.useState)(false);
|
|
976
|
+
const imgInputRef = (0, import_react12.useRef)(null);
|
|
977
|
+
const [addingInCat, setAddingInCat] = (0, import_react12.useState)(null);
|
|
978
|
+
const [newLabel, setNewLabel] = (0, import_react12.useState)("");
|
|
979
|
+
const [newValue, setNewValue] = (0, import_react12.useState)("");
|
|
980
|
+
const [editingTag, setEditingTag] = (0, import_react12.useState)(null);
|
|
981
|
+
const [editLabel, setEditLabel] = (0, import_react12.useState)("");
|
|
982
|
+
const [editValue, setEditValue] = (0, import_react12.useState)("");
|
|
983
|
+
const longPressTimer = (0, import_react12.useRef)(null);
|
|
984
|
+
const longPressActivated = (0, import_react12.useRef)(false);
|
|
860
985
|
const toggleTag = (label) => {
|
|
861
986
|
setSelectedLabels((prev) => {
|
|
862
987
|
const next = new Set(prev);
|
|
@@ -866,97 +991,671 @@ var PromptTab = ({ workspaceTags, onGenerate, isGenerating, feedback }) => {
|
|
|
866
991
|
});
|
|
867
992
|
};
|
|
868
993
|
const handleGenerate = () => {
|
|
869
|
-
const
|
|
870
|
-
onGenerate(
|
|
994
|
+
const selectedTags = workspaceTags.all.filter((t) => selectedLabels.has(t.label) && !t.is_deleted).map((t) => ({ label: t.label, value: t.value, category: t.category }));
|
|
995
|
+
onGenerate(selectedTags, instructions, rules);
|
|
996
|
+
};
|
|
997
|
+
const handleCopy = () => {
|
|
998
|
+
if (!promptResult) return;
|
|
999
|
+
navigator.clipboard.writeText(promptResult).then(() => {
|
|
1000
|
+
setCopied(true);
|
|
1001
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1002
|
+
});
|
|
1003
|
+
};
|
|
1004
|
+
const startLongPress = (tag, cat) => {
|
|
1005
|
+
longPressActivated.current = false;
|
|
1006
|
+
longPressTimer.current = setTimeout(() => {
|
|
1007
|
+
longPressActivated.current = true;
|
|
1008
|
+
setEditingTag({ ...tag, category: cat });
|
|
1009
|
+
setEditLabel(tag.label);
|
|
1010
|
+
setEditValue(tag.value);
|
|
1011
|
+
}, LONG_PRESS_MS);
|
|
1012
|
+
};
|
|
1013
|
+
const cancelLongPress = () => {
|
|
1014
|
+
if (longPressTimer.current) {
|
|
1015
|
+
clearTimeout(longPressTimer.current);
|
|
1016
|
+
longPressTimer.current = null;
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const handleTagClick = (label) => {
|
|
1020
|
+
if (longPressActivated.current) return;
|
|
1021
|
+
toggleTag(label);
|
|
1022
|
+
};
|
|
1023
|
+
const handleSaveEdit = () => {
|
|
1024
|
+
if (!editingTag || !editLabel.trim()) return;
|
|
1025
|
+
onTagUpdate?.(editingTag.label, editingTag.category, {
|
|
1026
|
+
label: editLabel.trim(),
|
|
1027
|
+
value: editValue.trim() || editLabel.trim()
|
|
1028
|
+
});
|
|
1029
|
+
setSelectedLabels((prev) => {
|
|
1030
|
+
const next = new Set(prev);
|
|
1031
|
+
if (next.has(editingTag.label)) {
|
|
1032
|
+
next.delete(editingTag.label);
|
|
1033
|
+
next.add(editLabel.trim());
|
|
1034
|
+
}
|
|
1035
|
+
return next;
|
|
1036
|
+
});
|
|
1037
|
+
setEditingTag(null);
|
|
1038
|
+
};
|
|
1039
|
+
const handleDeleteTag = () => {
|
|
1040
|
+
if (!editingTag) return;
|
|
1041
|
+
onTagDelete?.(editingTag.label, editingTag.category);
|
|
1042
|
+
setSelectedLabels((prev) => {
|
|
1043
|
+
const next = new Set(prev);
|
|
1044
|
+
next.delete(editingTag.label);
|
|
1045
|
+
return next;
|
|
1046
|
+
});
|
|
1047
|
+
setEditingTag(null);
|
|
1048
|
+
};
|
|
1049
|
+
const handleCreateBased = () => {
|
|
1050
|
+
if (!editingTag) return;
|
|
1051
|
+
setAddingInCat(editingTag.category);
|
|
1052
|
+
setActiveCategory(editingTag.category);
|
|
1053
|
+
setNewLabel(editingTag.label);
|
|
1054
|
+
setNewValue(editingTag.value);
|
|
1055
|
+
setEditingTag(null);
|
|
871
1056
|
};
|
|
872
|
-
const categories = Object.entries(workspaceTags.by_category).filter(([, tags]) => tags.
|
|
873
|
-
|
|
874
|
-
/* @__PURE__ */ (0,
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1057
|
+
const categories = Object.entries(workspaceTags.by_category).filter(([cat, tags]) => !EXCLUDED.includes(cat) && tags.some((t) => !t.is_deleted));
|
|
1058
|
+
const tagToolbar = /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-1.5", children: [
|
|
1059
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1060
|
+
"button",
|
|
1061
|
+
{
|
|
1062
|
+
onClick: onRandom,
|
|
1063
|
+
disabled: !onRandom,
|
|
1064
|
+
className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
|
|
1065
|
+
children: "RANDOM"
|
|
1066
|
+
}
|
|
1067
|
+
),
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1069
|
+
"button",
|
|
1070
|
+
{
|
|
1071
|
+
onClick: onTokenize,
|
|
1072
|
+
disabled: !onTokenize || isTokenizing || !instructions,
|
|
1073
|
+
className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
|
|
1074
|
+
children: isTokenizing ? "..." : "TOKENIZE"
|
|
1075
|
+
}
|
|
1076
|
+
),
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1078
|
+
"button",
|
|
1079
|
+
{
|
|
1080
|
+
onClick: () => imgInputRef.current?.click(),
|
|
1081
|
+
disabled: !onScanImage || isScanning,
|
|
1082
|
+
className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
|
|
1083
|
+
children: isScanning ? "..." : "SCAN"
|
|
1084
|
+
}
|
|
1085
|
+
),
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1087
|
+
"input",
|
|
1088
|
+
{
|
|
1089
|
+
ref: imgInputRef,
|
|
1090
|
+
type: "file",
|
|
1091
|
+
accept: "image/*",
|
|
1092
|
+
className: "hidden",
|
|
1093
|
+
onChange: (e) => {
|
|
1094
|
+
if (e.target.files?.[0] && onScanImage) onScanImage(e.target.files[0]);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
),
|
|
1098
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1099
|
+
"button",
|
|
1100
|
+
{
|
|
1101
|
+
onClick: onExpand,
|
|
1102
|
+
disabled: !onExpand || isExpanding || !instructions,
|
|
1103
|
+
className: "text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800 disabled:opacity-30",
|
|
1104
|
+
children: isExpanding ? "..." : "EXPAND"
|
|
1105
|
+
}
|
|
1106
|
+
)
|
|
1107
|
+
] });
|
|
1108
|
+
const canGenerate = selectedLabels.size > 0 || instructions.trim().length > 0 || rules.trim().length > 0;
|
|
1109
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar flex flex-col", children: [
|
|
1110
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-3 p-3 pb-6", children: [
|
|
1111
|
+
categories.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center justify-center py-16 gap-3 opacity-20", children: [
|
|
1112
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[48px]", children: "label_off" }),
|
|
1113
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-[9px] font-bold uppercase tracking-widest text-center", children: [
|
|
878
1114
|
"Keine Tags geladen.",
|
|
879
|
-
/* @__PURE__ */ (0,
|
|
1115
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("br", {}),
|
|
880
1116
|
"Importiere ein Workspace-JSON im Setup-Tab."
|
|
881
1117
|
] })
|
|
882
|
-
] }) :
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1118
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1119
|
+
CollapsibleCard,
|
|
1120
|
+
{
|
|
1121
|
+
title: "INHALT",
|
|
1122
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "layers" }),
|
|
1123
|
+
defaultOpen: true,
|
|
1124
|
+
children: categories.map(([cat, tags]) => {
|
|
1125
|
+
const visibleTags = tags.filter((t) => !t.is_deleted);
|
|
1126
|
+
const selectedInCat = visibleTags.filter((t) => selectedLabels.has(t.label));
|
|
1127
|
+
const isOpen = activeCategory === cat;
|
|
1128
|
+
const isAdding = addingInCat === cat;
|
|
1129
|
+
const commitNewTag = () => {
|
|
1130
|
+
if (!newLabel.trim()) return;
|
|
1131
|
+
const valueToUse = newValue.trim() || newLabel.trim();
|
|
1132
|
+
onTagCreate?.({ label: newLabel.trim(), value: valueToUse, category: cat, is_user_created: true });
|
|
1133
|
+
setNewLabel("");
|
|
1134
|
+
setNewValue("");
|
|
1135
|
+
setAddingInCat(null);
|
|
1136
|
+
};
|
|
1137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border-b border-neutral-800 last:border-0", children: [
|
|
1138
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center", children: [
|
|
1139
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1140
|
+
"button",
|
|
1141
|
+
{
|
|
1142
|
+
onClick: () => setActiveCategory(isOpen ? null : cat),
|
|
1143
|
+
className: "flex-grow flex items-center justify-between px-4 py-3 active:bg-white/5",
|
|
1144
|
+
children: [
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm font-bold tracking-widest uppercase text-blue-400", children: cat }),
|
|
1146
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
1147
|
+
selectedInCat.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] text-green-400 truncate max-w-[150px]", children: selectedInCat.map((t) => t.label).join(", ") }),
|
|
1148
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px] text-white/30 shrink-0", children: isOpen ? "expand_less" : "expand_more" })
|
|
1149
|
+
] })
|
|
1150
|
+
]
|
|
1151
|
+
}
|
|
1152
|
+
),
|
|
1153
|
+
onTagCreate && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1154
|
+
"button",
|
|
1155
|
+
{
|
|
1156
|
+
onClick: (e) => {
|
|
1157
|
+
e.stopPropagation();
|
|
1158
|
+
setAddingInCat(isAdding ? null : cat);
|
|
1159
|
+
setActiveCategory(cat);
|
|
1160
|
+
setNewLabel("");
|
|
1161
|
+
setNewValue("");
|
|
1162
|
+
},
|
|
1163
|
+
className: "px-3 py-3 text-white/20 active:text-white/60 hover:text-white/50 transition-colors",
|
|
1164
|
+
title: "Neues Tag anlegen",
|
|
1165
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "add" })
|
|
1166
|
+
}
|
|
1167
|
+
)
|
|
1168
|
+
] }),
|
|
1169
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 pb-3 flex flex-wrap gap-1.5", children: visibleTags.map((t) => {
|
|
1170
|
+
const active = selectedLabels.has(t.label);
|
|
1171
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1172
|
+
"button",
|
|
1173
|
+
{
|
|
1174
|
+
onMouseDown: () => startLongPress(t, cat),
|
|
1175
|
+
onMouseUp: cancelLongPress,
|
|
1176
|
+
onMouseLeave: cancelLongPress,
|
|
1177
|
+
onTouchStart: () => startLongPress(t, cat),
|
|
1178
|
+
onTouchEnd: cancelLongPress,
|
|
1179
|
+
onTouchCancel: cancelLongPress,
|
|
1180
|
+
onContextMenu: (e) => e.preventDefault(),
|
|
1181
|
+
onClick: () => handleTagClick(t.label),
|
|
1182
|
+
className: `px-2.5 py-1.5 rounded text-xs font-medium border transition-colors ${active ? "bg-blue-900/50 text-blue-200 border-blue-700" : "bg-blue-900/20 text-blue-400 border-blue-900/50 active:bg-blue-900/40 active:text-blue-200"} ${t.is_user_created ? "border-dashed" : ""}`,
|
|
1183
|
+
children: t.label
|
|
1184
|
+
},
|
|
1185
|
+
t.label
|
|
1186
|
+
);
|
|
1187
|
+
}) }),
|
|
1188
|
+
isAdding && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "px-4 pb-3 space-y-2 border-t border-neutral-800/50 pt-2", children: [
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1190
|
+
"input",
|
|
1191
|
+
{
|
|
1192
|
+
autoFocus: true,
|
|
1193
|
+
value: newLabel,
|
|
1194
|
+
onChange: (e) => setNewLabel(e.target.value),
|
|
1195
|
+
onKeyDown: (e) => {
|
|
1196
|
+
if (e.key === "Enter") {
|
|
1197
|
+
e.preventDefault();
|
|
1198
|
+
commitNewTag();
|
|
1199
|
+
}
|
|
1200
|
+
},
|
|
1201
|
+
placeholder: "Label (z.B. Picasso)",
|
|
1202
|
+
className: "w-full bg-black border border-neutral-700 rounded px-3 py-1.5 text-xs text-white outline-none focus:border-blue-600"
|
|
1203
|
+
}
|
|
1204
|
+
),
|
|
1205
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1206
|
+
"textarea",
|
|
1207
|
+
{
|
|
1208
|
+
value: newValue,
|
|
1209
|
+
onChange: (e) => setNewValue(e.target.value),
|
|
1210
|
+
placeholder: "Value (Prompt-Text) \u2014 leer l\xE4sst Label als Value",
|
|
1211
|
+
rows: 3,
|
|
1212
|
+
className: "w-full bg-black border border-neutral-700 rounded px-3 py-1.5 text-xs text-white outline-none focus:border-blue-600 resize-none"
|
|
1213
|
+
}
|
|
1214
|
+
),
|
|
1215
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2", children: [
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1217
|
+
"button",
|
|
1218
|
+
{
|
|
1219
|
+
onClick: commitNewTag,
|
|
1220
|
+
disabled: !newLabel.trim(),
|
|
1221
|
+
className: "flex items-center gap-1 px-3 py-1.5 rounded text-xs font-bold bg-blue-700 text-white disabled:opacity-30 active:bg-blue-600",
|
|
1222
|
+
children: [
|
|
1223
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "check" }),
|
|
1224
|
+
" Speichern"
|
|
1225
|
+
]
|
|
1226
|
+
}
|
|
1227
|
+
),
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1229
|
+
"button",
|
|
1230
|
+
{
|
|
1231
|
+
onClick: () => {
|
|
1232
|
+
setAddingInCat(null);
|
|
1233
|
+
setNewLabel("");
|
|
1234
|
+
setNewValue("");
|
|
1235
|
+
},
|
|
1236
|
+
className: "flex items-center gap-1 px-3 py-1.5 rounded text-xs text-white/40 border border-neutral-700 active:bg-neutral-800",
|
|
1237
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" })
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1240
|
+
] })
|
|
1241
|
+
] })
|
|
1242
|
+
] }, cat);
|
|
1243
|
+
})
|
|
1244
|
+
}
|
|
1245
|
+
),
|
|
1246
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1247
|
+
CollapsibleCard,
|
|
1248
|
+
{
|
|
1249
|
+
title: "BESCHREIBUNG",
|
|
1250
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "edit_note" }),
|
|
1251
|
+
actions: tagToolbar,
|
|
1252
|
+
defaultOpen: true,
|
|
1253
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1254
|
+
"textarea",
|
|
888
1255
|
{
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1256
|
+
value: instructions,
|
|
1257
|
+
onChange: (e) => setInstructions(e.target.value),
|
|
1258
|
+
placeholder: "...",
|
|
1259
|
+
className: "w-full bg-black border border-neutral-800 rounded outline-none text-xs text-white/80 placeholder-neutral-700 resize-none dark-scrollbar p-3",
|
|
1260
|
+
style: { minHeight: 80 }
|
|
1261
|
+
}
|
|
1262
|
+
) })
|
|
1263
|
+
}
|
|
1264
|
+
),
|
|
1265
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1266
|
+
CollapsibleCard,
|
|
1267
|
+
{
|
|
1268
|
+
title: "REGELN",
|
|
1269
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "rule" }),
|
|
1270
|
+
defaultOpen: false,
|
|
1271
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1272
|
+
"textarea",
|
|
1273
|
+
{
|
|
1274
|
+
value: rules,
|
|
1275
|
+
onChange: (e) => setRules(e.target.value),
|
|
1276
|
+
placeholder: "z.B. Verwende immer explizite Nacktheit unter transparentem Stoff. Tags in Flie\xDFtext einbauen. Kein Markdown.",
|
|
1277
|
+
className: "w-full bg-black border border-neutral-800 rounded outline-none text-xs text-white/80 placeholder-neutral-700 resize-none dark-scrollbar p-3",
|
|
1278
|
+
style: { minHeight: 80 }
|
|
1279
|
+
}
|
|
1280
|
+
) })
|
|
1281
|
+
}
|
|
1282
|
+
),
|
|
1283
|
+
selectedLabels.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { onClick: () => setSelectedLabels(/* @__PURE__ */ new Set()), className: "flex items-center gap-1 text-[10px] text-white/30 active:text-white/60 self-start", children: [
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }),
|
|
1285
|
+
selectedLabels.size,
|
|
1286
|
+
" Tags gew\xE4hlt"
|
|
1287
|
+
] }),
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1289
|
+
"button",
|
|
1290
|
+
{
|
|
1291
|
+
onClick: handleGenerate,
|
|
1292
|
+
disabled: isGenerating || !canGenerate,
|
|
1293
|
+
className: "w-full py-4 rounded-lg font-bold text-sm flex items-center justify-center gap-2 bg-white text-black disabled:opacity-40 active:bg-neutral-200",
|
|
1294
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px] animate-spin", children: "autorenew" }),
|
|
1296
|
+
"BAUT PROMPT..."
|
|
1297
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "auto_fix_high" }),
|
|
1299
|
+
"PROMPT BAUEN"
|
|
1300
|
+
] })
|
|
1301
|
+
}
|
|
1302
|
+
),
|
|
1303
|
+
promptResult && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
1304
|
+
lastPayload && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1305
|
+
CollapsibleCard,
|
|
1306
|
+
{
|
|
1307
|
+
title: "INPUT MONITOR",
|
|
1308
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "terminal" }),
|
|
1309
|
+
defaultOpen: false,
|
|
1310
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("pre", { className: "text-[10px] font-mono text-blue-300/80 bg-blue-950/10 border border-blue-900/20 rounded p-3 overflow-x-auto whitespace-pre-wrap leading-relaxed", children: lastPayload }) })
|
|
1311
|
+
}
|
|
1312
|
+
),
|
|
1313
|
+
feedback && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1314
|
+
CollapsibleCard,
|
|
1315
|
+
{
|
|
1316
|
+
title: "ANALYSE",
|
|
1317
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "chat" }),
|
|
1318
|
+
defaultOpen: true,
|
|
1319
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs font-mono text-orange-200/80 bg-orange-950/10 border border-orange-900/20 rounded p-3 leading-relaxed", children: feedback }) })
|
|
1320
|
+
}
|
|
1321
|
+
),
|
|
1322
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border border-neutral-800 rounded-lg", children: [
|
|
1323
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between px-4 py-3 bg-neutral-900/50 rounded-t-lg", children: [
|
|
1324
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-widest text-neutral-400", children: "Prompt" }),
|
|
1325
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1326
|
+
"button",
|
|
1327
|
+
{
|
|
1328
|
+
onClick: handleCopy,
|
|
1329
|
+
className: "flex items-center gap-1.5 text-[9px] font-bold uppercase px-2 py-1 rounded border border-neutral-700 text-neutral-400 active:bg-neutral-800",
|
|
1330
|
+
children: [
|
|
1331
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: copied ? "check" : "content_copy" }),
|
|
1332
|
+
copied ? "KOPIERT" : "KOPIEREN"
|
|
1333
|
+
]
|
|
1334
|
+
}
|
|
1335
|
+
)
|
|
1336
|
+
] }),
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-xs text-white/80 leading-relaxed italic", children: promptResult }) })
|
|
1338
|
+
] }),
|
|
1339
|
+
onGenerateImage && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1340
|
+
"button",
|
|
901
1341
|
{
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1342
|
+
onClick: () => onGenerateImage(promptResult),
|
|
1343
|
+
disabled: isGeneratingImage,
|
|
1344
|
+
className: "w-full py-4 rounded-lg bg-white text-black font-bold text-sm flex items-center justify-center gap-2 disabled:opacity-40 active:bg-neutral-200",
|
|
1345
|
+
children: [
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isGeneratingImage ? "hourglass_empty" : "image" }),
|
|
1347
|
+
isGeneratingImage ? "GENERIERE..." : "GENERIEREN"
|
|
1348
|
+
]
|
|
906
1349
|
}
|
|
907
1350
|
)
|
|
908
1351
|
] })
|
|
909
1352
|
] }),
|
|
910
|
-
/* @__PURE__ */ (0,
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1353
|
+
editingTag && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1354
|
+
"div",
|
|
1355
|
+
{
|
|
1356
|
+
className: "fixed inset-0 z-50 flex items-end justify-center",
|
|
1357
|
+
style: { background: "rgba(0,0,0,0.75)" },
|
|
1358
|
+
onClick: () => setEditingTag(null),
|
|
1359
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1360
|
+
"div",
|
|
914
1361
|
{
|
|
915
|
-
|
|
916
|
-
|
|
1362
|
+
className: "w-full max-w-lg bg-[#1c1c1c] border border-neutral-700 rounded-t-2xl p-5 space-y-4",
|
|
1363
|
+
onClick: (e) => e.stopPropagation(),
|
|
917
1364
|
children: [
|
|
918
|
-
/* @__PURE__ */ (0,
|
|
919
|
-
|
|
920
|
-
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1366
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] font-bold text-neutral-400 uppercase tracking-widest", children: editingTag.category }),
|
|
1367
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { onClick: () => setEditingTag(null), className: "text-white/30 active:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "close" }) })
|
|
1368
|
+
] }),
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1370
|
+
"input",
|
|
1371
|
+
{
|
|
1372
|
+
autoFocus: true,
|
|
1373
|
+
value: editLabel,
|
|
1374
|
+
onChange: (e) => setEditLabel(e.target.value),
|
|
1375
|
+
placeholder: "Label",
|
|
1376
|
+
className: "w-full bg-black border border-neutral-700 rounded px-3 py-2 text-sm text-white outline-none focus:border-blue-600"
|
|
1377
|
+
}
|
|
1378
|
+
),
|
|
1379
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1380
|
+
"textarea",
|
|
1381
|
+
{
|
|
1382
|
+
value: editValue,
|
|
1383
|
+
onChange: (e) => setEditValue(e.target.value),
|
|
1384
|
+
placeholder: "Value (Prompt-Text)",
|
|
1385
|
+
rows: 4,
|
|
1386
|
+
className: "w-full bg-black border border-neutral-700 rounded px-3 py-2 text-xs text-white outline-none focus:border-blue-600 resize-none"
|
|
1387
|
+
}
|
|
1388
|
+
),
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex gap-2 flex-wrap", children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1391
|
+
"button",
|
|
1392
|
+
{
|
|
1393
|
+
onClick: handleSaveEdit,
|
|
1394
|
+
disabled: !editLabel.trim(),
|
|
1395
|
+
className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold bg-blue-700 text-white disabled:opacity-30 active:bg-blue-600",
|
|
1396
|
+
children: [
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "check" }),
|
|
1398
|
+
" Speichern"
|
|
1399
|
+
]
|
|
1400
|
+
}
|
|
1401
|
+
),
|
|
1402
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
1403
|
+
"button",
|
|
1404
|
+
{
|
|
1405
|
+
onClick: handleCreateBased,
|
|
1406
|
+
className: "flex-1 flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold border border-neutral-600 text-white/70 active:bg-neutral-800",
|
|
1407
|
+
children: [
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "content_copy" }),
|
|
1409
|
+
" Neu auf Basis"
|
|
1410
|
+
]
|
|
1411
|
+
}
|
|
1412
|
+
),
|
|
1413
|
+
onTagDelete && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1414
|
+
"button",
|
|
1415
|
+
{
|
|
1416
|
+
onClick: handleDeleteTag,
|
|
1417
|
+
className: "flex items-center justify-center gap-1.5 px-3 py-2.5 rounded text-sm font-bold border border-red-900/60 text-red-400 active:bg-red-900/20",
|
|
1418
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "delete" })
|
|
1419
|
+
}
|
|
1420
|
+
)
|
|
1421
|
+
] })
|
|
921
1422
|
]
|
|
922
1423
|
}
|
|
1424
|
+
)
|
|
1425
|
+
}
|
|
1426
|
+
)
|
|
1427
|
+
] });
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
// src/components/ProjectSyncTab.tsx
|
|
1431
|
+
var import_react13 = require("react");
|
|
1432
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1433
|
+
var ProjectSyncTab = ({
|
|
1434
|
+
onProjectExport,
|
|
1435
|
+
onProjectImport,
|
|
1436
|
+
onWorkspaceImport,
|
|
1437
|
+
projectActionState,
|
|
1438
|
+
serverProjects,
|
|
1439
|
+
onServerSave,
|
|
1440
|
+
onServerLoad,
|
|
1441
|
+
onServerDelete,
|
|
1442
|
+
onRefreshServerProjects,
|
|
1443
|
+
onComputeSyncDiff,
|
|
1444
|
+
onExecuteSync
|
|
1445
|
+
}) => {
|
|
1446
|
+
const projectInputRef = (0, import_react13.useRef)(null);
|
|
1447
|
+
const workspaceInputRef = (0, import_react13.useRef)(null);
|
|
1448
|
+
const [saveName, setSaveName] = (0, import_react13.useState)("");
|
|
1449
|
+
const [isSaving, setIsSaving] = (0, import_react13.useState)(false);
|
|
1450
|
+
const [isExporting, setIsExporting] = (0, import_react13.useState)(false);
|
|
1451
|
+
const [syncState, setSyncState] = (0, import_react13.useState)("idle");
|
|
1452
|
+
const [syncDiff, setSyncDiff] = (0, import_react13.useState)(null);
|
|
1453
|
+
const [selectedLocalIds, setSelectedLocalIds] = (0, import_react13.useState)(/* @__PURE__ */ new Set());
|
|
1454
|
+
const handleExport = async () => {
|
|
1455
|
+
if (!onProjectExport) return;
|
|
1456
|
+
setIsExporting(true);
|
|
1457
|
+
try {
|
|
1458
|
+
await onProjectExport();
|
|
1459
|
+
} finally {
|
|
1460
|
+
setIsExporting(false);
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
const handleComputeDiff = async () => {
|
|
1464
|
+
if (!onComputeSyncDiff) return;
|
|
1465
|
+
setSyncState("computing");
|
|
1466
|
+
setSyncDiff(null);
|
|
1467
|
+
try {
|
|
1468
|
+
const diff = await onComputeSyncDiff();
|
|
1469
|
+
setSyncDiff(diff);
|
|
1470
|
+
setSelectedLocalIds(new Set(diff.localOnly.map((g) => g.id)));
|
|
1471
|
+
setSyncState("ready");
|
|
1472
|
+
} catch {
|
|
1473
|
+
setSyncState("error");
|
|
1474
|
+
setTimeout(() => setSyncState("idle"), 4e3);
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
const handleExecuteSync = async () => {
|
|
1478
|
+
if (!onExecuteSync || !syncDiff) return;
|
|
1479
|
+
setSyncState("executing");
|
|
1480
|
+
try {
|
|
1481
|
+
await onExecuteSync([...selectedLocalIds]);
|
|
1482
|
+
setSyncState("done");
|
|
1483
|
+
setSyncDiff(null);
|
|
1484
|
+
setTimeout(() => setSyncState("idle"), 3e3);
|
|
1485
|
+
} catch {
|
|
1486
|
+
setSyncState("error");
|
|
1487
|
+
setTimeout(() => setSyncState("idle"), 4e3);
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
const toggleLocalId = (id) => {
|
|
1491
|
+
setSelectedLocalIds((prev) => {
|
|
1492
|
+
const next = new Set(prev);
|
|
1493
|
+
if (next.has(id)) next.delete(id);
|
|
1494
|
+
else next.add(id);
|
|
1495
|
+
return next;
|
|
1496
|
+
});
|
|
1497
|
+
};
|
|
1498
|
+
const isWorking = projectActionState === "working" || projectActionState === "working-full";
|
|
1499
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "p-6 flex flex-col gap-8", children: [
|
|
1500
|
+
(onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Projekt-ZIP" }),
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1503
|
+
onProjectExport && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "filled", icon: "download", loading: isExporting || isWorking, onClick: handleExport, children: "Exportieren (.zip)" }),
|
|
1504
|
+
onProjectImport && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "ZIP laden" }),
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1507
|
+
"input",
|
|
1508
|
+
{
|
|
1509
|
+
ref: projectInputRef,
|
|
1510
|
+
type: "file",
|
|
1511
|
+
accept: ".zip",
|
|
1512
|
+
className: "hidden",
|
|
1513
|
+
onChange: (e) => {
|
|
1514
|
+
const f = e.target.files?.[0];
|
|
1515
|
+
if (f) onProjectImport(f);
|
|
1516
|
+
e.target.value = "";
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
)
|
|
1520
|
+
] })
|
|
1521
|
+
] }),
|
|
1522
|
+
onWorkspaceImport && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1523
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
|
|
1524
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1525
|
+
"input",
|
|
1526
|
+
{
|
|
1527
|
+
ref: workspaceInputRef,
|
|
1528
|
+
type: "file",
|
|
1529
|
+
accept: ".json",
|
|
1530
|
+
className: "hidden",
|
|
1531
|
+
onChange: (e) => {
|
|
1532
|
+
const f = e.target.files?.[0];
|
|
1533
|
+
if (f) onWorkspaceImport(f);
|
|
1534
|
+
e.target.value = "";
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
)
|
|
1538
|
+
] })
|
|
1539
|
+
] }),
|
|
1540
|
+
(onServerSave || serverProjects || onServerDelete) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Server-Speicher" }),
|
|
1542
|
+
onServerSave && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex gap-2", children: [
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1544
|
+
"input",
|
|
1545
|
+
{
|
|
1546
|
+
type: "text",
|
|
1547
|
+
value: saveName,
|
|
1548
|
+
onChange: (e) => setSaveName(e.target.value),
|
|
1549
|
+
placeholder: "Name (optional)",
|
|
1550
|
+
className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 text-[12px] text-white/70 outline-none placeholder:text-white/20",
|
|
1551
|
+
style: { height: 40 }
|
|
1552
|
+
}
|
|
923
1553
|
),
|
|
924
|
-
/* @__PURE__ */ (0,
|
|
925
|
-
|
|
1554
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1555
|
+
PillButton,
|
|
926
1556
|
{
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1557
|
+
variant: "filled",
|
|
1558
|
+
icon: "cloud_upload",
|
|
1559
|
+
loading: isSaving,
|
|
1560
|
+
onClick: async () => {
|
|
1561
|
+
setIsSaving(true);
|
|
1562
|
+
try {
|
|
1563
|
+
await onServerSave(saveName || void 0);
|
|
1564
|
+
setSaveName("");
|
|
1565
|
+
await onRefreshServerProjects?.();
|
|
1566
|
+
} finally {
|
|
1567
|
+
setIsSaving(false);
|
|
1568
|
+
}
|
|
1569
|
+
},
|
|
1570
|
+
children: "Speichern"
|
|
932
1571
|
}
|
|
933
|
-
)
|
|
1572
|
+
)
|
|
934
1573
|
] }),
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
"
|
|
939
|
-
|
|
940
|
-
|
|
1574
|
+
(!serverProjects || serverProjects.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts gespeichert." }),
|
|
1575
|
+
serverProjects && serverProjects.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col gap-1", children: serverProjects.map((p) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1577
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[11px] text-white/70 font-bold truncate", children: p.name }),
|
|
1578
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/25", children: [
|
|
1579
|
+
new Date(p.saved_at).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }),
|
|
1580
|
+
" \xB7 ",
|
|
1581
|
+
(p.size / 1024).toFixed(0),
|
|
1582
|
+
" KB"
|
|
1583
|
+
] })
|
|
1584
|
+
] }),
|
|
1585
|
+
onServerLoad && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: () => onServerLoad(p.id), className: "w-8 h-8 flex items-center justify-center text-white/30 active:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "cloud_download" }) }),
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { onClick: () => onServerDelete?.(p.id), className: "w-8 h-8 flex items-center justify-center text-white/20 active:text-red-400 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "delete" }) })
|
|
1587
|
+
] }, p.id)) })
|
|
1588
|
+
] }),
|
|
1589
|
+
onComputeSyncDiff && onExecuteSync && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1590
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Sync" }),
|
|
1591
|
+
(syncState === "idle" || syncState === "computing") && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(PillButton, { variant: "outline", icon: "compare_arrows", loading: syncState === "computing", onClick: handleComputeDiff, children: "Diff berechnen" }),
|
|
1592
|
+
syncState === "done" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2 px-2 text-[11px] text-green-400", children: [
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[16px]", children: "check_circle" }),
|
|
1594
|
+
"Sync abgeschlossen"
|
|
1595
|
+
] }),
|
|
1596
|
+
syncState === "error" && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[11px] text-red-400 px-2", children: "Fehler beim Sync." }),
|
|
1597
|
+
syncDiff && (syncState === "ready" || syncState === "executing") && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
1598
|
+
syncDiff.localOnly.length === 0 && syncDiff.serverOnly.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/30 px-2", children: "Alles synchron \u2014 keine Unterschiede." }),
|
|
1599
|
+
syncDiff.localOnly.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
|
|
1601
|
+
"Nur lokal (",
|
|
1602
|
+
syncDiff.localOnly.length,
|
|
1603
|
+
")"
|
|
1604
|
+
] }),
|
|
1605
|
+
syncDiff.localOnly.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
1606
|
+
"button",
|
|
1607
|
+
{
|
|
1608
|
+
onClick: () => toggleLocalId(gen.id),
|
|
1609
|
+
className: `flex items-center gap-3 px-3 py-2 rounded-xl border transition-all text-left ${selectedLocalIds.has(gen.id) ? "bg-blue-500/10 border-blue-500/30" : "bg-white/3 border-white/5 opacity-60"}`,
|
|
1610
|
+
children: [
|
|
1611
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: `w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 ${selectedLocalIds.has(gen.id) ? "bg-blue-500 border-blue-500" : "border-white/20"}`, children: selectedLocalIds.has(gen.id) && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "material-symbols-outlined text-[11px] text-white", children: "check" }) }),
|
|
1612
|
+
gen.base64 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
|
|
1613
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
|
|
1616
|
+
] })
|
|
1617
|
+
]
|
|
1618
|
+
},
|
|
1619
|
+
gen.id
|
|
1620
|
+
))
|
|
1621
|
+
] }),
|
|
1622
|
+
syncDiff.serverOnly.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1623
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
|
|
1624
|
+
"Nur auf Server (",
|
|
1625
|
+
syncDiff.serverOnly.length,
|
|
1626
|
+
")"
|
|
1627
|
+
] }),
|
|
1628
|
+
syncDiff.serverOnly.map((gen) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-3 px-3 py-2 rounded-xl bg-white/3 border border-white/5 opacity-40", children: [
|
|
1629
|
+
gen.base64 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
|
|
1630
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1631
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
|
|
1632
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
|
|
1633
|
+
] })
|
|
1634
|
+
] }, gen.id))
|
|
1635
|
+
] }),
|
|
1636
|
+
(syncDiff.localOnly.length > 0 || syncDiff.serverOnly.length > 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
941
1637
|
PillButton,
|
|
942
1638
|
{
|
|
943
1639
|
variant: "solid",
|
|
944
|
-
icon: "
|
|
945
|
-
loading:
|
|
946
|
-
disabled:
|
|
947
|
-
onClick:
|
|
948
|
-
children:
|
|
1640
|
+
icon: "sync",
|
|
1641
|
+
loading: syncState === "executing",
|
|
1642
|
+
disabled: selectedLocalIds.size === 0,
|
|
1643
|
+
onClick: handleExecuteSync,
|
|
1644
|
+
children: [
|
|
1645
|
+
selectedLocalIds.size,
|
|
1646
|
+
" \xFCbertragen"
|
|
1647
|
+
]
|
|
949
1648
|
}
|
|
950
1649
|
)
|
|
951
1650
|
] })
|
|
952
1651
|
] })
|
|
953
|
-
] });
|
|
1652
|
+
] }) });
|
|
954
1653
|
};
|
|
955
1654
|
|
|
956
1655
|
// src/components/AvatarArchitectApp.tsx
|
|
957
|
-
var
|
|
958
|
-
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia }) {
|
|
959
|
-
(0,
|
|
1656
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
1657
|
+
function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
|
|
1658
|
+
(0, import_react14.useEffect)(() => {
|
|
960
1659
|
const id = "flow-styles";
|
|
961
1660
|
if (!document.getElementById(id)) {
|
|
962
1661
|
const style = document.createElement("style");
|
|
@@ -965,31 +1664,125 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
965
1664
|
document.head.appendChild(style);
|
|
966
1665
|
}
|
|
967
1666
|
}, []);
|
|
968
|
-
const [
|
|
969
|
-
const [
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const [
|
|
977
|
-
const
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
const [
|
|
987
|
-
const [
|
|
988
|
-
const [
|
|
989
|
-
const [
|
|
1667
|
+
const [showStart, setShowStart] = (0, import_react14.useState)(true);
|
|
1668
|
+
const [layoutChoice, setLayoutChoice] = (0, import_react14.useState)(() => {
|
|
1669
|
+
try {
|
|
1670
|
+
return localStorage.getItem("aa-layout") || null;
|
|
1671
|
+
} catch {
|
|
1672
|
+
return null;
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
const [projectLoaded, setProjectLoaded] = (0, import_react14.useState)(false);
|
|
1676
|
+
const wsInputRef = (0, import_react14.useRef)(null);
|
|
1677
|
+
const startApp = (choice) => {
|
|
1678
|
+
try {
|
|
1679
|
+
localStorage.setItem("aa-layout", choice);
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
setLayoutChoice(choice);
|
|
1683
|
+
setShowStart(false);
|
|
1684
|
+
};
|
|
1685
|
+
const [nodes, setNodes] = (0, import_react14.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
|
|
1686
|
+
const [edges, setEdges] = (0, import_react14.useState)([]);
|
|
1687
|
+
const [history, setHistory] = (0, import_react14.useState)([]);
|
|
1688
|
+
const [galleryItems, setGalleryItems] = (0, import_react14.useState)([]);
|
|
1689
|
+
const [activePrompt, setActivePrompt] = (0, import_react14.useState)("");
|
|
1690
|
+
const [isSynthesizing, setIsSynthesizing] = (0, import_react14.useState)(false);
|
|
1691
|
+
const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react14.useState)(0);
|
|
1692
|
+
const [currentResult, setCurrentResult] = (0, import_react14.useState)(null);
|
|
1693
|
+
const [focusedNodeId, setFocusedNodeId] = (0, import_react14.useState)(null);
|
|
1694
|
+
const [leftTab, setLeftTab] = (0, import_react14.useState)("prompt");
|
|
1695
|
+
const [promptFeedback, setPromptFeedback] = (0, import_react14.useState)(null);
|
|
1696
|
+
const [lastPromptPayload, setLastPromptPayload] = (0, import_react14.useState)(null);
|
|
1697
|
+
const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react14.useState)(false);
|
|
1698
|
+
const [activeTab, setActiveTab] = (0, import_react14.useState)("history");
|
|
1699
|
+
const [mobileTab, setMobileTab] = (0, import_react14.useState)("stage");
|
|
1700
|
+
const [aspectRatio, setAspectRatio] = (0, import_react14.useState)("1:1");
|
|
1701
|
+
const [selectedModel, setSelectedModel] = (0, import_react14.useState)("\u{1F34C} Nano Banana Pro");
|
|
1702
|
+
const [seed, setSeed] = (0, import_react14.useState)(Math.floor(Math.random() * 1e6));
|
|
1703
|
+
const [seedMode, setSeedMode] = (0, import_react14.useState)("random");
|
|
1704
|
+
const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react14.useState)(false);
|
|
1705
|
+
const [isRightCollapsed, setIsRightCollapsed] = (0, import_react14.useState)(false);
|
|
1706
|
+
const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react14.useState)(false);
|
|
1707
|
+
const [projectActionState, setProjectActionState] = (0, import_react14.useState)("idle");
|
|
1708
|
+
const syncServerDataRef = (0, import_react14.useRef)(null);
|
|
1709
|
+
const [workspaceTags, setWorkspaceTags] = (0, import_react14.useState)(null);
|
|
1710
|
+
const [serverProjects, setServerProjects] = (0, import_react14.useState)([]);
|
|
1711
|
+
const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react14.useState)(false);
|
|
1712
|
+
const [highContrast, setHighContrast] = (0, import_react14.useState)(() => {
|
|
1713
|
+
try {
|
|
1714
|
+
return localStorage.getItem("aa-contrast") === "high";
|
|
1715
|
+
} catch {
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
const [touchStartX, setTouchStartX] = (0, import_react14.useState)(null);
|
|
1720
|
+
const [isFullscreen, setIsFullscreen] = (0, import_react14.useState)(false);
|
|
1721
|
+
const [zoomScale, setZoomScale] = (0, import_react14.useState)(1);
|
|
1722
|
+
const [zoomOffset, setZoomOffset] = (0, import_react14.useState)({ x: 0, y: 0 });
|
|
1723
|
+
const lastPinchDist = (0, import_react14.useRef)(null);
|
|
1724
|
+
const lastTapTime = (0, import_react14.useRef)(0);
|
|
1725
|
+
const dragStart = (0, import_react14.useRef)(null);
|
|
1726
|
+
const openFullscreen = () => {
|
|
1727
|
+
setIsFullscreen(true);
|
|
1728
|
+
setZoomScale(1);
|
|
1729
|
+
setZoomOffset({ x: 0, y: 0 });
|
|
1730
|
+
};
|
|
1731
|
+
const closeFullscreen = () => {
|
|
1732
|
+
setIsFullscreen(false);
|
|
1733
|
+
setZoomScale(1);
|
|
1734
|
+
setZoomOffset({ x: 0, y: 0 });
|
|
1735
|
+
};
|
|
1736
|
+
const handleFsTouchStart = (e) => {
|
|
1737
|
+
if (e.touches.length === 2) {
|
|
1738
|
+
lastPinchDist.current = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
|
|
1739
|
+
} else if (e.touches.length === 1) {
|
|
1740
|
+
const now = Date.now();
|
|
1741
|
+
if (now - lastTapTime.current < 280) {
|
|
1742
|
+
setZoomScale((s) => s > 1 ? 1 : 2.5);
|
|
1743
|
+
setZoomOffset({ x: 0, y: 0 });
|
|
1744
|
+
}
|
|
1745
|
+
lastTapTime.current = now;
|
|
1746
|
+
dragStart.current = { x: e.touches[0].clientX, y: e.touches[0].clientY, ox: zoomOffset.x, oy: zoomOffset.y };
|
|
1747
|
+
}
|
|
1748
|
+
};
|
|
1749
|
+
const handleFsTouchMove = (e) => {
|
|
1750
|
+
if (e.touches.length === 2 && lastPinchDist.current !== null) {
|
|
1751
|
+
const dist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY);
|
|
1752
|
+
setZoomScale((s) => Math.min(Math.max(s * (dist / lastPinchDist.current), 1), 5));
|
|
1753
|
+
lastPinchDist.current = dist;
|
|
1754
|
+
} else if (e.touches.length === 1 && dragStart.current && zoomScale > 1) {
|
|
1755
|
+
setZoomOffset({ x: dragStart.current.ox + e.touches[0].clientX - dragStart.current.x, y: dragStart.current.oy + e.touches[0].clientY - dragStart.current.y });
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
const handleFsTouchEnd = (e) => {
|
|
1759
|
+
lastPinchDist.current = null;
|
|
1760
|
+
if (e.changedTouches.length === 1 && zoomScale <= 1 && dragStart.current) {
|
|
1761
|
+
const dx = e.changedTouches[0].clientX - dragStart.current.x;
|
|
1762
|
+
if (dx < -50) goToNext();
|
|
1763
|
+
else if (dx > 50) goToPrev();
|
|
1764
|
+
}
|
|
1765
|
+
dragStart.current = null;
|
|
1766
|
+
};
|
|
1767
|
+
const toggleContrast = () => {
|
|
1768
|
+
const next = !highContrast;
|
|
1769
|
+
setHighContrast(next);
|
|
1770
|
+
try {
|
|
1771
|
+
localStorage.setItem("aa-contrast", next ? "high" : "low");
|
|
1772
|
+
} catch {
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
const currentIndex = (0, import_react14.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
|
|
1776
|
+
const goToPrev = (0, import_react14.useCallback)(() => {
|
|
1777
|
+
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
1778
|
+
}, [currentIndex, history]);
|
|
1779
|
+
const goToNext = (0, import_react14.useCallback)(() => {
|
|
1780
|
+
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
1781
|
+
}, [currentIndex, history]);
|
|
1782
|
+
const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
|
|
990
1783
|
const isGenerating = activeGenerationsCount > 0;
|
|
991
1784
|
useKeyboardNavigation(history, currentResult, setCurrentResult);
|
|
992
|
-
const getSubtreeFormat = (0,
|
|
1785
|
+
const getSubtreeFormat = (0, import_react14.useCallback)((nodeId, depth = 0) => {
|
|
993
1786
|
const node = nodes.find((n) => n.id === nodeId);
|
|
994
1787
|
if (!node) return "";
|
|
995
1788
|
const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
|
|
@@ -997,7 +1790,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
997
1790
|
return `${indent}- ${node.data.label || "(unbenannt)"}
|
|
998
1791
|
` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
|
|
999
1792
|
}, [nodes, edges]);
|
|
1000
|
-
const activePath = (0,
|
|
1793
|
+
const activePath = (0, import_react14.useMemo)(() => {
|
|
1001
1794
|
if (!focusedNodeId) return /* @__PURE__ */ new Set();
|
|
1002
1795
|
const path = /* @__PURE__ */ new Set([focusedNodeId]);
|
|
1003
1796
|
let currId = focusedNodeId;
|
|
@@ -1015,17 +1808,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1015
1808
|
setActiveGenerationsCount((prev) => prev + 1);
|
|
1016
1809
|
const genId = crypto.randomUUID();
|
|
1017
1810
|
const activeSeed = seedMode === "random" ? Math.floor(Math.random() * 1e9) : seed;
|
|
1018
|
-
const newGen = {
|
|
1019
|
-
id: genId,
|
|
1020
|
-
nodeId: overrideNodeId || focusedNodeId || "1",
|
|
1021
|
-
status: "processing",
|
|
1022
|
-
timestamp: Date.now(),
|
|
1023
|
-
prompt: promptToUse,
|
|
1024
|
-
seed: activeSeed,
|
|
1025
|
-
model: selectedModel,
|
|
1026
|
-
tags: [],
|
|
1027
|
-
type: "generation"
|
|
1028
|
-
};
|
|
1811
|
+
const newGen = { id: genId, nodeId: overrideNodeId || focusedNodeId || "1", status: "processing", timestamp: Date.now(), prompt: promptToUse, seed: activeSeed, model: selectedModel, tags: [], type: "generation" };
|
|
1029
1812
|
setHistory((prev) => [newGen, ...prev]);
|
|
1030
1813
|
if (!options.silent) setCurrentResult(newGen);
|
|
1031
1814
|
if (seedMode === "random") setSeed(activeSeed);
|
|
@@ -1062,12 +1845,48 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1062
1845
|
setIsSynthesizing(false);
|
|
1063
1846
|
}
|
|
1064
1847
|
};
|
|
1065
|
-
const
|
|
1848
|
+
const handleTagCreate = (tag) => {
|
|
1849
|
+
setWorkspaceTags((prev) => {
|
|
1850
|
+
if (!prev) return prev;
|
|
1851
|
+
const newTagOption = { label: tag.label, value: tag.value, is_user_created: true };
|
|
1852
|
+
const updatedCat = [...prev.by_category[tag.category] || [], newTagOption];
|
|
1853
|
+
return {
|
|
1854
|
+
by_category: { ...prev.by_category, [tag.category]: updatedCat },
|
|
1855
|
+
all: [...prev.all, { ...newTagOption, category: tag.category }]
|
|
1856
|
+
};
|
|
1857
|
+
});
|
|
1858
|
+
};
|
|
1859
|
+
const handleTagUpdate = (originalLabel, originalCategory, updates) => {
|
|
1860
|
+
setWorkspaceTags((prev) => {
|
|
1861
|
+
if (!prev) return prev;
|
|
1862
|
+
const updatedCat = (prev.by_category[originalCategory] || []).map(
|
|
1863
|
+
(t) => t.label === originalLabel ? { ...t, ...updates } : t
|
|
1864
|
+
);
|
|
1865
|
+
const updatedAll = prev.all.map(
|
|
1866
|
+
(t) => t.label === originalLabel && t.category === originalCategory ? { ...t, ...updates } : t
|
|
1867
|
+
);
|
|
1868
|
+
return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
|
|
1869
|
+
});
|
|
1870
|
+
};
|
|
1871
|
+
const handleTagDelete = (label, category) => {
|
|
1872
|
+
setWorkspaceTags((prev) => {
|
|
1873
|
+
if (!prev) return prev;
|
|
1874
|
+
const updatedCat = (prev.by_category[category] || []).map(
|
|
1875
|
+
(t) => t.label === label ? { ...t, is_deleted: true } : t
|
|
1876
|
+
);
|
|
1877
|
+
const updatedAll = prev.all.map(
|
|
1878
|
+
(t) => t.label === label && t.category === category ? { ...t, is_deleted: true } : t
|
|
1879
|
+
);
|
|
1880
|
+
return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
|
|
1881
|
+
});
|
|
1882
|
+
};
|
|
1883
|
+
const handlePromptTabGenerate = async (selectedTags, instructions, rules) => {
|
|
1066
1884
|
setIsPromptTabGenerating(true);
|
|
1067
1885
|
setPromptFeedback(null);
|
|
1068
1886
|
try {
|
|
1069
1887
|
const treeText = focusedNodeId ? getSubtreeFormat(focusedNodeId) : void 0;
|
|
1070
|
-
const payload = buildPromptTabPayload(
|
|
1888
|
+
const payload = buildPromptTabPayload(selectedTags, instructions, rules, treeText);
|
|
1889
|
+
setLastPromptPayload(payload);
|
|
1071
1890
|
const raw = await onGeneratePrompt(payload);
|
|
1072
1891
|
const { prompt, feedback } = parsePromptResponse(raw);
|
|
1073
1892
|
setActivePrompt(prompt);
|
|
@@ -1084,9 +1903,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1084
1903
|
const base64Data = currentResult.base64.split(",")[1];
|
|
1085
1904
|
const mimeType = currentResult.base64.split(";")[0].split(":")[1] || "image/png";
|
|
1086
1905
|
let finalBase64 = base64Data;
|
|
1087
|
-
if (mimeType === "image/png")
|
|
1088
|
-
finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
|
|
1089
|
-
}
|
|
1906
|
+
if (mimeType === "image/png") finalBase64 = injectXMPMetadata(base64Data, currentResult.prompt || "", currentResult.seed, currentResult.model, currentResult.id, currentResult.tags || [], getSubtreeFormat(currentResult.nodeId));
|
|
1090
1907
|
await onDownload(finalBase64, mimeType, `avatar_${currentResult.id.slice(0, 5)}.${mimeType.split("/")[1]}`);
|
|
1091
1908
|
} catch (e) {
|
|
1092
1909
|
console.error("Download Error", e);
|
|
@@ -1095,7 +1912,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1095
1912
|
const handleProjectExport = async () => {
|
|
1096
1913
|
setProjectActionState("working-full");
|
|
1097
1914
|
try {
|
|
1098
|
-
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode });
|
|
1915
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
1099
1916
|
await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
|
|
1100
1917
|
setProjectActionState("done");
|
|
1101
1918
|
setTimeout(() => setProjectActionState("idle"), 3e3);
|
|
@@ -1111,7 +1928,10 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1111
1928
|
const data = await importProjectFromZip(file);
|
|
1112
1929
|
if (data.nodes) setNodes(data.nodes);
|
|
1113
1930
|
if (data.edges) setEdges(data.edges);
|
|
1114
|
-
if (data.history)
|
|
1931
|
+
if (data.history) {
|
|
1932
|
+
setHistory(data.history);
|
|
1933
|
+
if (data.history.length > 0) setCurrentResult(data.history[0]);
|
|
1934
|
+
}
|
|
1115
1935
|
if (data.galleryItems) setGalleryItems(data.galleryItems);
|
|
1116
1936
|
if (data.settings) {
|
|
1117
1937
|
setAspectRatio(data.settings.aspectRatio || "1:1");
|
|
@@ -1119,7 +1939,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1119
1939
|
setSeed(data.settings.seed || 0);
|
|
1120
1940
|
setSeedMode(data.settings.seedMode || "random");
|
|
1121
1941
|
}
|
|
1942
|
+
if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
|
|
1122
1943
|
setProjectActionState("done");
|
|
1944
|
+
setProjectLoaded(true);
|
|
1123
1945
|
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
1124
1946
|
} catch {
|
|
1125
1947
|
setProjectActionState("error");
|
|
@@ -1134,12 +1956,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1134
1956
|
const rawTags = root.tags || root;
|
|
1135
1957
|
if (!rawTags?.by_category) return;
|
|
1136
1958
|
const filtered = {};
|
|
1137
|
-
const
|
|
1959
|
+
const EXCLUDED2 = ["pdf-export", "book.micro-edit-rules"];
|
|
1138
1960
|
Object.entries(rawTags.by_category).forEach(([cat, items]) => {
|
|
1139
|
-
if (
|
|
1961
|
+
if (EXCLUDED2.includes(cat) || !Array.isArray(items)) return;
|
|
1140
1962
|
filtered[cat] = items.filter((t) => t && typeof t === "object" && t.label && t.value);
|
|
1141
1963
|
});
|
|
1142
|
-
const filteredAll = (rawTags.all || []).filter((t) => t && !
|
|
1964
|
+
const filteredAll = (rawTags.all || []).filter((t) => t && !EXCLUDED2.includes(t.category));
|
|
1143
1965
|
setWorkspaceTags({ by_category: filtered, all: filteredAll });
|
|
1144
1966
|
} catch (err) {
|
|
1145
1967
|
console.error("Workspace Load failed", err);
|
|
@@ -1147,37 +1969,571 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1147
1969
|
};
|
|
1148
1970
|
reader.readAsText(file);
|
|
1149
1971
|
};
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1972
|
+
const fetchServerProjects = async () => {
|
|
1973
|
+
if (!onFetchServerProjects) return;
|
|
1974
|
+
try {
|
|
1975
|
+
setServerProjects(await onFetchServerProjects());
|
|
1976
|
+
} catch {
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
const handleServerSave = async (name) => {
|
|
1980
|
+
if (!onServerSave) return;
|
|
1981
|
+
setProjectActionState("working-full");
|
|
1982
|
+
try {
|
|
1983
|
+
const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags);
|
|
1984
|
+
await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
|
|
1985
|
+
await fetchServerProjects();
|
|
1986
|
+
setProjectActionState("done");
|
|
1987
|
+
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
1988
|
+
} catch {
|
|
1989
|
+
setProjectActionState("error");
|
|
1990
|
+
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
const handleServerLoad = async (id) => {
|
|
1994
|
+
if (!onServerLoad) return;
|
|
1995
|
+
const file = await onServerLoad(id);
|
|
1996
|
+
await handleProjectImport(file);
|
|
1997
|
+
};
|
|
1998
|
+
const handleServerDelete = async (id) => {
|
|
1999
|
+
if (!onServerDelete) return;
|
|
2000
|
+
await onServerDelete(id);
|
|
2001
|
+
await fetchServerProjects();
|
|
2002
|
+
};
|
|
2003
|
+
const handleComputeSyncDiff = async () => {
|
|
2004
|
+
if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
|
|
2005
|
+
const projects = await onFetchServerProjects();
|
|
2006
|
+
if (projects.length === 0) return { localOnly: [...history], serverOnly: [] };
|
|
2007
|
+
const file = await onServerLoad(projects[0].id);
|
|
2008
|
+
const serverData = await importProjectFromZip(file);
|
|
2009
|
+
syncServerDataRef.current = serverData;
|
|
2010
|
+
const localOnly = history.filter((h) => !(serverData.history || []).find((s) => s.id === h.id));
|
|
2011
|
+
const serverOnly = (serverData.history || []).filter((s) => !history.find((h) => h.id === s.id));
|
|
2012
|
+
return { localOnly, serverOnly };
|
|
2013
|
+
};
|
|
2014
|
+
const handleExecuteSync = async (selectedLocalIds) => {
|
|
2015
|
+
const serverData = syncServerDataRef.current;
|
|
2016
|
+
if (!serverData || !onServerSave) return;
|
|
2017
|
+
setProjectActionState("working-full");
|
|
2018
|
+
try {
|
|
2019
|
+
const selectedLocal = history.filter((h) => selectedLocalIds.includes(h.id));
|
|
2020
|
+
const mergedHistory = [...serverData.history || [], ...selectedLocal];
|
|
2021
|
+
const { base64 } = await exportProjectToZip(
|
|
2022
|
+
serverData.nodes || nodes,
|
|
2023
|
+
serverData.edges || edges,
|
|
2024
|
+
mergedHistory,
|
|
2025
|
+
serverData.galleryItems || galleryItems,
|
|
2026
|
+
serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
|
|
2027
|
+
serverData.workspaceTags || workspaceTags
|
|
2028
|
+
);
|
|
2029
|
+
await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
|
|
2030
|
+
await fetchServerProjects();
|
|
2031
|
+
setProjectActionState("done");
|
|
2032
|
+
setTimeout(() => setProjectActionState("idle"), 2e3);
|
|
2033
|
+
} catch {
|
|
2034
|
+
setProjectActionState("error");
|
|
2035
|
+
setTimeout(() => setProjectActionState("idle"), 4e3);
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2038
|
+
(0, import_react14.useEffect)(() => {
|
|
2039
|
+
if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
|
|
2040
|
+
}, [activeTab]);
|
|
2041
|
+
if (isFullscreen && currentResult?.base64) {
|
|
2042
|
+
const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
|
|
2043
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2044
|
+
"div",
|
|
2045
|
+
{
|
|
2046
|
+
className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
|
|
2047
|
+
onTouchStart: handleFsTouchStart,
|
|
2048
|
+
onTouchMove: handleFsTouchMove,
|
|
2049
|
+
onTouchEnd: handleFsTouchEnd,
|
|
2050
|
+
children: [
|
|
2051
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2052
|
+
"img",
|
|
1156
2053
|
{
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
"
|
|
1162
|
-
|
|
2054
|
+
src: fsBase64,
|
|
2055
|
+
alt: "",
|
|
2056
|
+
draggable: false,
|
|
2057
|
+
style: {
|
|
2058
|
+
width: "100%",
|
|
2059
|
+
height: "100%",
|
|
2060
|
+
objectFit: "contain",
|
|
2061
|
+
transform: `scale(${zoomScale}) translate(${zoomOffset.x / zoomScale}px, ${zoomOffset.y / zoomScale}px)`,
|
|
2062
|
+
transformOrigin: "center center",
|
|
2063
|
+
userSelect: "none",
|
|
2064
|
+
pointerEvents: "none"
|
|
2065
|
+
}
|
|
1163
2066
|
}
|
|
1164
2067
|
),
|
|
1165
|
-
|
|
1166
|
-
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
|
|
2069
|
+
zoomScale > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
|
|
2070
|
+
setZoomScale(1);
|
|
2071
|
+
setZoomOffset({ x: 0, y: 0 });
|
|
2072
|
+
}, 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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
|
|
2073
|
+
history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2074
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
|
|
2075
|
+
if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
|
|
2076
|
+
}, 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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
2077
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
|
|
2078
|
+
if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
|
|
2079
|
+
}, 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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
2080
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2081
|
+
currentIndex + 1,
|
|
2082
|
+
" / ",
|
|
2083
|
+
history.length
|
|
2084
|
+
] })
|
|
2085
|
+
] }),
|
|
2086
|
+
zoomScale === 1 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
|
|
2087
|
+
]
|
|
2088
|
+
}
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
if (showStart) {
|
|
2092
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
|
|
2094
|
+
const f = e.target.files?.[0];
|
|
2095
|
+
if (f) handleProjectImport(f);
|
|
2096
|
+
e.target.value = "";
|
|
2097
|
+
} }),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
|
|
2099
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
|
|
2100
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" })
|
|
2101
|
+
] }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2103
|
+
"button",
|
|
2104
|
+
{
|
|
2105
|
+
onClick: toggleContrast,
|
|
2106
|
+
className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
|
|
2107
|
+
style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
|
|
2108
|
+
children: [
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-start", children: [
|
|
2111
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
|
|
2112
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
|
|
2113
|
+
] })
|
|
2114
|
+
]
|
|
2115
|
+
}
|
|
2116
|
+
),
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
2118
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2119
|
+
"button",
|
|
2120
|
+
{
|
|
2121
|
+
onClick: () => wsInputRef.current?.click(),
|
|
2122
|
+
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",
|
|
2123
|
+
style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
|
|
2124
|
+
children: [
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
|
|
2126
|
+
projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
|
|
2127
|
+
]
|
|
2128
|
+
}
|
|
2129
|
+
),
|
|
2130
|
+
!projectLoaded && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
|
|
2131
|
+
] }),
|
|
2132
|
+
onFetchServerProjects && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
2133
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2134
|
+
"button",
|
|
2135
|
+
{
|
|
2136
|
+
disabled: isLoadingFromServer,
|
|
2137
|
+
onClick: async () => {
|
|
2138
|
+
setIsLoadingFromServer(true);
|
|
2139
|
+
try {
|
|
2140
|
+
const projects = await onFetchServerProjects();
|
|
2141
|
+
setServerProjects(projects);
|
|
2142
|
+
if (projects.length > 0 && onServerLoad) {
|
|
2143
|
+
const file = await onServerLoad(projects[0].id);
|
|
2144
|
+
await handleProjectImport(file);
|
|
2145
|
+
}
|
|
2146
|
+
} catch {
|
|
2147
|
+
} finally {
|
|
2148
|
+
setIsLoadingFromServer(false);
|
|
2149
|
+
}
|
|
2150
|
+
},
|
|
2151
|
+
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",
|
|
2152
|
+
style: { height: 56, background: "#7c3aed" },
|
|
2153
|
+
children: [
|
|
2154
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
|
|
2155
|
+
isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
|
|
2156
|
+
]
|
|
2157
|
+
}
|
|
2158
|
+
),
|
|
2159
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
|
|
2160
|
+
] }),
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
|
|
2162
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
|
|
2163
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
|
|
2164
|
+
{ id: "mobile", icon: "smartphone", label: "Mobile" },
|
|
2165
|
+
{ id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
|
|
2166
|
+
{ id: "desktop", icon: "desktop_windows", label: "Desktop" },
|
|
2167
|
+
{ id: "tablet-landscape", icon: "tablet", label: "Landscape" }
|
|
2168
|
+
].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2169
|
+
"button",
|
|
2170
|
+
{
|
|
2171
|
+
onClick: () => startApp(opt.id),
|
|
2172
|
+
className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
|
|
2173
|
+
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" },
|
|
2174
|
+
children: [
|
|
2175
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
|
|
2176
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
|
|
2177
|
+
]
|
|
2178
|
+
},
|
|
2179
|
+
opt.id
|
|
2180
|
+
)) }),
|
|
2181
|
+
layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
|
|
2182
|
+
layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
|
|
2183
|
+
] })
|
|
2184
|
+
] });
|
|
2185
|
+
}
|
|
2186
|
+
if (layoutChoice === "mobile" || layoutChoice === "mobile-desktop") {
|
|
2187
|
+
const mdMode = layoutChoice === "mobile-desktop";
|
|
2188
|
+
const mdScale = mdMode ? window.innerWidth / 430 : 1;
|
|
2189
|
+
const mdW = mdMode ? 430 : void 0;
|
|
2190
|
+
const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
|
|
2191
|
+
const mobileRoot = /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
|
|
2192
|
+
width: mdMode ? mdW : "100vw",
|
|
2193
|
+
height: mdMode ? mdH : "100dvh",
|
|
2194
|
+
transform: mdMode ? `scale(${mdScale})` : void 0,
|
|
2195
|
+
transformOrigin: mdMode ? "top left" : void 0,
|
|
2196
|
+
...hcStyle || {}
|
|
2197
|
+
}, children: [
|
|
2198
|
+
mobileTab === "stage" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
|
|
2200
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] }),
|
|
2201
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] }),
|
|
2202
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1" }),
|
|
2203
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
2204
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
|
|
2205
|
+
] }),
|
|
2206
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
2207
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2208
|
+
"textarea",
|
|
2209
|
+
{
|
|
2210
|
+
value: activePrompt,
|
|
2211
|
+
onChange: (e) => setActivePrompt(e.target.value),
|
|
2212
|
+
className: "w-full bg-transparent border-none outline-none text-[14px] leading-relaxed text-white/80 resize-none dark-scrollbar px-4 pt-3 pb-3",
|
|
2213
|
+
style: { height: 80 },
|
|
2214
|
+
placeholder: "Prompt eingeben..."
|
|
2215
|
+
}
|
|
2216
|
+
),
|
|
2217
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
|
|
2218
|
+
] }) }),
|
|
2219
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2220
|
+
"button",
|
|
2221
|
+
{
|
|
2222
|
+
onClick: () => handleGenerateImage(),
|
|
2223
|
+
disabled: !activePrompt.trim() || isGenerating,
|
|
2224
|
+
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",
|
|
2225
|
+
style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
|
|
2226
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
|
|
2228
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generiere..." })
|
|
2229
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2230
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
|
|
2231
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generieren" })
|
|
2232
|
+
] })
|
|
2233
|
+
}
|
|
2234
|
+
) }),
|
|
2235
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
|
|
2236
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2237
|
+
"div",
|
|
1167
2238
|
{
|
|
1168
|
-
|
|
1169
|
-
|
|
2239
|
+
className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
|
|
2240
|
+
style: { aspectRatio: "1/1" },
|
|
2241
|
+
onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
|
|
2242
|
+
onTouchEnd: (e) => {
|
|
2243
|
+
if (touchStartX === null) return;
|
|
2244
|
+
const dx = e.changedTouches[0].clientX - touchStartX;
|
|
2245
|
+
if (dx < -50) goToNext();
|
|
2246
|
+
else if (dx > 50) goToPrev();
|
|
2247
|
+
setTouchStartX(null);
|
|
2248
|
+
},
|
|
1170
2249
|
children: [
|
|
1171
|
-
/* @__PURE__ */ (0,
|
|
1172
|
-
|
|
2250
|
+
currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
2252
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
2253
|
+
] }),
|
|
2254
|
+
currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
|
|
2255
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
|
|
2256
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" })
|
|
2258
|
+
] }),
|
|
2259
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
|
|
2260
|
+
!currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
2261
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
|
|
2262
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
2263
|
+
] }),
|
|
2264
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
|
|
2265
|
+
history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2266
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
|
|
2267
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
|
|
2268
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2269
|
+
currentIndex + 1,
|
|
2270
|
+
" / ",
|
|
2271
|
+
history.length
|
|
2272
|
+
] })
|
|
2273
|
+
] })
|
|
1173
2274
|
]
|
|
1174
2275
|
}
|
|
2276
|
+
),
|
|
2277
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex gap-2 mt-3", children: [
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/60", children: "Prompt" })
|
|
2281
|
+
] }),
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
|
|
2284
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
|
|
2285
|
+
] }),
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2287
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
|
|
2288
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] text-white/60", children: "Laden" })
|
|
2289
|
+
] })
|
|
2290
|
+
] })
|
|
2291
|
+
] })
|
|
2292
|
+
] }),
|
|
2293
|
+
mobileTab === "browse" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)) }),
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
2296
|
+
activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
|
|
2297
|
+
setCurrentResult(g);
|
|
2298
|
+
setMobileTab("stage");
|
|
2299
|
+
}, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
2300
|
+
activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2301
|
+
MediaLibrary,
|
|
2302
|
+
{
|
|
2303
|
+
items: galleryItems,
|
|
2304
|
+
onImport: async () => {
|
|
2305
|
+
const media = await onSelectMedia();
|
|
2306
|
+
if (!media?.length) return;
|
|
2307
|
+
setGalleryItems((prev) => [...media.map((m) => ({ id: crypto.randomUUID(), nodeId: "1", base64: `data:${m.mimeType};base64,${m.base64}`, mediaId: m.mediaId, prompt: m.name || "Importiert", timestamp: Date.now(), status: "done", tags: [], type: "import" })), ...prev]);
|
|
2308
|
+
},
|
|
2309
|
+
onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
|
|
2310
|
+
onSelect: (g) => {
|
|
2311
|
+
setCurrentResult(g);
|
|
2312
|
+
setMobileTab("stage");
|
|
2313
|
+
},
|
|
2314
|
+
onGenerateReference: (item) => {
|
|
2315
|
+
handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true });
|
|
2316
|
+
setMobileTab("stage");
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
),
|
|
2320
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
|
|
2321
|
+
setCurrentResult(g);
|
|
2322
|
+
} })
|
|
2323
|
+
] })
|
|
2324
|
+
] }),
|
|
2325
|
+
mobileTab === "tools" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
|
|
2326
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
|
|
2327
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => {
|
|
2328
|
+
setLeftTab("prompt");
|
|
2329
|
+
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2330
|
+
}, 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: [
|
|
2331
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
|
|
2332
|
+
"Prompt"
|
|
2333
|
+
] }),
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { onClick: () => {
|
|
2335
|
+
setLeftTab("hierarchy");
|
|
2336
|
+
if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
|
|
2337
|
+
}, 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: [
|
|
2338
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
|
|
2339
|
+
"Hierarchie"
|
|
2340
|
+
] }),
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2342
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
|
|
2343
|
+
"Setup"
|
|
2344
|
+
] }),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2346
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
|
|
2347
|
+
"Sync"
|
|
2348
|
+
] })
|
|
2349
|
+
] }),
|
|
2350
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
2351
|
+
leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2352
|
+
ListView,
|
|
2353
|
+
{
|
|
2354
|
+
nodes,
|
|
2355
|
+
edges,
|
|
2356
|
+
onNodeChange: (id, l) => setNodes((nds) => nds.map((n) => n.id === id ? { ...n, data: { ...n.data, label: l } } : n)),
|
|
2357
|
+
onAddChild: (pId) => {
|
|
2358
|
+
const newId = crypto.randomUUID();
|
|
2359
|
+
setNodes((nds) => [...nds, { id: newId, type: "custom", position: { x: 0, y: 0 }, data: { label: "", placeholder: "Konzept..." } }]);
|
|
2360
|
+
setEdges((eds) => [...eds, { id: `e-${pId}-${newId}`, source: pId, target: newId }]);
|
|
2361
|
+
setFocusedNodeId(newId);
|
|
2362
|
+
return newId;
|
|
2363
|
+
},
|
|
2364
|
+
onDeleteNode: (id) => {
|
|
2365
|
+
setNodes((nds) => nds.filter((n) => n.id !== id));
|
|
2366
|
+
setEdges((eds) => eds.filter((e) => e.source !== id && e.target !== id));
|
|
2367
|
+
},
|
|
2368
|
+
onFocus: setFocusedNodeId,
|
|
2369
|
+
focusedNodeId,
|
|
2370
|
+
activePath,
|
|
2371
|
+
onGenerate: (id) => {
|
|
2372
|
+
handleSynthesizePrompt(id);
|
|
2373
|
+
setMobileTab("stage");
|
|
2374
|
+
},
|
|
2375
|
+
onGenerateBranch: (id) => {
|
|
2376
|
+
handleSynthesizePrompt(id, true);
|
|
2377
|
+
setMobileTab("stage");
|
|
2378
|
+
},
|
|
2379
|
+
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
2380
|
+
}
|
|
2381
|
+
) }),
|
|
2382
|
+
leftTab === "prompt" && workspaceTags && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
|
|
2383
|
+
handleGenerateImage(prompt);
|
|
2384
|
+
setMobileTab("stage");
|
|
2385
|
+
}, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete }),
|
|
2386
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
2387
|
+
activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2388
|
+
ProjectSyncTab,
|
|
2389
|
+
{
|
|
2390
|
+
onProjectExport: handleProjectExport,
|
|
2391
|
+
onProjectImport: (f) => handleProjectImport(f),
|
|
2392
|
+
onWorkspaceImport: handleWorkspaceImport,
|
|
2393
|
+
projectActionState,
|
|
2394
|
+
serverProjects: onFetchServerProjects ? serverProjects : void 0,
|
|
2395
|
+
onServerSave: onServerSave ? handleServerSave : void 0,
|
|
2396
|
+
onServerLoad: onServerLoad ? handleServerLoad : void 0,
|
|
2397
|
+
onServerDelete: onServerDelete ? handleServerDelete : void 0,
|
|
2398
|
+
onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
|
|
2399
|
+
onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
|
|
2400
|
+
onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
|
|
2401
|
+
}
|
|
1175
2402
|
)
|
|
2403
|
+
] })
|
|
2404
|
+
] }),
|
|
2405
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
|
|
2406
|
+
{ id: "tools", icon: "auto_fix_high", label: "Prompt" },
|
|
2407
|
+
{ id: "stage", icon: "palette", label: "Stage" },
|
|
2408
|
+
{ id: "browse", icon: "photo_library", label: "Galerie" }
|
|
2409
|
+
].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2410
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
|
|
2412
|
+
] }, tab.id)) })
|
|
2413
|
+
] });
|
|
2414
|
+
if (mdMode) {
|
|
2415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
|
|
2416
|
+
}
|
|
2417
|
+
return mobileRoot;
|
|
2418
|
+
}
|
|
2419
|
+
if (layoutChoice === "tablet-landscape") {
|
|
2420
|
+
const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
|
|
2421
|
+
const tlW = 920;
|
|
2422
|
+
const tlH = 520;
|
|
2423
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
|
|
2424
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2425
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] }),
|
|
2427
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] }),
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1 } }),
|
|
2429
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
|
|
2430
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
|
|
2431
|
+
] }),
|
|
2432
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2433
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2434
|
+
"textarea",
|
|
2435
|
+
{
|
|
2436
|
+
value: activePrompt,
|
|
2437
|
+
onChange: (e) => setActivePrompt(e.target.value),
|
|
2438
|
+
style: { width: "100%", background: "transparent", border: "none", outline: "none", fontSize: 12, lineHeight: 1.5, color: "rgba(255,255,255,0.8)", resize: "none", height: 80, padding: "10px 32px 10px 12px", fontFamily: "inherit", boxSizing: "border-box", display: "block" },
|
|
2439
|
+
placeholder: "Prompt eingeben..."
|
|
2440
|
+
}
|
|
2441
|
+
),
|
|
2442
|
+
activePrompt && /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
|
|
2443
|
+
] }) }),
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2445
|
+
"button",
|
|
2446
|
+
{
|
|
2447
|
+
onClick: () => handleGenerateImage(),
|
|
2448
|
+
disabled: !activePrompt.trim() || isGenerating,
|
|
2449
|
+
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" },
|
|
2450
|
+
children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2451
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
2452
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generiere..." })
|
|
2453
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2454
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
|
|
2455
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Generieren" })
|
|
2456
|
+
] })
|
|
2457
|
+
}
|
|
2458
|
+
) }),
|
|
2459
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
|
|
2460
|
+
] }),
|
|
2461
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
|
|
2462
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2463
|
+
"div",
|
|
2464
|
+
{
|
|
2465
|
+
style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
|
|
2466
|
+
onTouchStart: (e) => setTouchStartX(e.touches[0].clientX),
|
|
2467
|
+
onTouchEnd: (e) => {
|
|
2468
|
+
if (touchStartX === null) return;
|
|
2469
|
+
const dx = e.changedTouches[0].clientX - touchStartX;
|
|
2470
|
+
if (dx < -50) goToNext();
|
|
2471
|
+
else if (dx > 50) goToPrev();
|
|
2472
|
+
setTouchStartX(null);
|
|
2473
|
+
},
|
|
2474
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2475
|
+
currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
2476
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
|
|
2477
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
|
|
2478
|
+
] }),
|
|
2479
|
+
currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
|
|
2482
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" })
|
|
2483
|
+
] }),
|
|
2484
|
+
currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
|
|
2485
|
+
!currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
|
|
2486
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
|
|
2487
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
|
|
2488
|
+
] }),
|
|
2489
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
|
|
2490
|
+
history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2494
|
+
currentIndex + 1,
|
|
2495
|
+
" / ",
|
|
2496
|
+
history.length
|
|
2497
|
+
] })
|
|
2498
|
+
] })
|
|
2499
|
+
] })
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
|
|
2503
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
|
|
2505
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Prompt" })
|
|
2506
|
+
] }),
|
|
2507
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
|
|
2509
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Referenz" })
|
|
2510
|
+
] }),
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2512
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "Laden" })
|
|
2514
|
+
] })
|
|
2515
|
+
] })
|
|
2516
|
+
] })
|
|
2517
|
+
] }) });
|
|
2518
|
+
}
|
|
2519
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
|
|
2520
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : 260, transition: "width 0.2s" }, children: [
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
|
|
2523
|
+
!isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-1 gap-1", children: [
|
|
2524
|
+
workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
|
|
2526
|
+
"Prompt"
|
|
2527
|
+
] }),
|
|
2528
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2529
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
|
|
2530
|
+
"Hierarchie"
|
|
2531
|
+
] })
|
|
1176
2532
|
] }),
|
|
1177
|
-
/* @__PURE__ */ (0,
|
|
2533
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" })
|
|
1178
2534
|
] }),
|
|
1179
|
-
!isLeftCollapsed && /* @__PURE__ */ (0,
|
|
1180
|
-
leftTab === "hierarchy" && /* @__PURE__ */ (0,
|
|
2535
|
+
!isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
2536
|
+
leftTab === "hierarchy" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1181
2537
|
ListView,
|
|
1182
2538
|
{
|
|
1183
2539
|
nodes,
|
|
@@ -1201,125 +2557,105 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1201
2557
|
onGenerateBranch: (id) => handleSynthesizePrompt(id, true),
|
|
1202
2558
|
isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
|
|
1203
2559
|
}
|
|
1204
|
-
) }
|
|
1205
|
-
leftTab === "prompt" && workspaceTags && /* @__PURE__ */ (0,
|
|
1206
|
-
|
|
1207
|
-
{
|
|
1208
|
-
workspaceTags,
|
|
1209
|
-
onGenerate: handlePromptTabGenerate,
|
|
1210
|
-
isGenerating: isPromptTabGenerating,
|
|
1211
|
-
feedback: promptFeedback
|
|
1212
|
-
}
|
|
1213
|
-
) }, "prompt")
|
|
1214
|
-
] }) })
|
|
2560
|
+
) }),
|
|
2561
|
+
leftTab === "prompt" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete })
|
|
2562
|
+
] })
|
|
1215
2563
|
] }),
|
|
1216
|
-
/* @__PURE__ */ (0,
|
|
1217
|
-
/* @__PURE__ */ (0,
|
|
1218
|
-
/* @__PURE__ */ (0,
|
|
1219
|
-
/* @__PURE__ */ (0,
|
|
1220
|
-
/* @__PURE__ */ (0,
|
|
2564
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
|
|
2565
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2566
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
2567
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] }),
|
|
2568
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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" }] })
|
|
1221
2569
|
] }),
|
|
1222
|
-
/* @__PURE__ */ (0,
|
|
1223
|
-
/* @__PURE__ */ (0,
|
|
1224
|
-
/* @__PURE__ */ (0,
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2571
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
|
|
1225
2573
|
] })
|
|
1226
2574
|
] }),
|
|
1227
|
-
/* @__PURE__ */ (0,
|
|
1228
|
-
|
|
1229
|
-
/* @__PURE__ */ (0,
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
}
|
|
1237
|
-
),
|
|
1238
|
-
activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime11.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_runtime11.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
1239
|
-
] }) }) }),
|
|
1240
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.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: [
|
|
1241
|
-
isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime11.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: [
|
|
1242
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
|
|
1243
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
|
|
2576
|
+
!isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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..." }),
|
|
2578
|
+
activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime13.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_runtime13.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
|
|
2579
|
+
] }) }),
|
|
2580
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2581
|
+
isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2582
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
|
|
2583
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
|
|
1244
2584
|
] }),
|
|
1245
|
-
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0,
|
|
1246
|
-
/* @__PURE__ */ (0,
|
|
1247
|
-
/* @__PURE__ */ (0,
|
|
1248
|
-
] }) : currentResult.status === "error" ? /* @__PURE__ */ (0,
|
|
1249
|
-
/* @__PURE__ */ (0,
|
|
1250
|
-
/* @__PURE__ */ (0,
|
|
1251
|
-
/* @__PURE__ */ (0,
|
|
1252
|
-
/* @__PURE__ */ (0,
|
|
2585
|
+
currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
|
|
2586
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
|
|
2587
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
|
|
2588
|
+
] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
|
|
2589
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
|
|
2590
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
|
|
2592
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
|
|
1253
2593
|
] }),
|
|
1254
|
-
/* @__PURE__ */ (0,
|
|
1255
|
-
] }) : /* @__PURE__ */ (0,
|
|
1256
|
-
/* @__PURE__ */ (0,
|
|
1257
|
-
/* @__PURE__ */ (0,
|
|
1258
|
-
/* @__PURE__ */ (0,
|
|
1259
|
-
/* @__PURE__ */ (0,
|
|
1260
|
-
/* @__PURE__ */ (0,
|
|
2594
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
|
|
2595
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
|
|
2596
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
|
|
2597
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.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: [
|
|
2598
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
|
|
2599
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
|
|
2600
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
|
|
1261
2601
|
] })
|
|
1262
|
-
] }) : /* @__PURE__ */ (0,
|
|
1263
|
-
/* @__PURE__ */ (0,
|
|
1264
|
-
/* @__PURE__ */ (0,
|
|
2602
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
|
|
2603
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
|
|
1265
2605
|
] })
|
|
1266
2606
|
] }) })
|
|
1267
2607
|
] })
|
|
1268
2608
|
] }),
|
|
1269
|
-
/* @__PURE__ */ (0,
|
|
1270
|
-
/* @__PURE__ */ (0,
|
|
1271
|
-
/* @__PURE__ */ (0,
|
|
2609
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : 320, transition: "width 0.2s" }, children: [
|
|
2610
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => {
|
|
1272
2612
|
setActiveTab(tab);
|
|
1273
2613
|
setIsRightCollapsed(false);
|
|
1274
|
-
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0,
|
|
1275
|
-
/* @__PURE__ */ (0,
|
|
2614
|
+
}, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : "cloud_sync" }) }, tab)) }),
|
|
2615
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
|
|
1276
2616
|
] }),
|
|
1277
|
-
!isRightCollapsed && /* @__PURE__ */ (0,
|
|
1278
|
-
activeTab === "history" && /* @__PURE__ */ (0,
|
|
1279
|
-
activeTab === "gallery" && /* @__PURE__ */ (0,
|
|
2617
|
+
!isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
|
|
2618
|
+
activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
|
|
2619
|
+
activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
1280
2620
|
MediaLibrary,
|
|
1281
2621
|
{
|
|
1282
2622
|
items: galleryItems,
|
|
1283
2623
|
onImport: async () => {
|
|
1284
2624
|
const media = await onSelectMedia();
|
|
1285
2625
|
if (!media?.length) return;
|
|
1286
|
-
|
|
1287
|
-
id: crypto.randomUUID(),
|
|
1288
|
-
nodeId: "1",
|
|
1289
|
-
base64: `data:${m.mimeType};base64,${m.base64}`,
|
|
1290
|
-
mediaId: m.mediaId,
|
|
1291
|
-
prompt: m.name || "Importiert",
|
|
1292
|
-
timestamp: Date.now(),
|
|
1293
|
-
status: "done",
|
|
1294
|
-
tags: [],
|
|
1295
|
-
type: "import"
|
|
1296
|
-
}));
|
|
1297
|
-
setGalleryItems((prev) => [...newItems, ...prev]);
|
|
2626
|
+
setGalleryItems((prev) => [...media.map((m) => ({ id: crypto.randomUUID(), nodeId: "1", base64: `data:${m.mimeType};base64,${m.base64}`, mediaId: m.mediaId, prompt: m.name || "Importiert", timestamp: Date.now(), status: "done", tags: [], type: "import" })), ...prev]);
|
|
1298
2627
|
},
|
|
1299
2628
|
onDelete: (id) => setGalleryItems((g) => g.filter((x) => x.id !== id)),
|
|
1300
2629
|
onSelect: setCurrentResult,
|
|
1301
2630
|
onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
|
|
1302
|
-
}
|
|
1303
|
-
"gallery"
|
|
2631
|
+
}
|
|
1304
2632
|
),
|
|
1305
|
-
activeTab === "inspect" && /* @__PURE__ */ (0,
|
|
1306
|
-
activeTab === "setup" && /* @__PURE__ */ (0,
|
|
1307
|
-
|
|
2633
|
+
activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
|
|
2634
|
+
activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
|
|
2635
|
+
activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2636
|
+
ProjectSyncTab,
|
|
1308
2637
|
{
|
|
1309
2638
|
onProjectExport: handleProjectExport,
|
|
1310
|
-
onProjectImport: handleProjectImport,
|
|
2639
|
+
onProjectImport: (f) => handleProjectImport(f),
|
|
1311
2640
|
onWorkspaceImport: handleWorkspaceImport,
|
|
1312
|
-
projectActionState
|
|
1313
|
-
|
|
1314
|
-
|
|
2641
|
+
projectActionState,
|
|
2642
|
+
serverProjects: onFetchServerProjects ? serverProjects : void 0,
|
|
2643
|
+
onServerSave: onServerSave ? handleServerSave : void 0,
|
|
2644
|
+
onServerLoad: onServerLoad ? handleServerLoad : void 0,
|
|
2645
|
+
onServerDelete: onServerDelete ? handleServerDelete : void 0,
|
|
2646
|
+
onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
|
|
2647
|
+
onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
|
|
2648
|
+
onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0
|
|
2649
|
+
}
|
|
1315
2650
|
)
|
|
1316
|
-
] })
|
|
2651
|
+
] })
|
|
1317
2652
|
] })
|
|
1318
2653
|
] });
|
|
1319
2654
|
}
|
|
1320
2655
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1321
2656
|
0 && (module.exports = {
|
|
1322
2657
|
AvatarArchitectApp,
|
|
2658
|
+
CollapsibleCard,
|
|
1323
2659
|
CompactDropdown,
|
|
1324
2660
|
FaToolsBadge,
|
|
1325
2661
|
GLOBAL_STYLES,
|
|
@@ -1328,6 +2664,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1328
2664
|
ListView,
|
|
1329
2665
|
MediaLibrary,
|
|
1330
2666
|
PillButton,
|
|
2667
|
+
ProjectSyncTab,
|
|
1331
2668
|
PromptTab,
|
|
1332
2669
|
SectionLabel,
|
|
1333
2670
|
SetupPanel,
|
|
@@ -1336,6 +2673,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
|
|
|
1336
2673
|
buildImageGenerationOptions,
|
|
1337
2674
|
buildPromptTabPayload,
|
|
1338
2675
|
cleanAiResponse,
|
|
2676
|
+
createFlowServices,
|
|
1339
2677
|
exportProjectToZip,
|
|
1340
2678
|
formatTreeToMarkdown,
|
|
1341
2679
|
getFormattedTimestamp,
|