@rslsp1/fa-app-tools 1.3.18 → 2.0.13

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.js CHANGED
@@ -259,13 +259,28 @@ var init_project = __esm({
259
259
  }
260
260
  });
261
261
 
262
+ // src/lib/hfEventTypes.ts
263
+ var CURRENT_EVENT_VERSION;
264
+ var init_hfEventTypes = __esm({
265
+ "src/lib/hfEventTypes.ts"() {
266
+ "use strict";
267
+ CURRENT_EVENT_VERSION = { major: 1, minor: 0 };
268
+ }
269
+ });
270
+
262
271
  // src/lib/hfStateService.ts
263
272
  var hfStateService_exports = {};
264
273
  __export(hfStateService_exports, {
265
274
  HF_TOKEN_KEY: () => HF_TOKEN_KEY,
266
275
  getHFToken: () => getHFToken,
276
+ getSessionClientId: () => getSessionClientId,
277
+ hfBatchArchive: () => hfBatchArchive,
278
+ hfBootstrapFromLegacy: () => hfBootstrapFromLegacy,
267
279
  hfDeleteProject: () => hfDeleteProject,
280
+ hfDownloadBinaryByPath: () => hfDownloadBinaryByPath,
281
+ hfDownloadJsonByPath: () => hfDownloadJsonByPath,
268
282
  hfDownloadProject: () => hfDownloadProject,
283
+ hfListDir: () => hfListDir,
269
284
  hfListProjects: () => hfListProjects,
270
285
  hfLoadImageAsBase64: () => hfLoadImageAsBase64,
271
286
  hfLoadMetadata: () => hfLoadMetadata,
@@ -275,7 +290,12 @@ __export(hfStateService_exports, {
275
290
  hfUploadImage: () => hfUploadImage,
276
291
  hfUploadProject: () => hfUploadProject,
277
292
  hfUploadProjectForm: () => hfUploadProjectForm,
278
- setHFToken: () => setHFToken
293
+ hfUploadSmallFile: () => hfUploadSmallFile,
294
+ loadHFState: () => loadHFState,
295
+ loadPendingEvents: () => loadPendingEvents,
296
+ setHFToken: () => setHFToken,
297
+ tsFromEventPath: () => tsFromEventPath,
298
+ writeHFEvent: () => writeHFEvent
279
299
  });
280
300
  function getHFToken() {
281
301
  try {
@@ -290,6 +310,177 @@ function setHFToken(token) {
290
310
  } catch {
291
311
  }
292
312
  }
313
+ function treeUrl(namespace, subdir = "") {
314
+ const parts = [namespace.replace(/\/$/, ""), subdir].filter(Boolean).join("/");
315
+ return `${HF_BASE}/api/datasets/${HF_REPO}/tree/main${parts ? "/" + parts : ""}`;
316
+ }
317
+ async function hfListDir(namespace, subdir, token) {
318
+ const res = await fetch(treeUrl(namespace, subdir), {
319
+ headers: { Authorization: `Bearer ${token}` }
320
+ });
321
+ if (res.status === 404) return [];
322
+ if (!res.ok) throw new Error(`HF list failed: ${res.status} ${res.statusText}`);
323
+ return res.json();
324
+ }
325
+ async function hfDownloadJsonByPath(repoPath, token) {
326
+ const res = await fetch(
327
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
328
+ { headers: { Authorization: `Bearer ${token}` } }
329
+ );
330
+ if (!res.ok) throw new Error(`HF download failed: ${res.status}`);
331
+ return res.json();
332
+ }
333
+ async function hfDownloadBinaryByPath(repoPath, token) {
334
+ const res = await fetch(
335
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/${repoPath}?download=true`,
336
+ { headers: { Authorization: `Bearer ${token}` } }
337
+ );
338
+ if (!res.ok) throw new Error(`HF download binary failed: ${res.status}`);
339
+ return new Uint8Array(await res.arrayBuffer());
340
+ }
341
+ async function hfUploadSmallFile(repoPath, content, token, summary = `Update ${repoPath}`) {
342
+ const bytes = new TextEncoder().encode(content);
343
+ let binary = "";
344
+ bytes.forEach((b) => binary += String.fromCharCode(b));
345
+ const b64 = btoa(binary);
346
+ const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
347
+ method: "POST",
348
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
349
+ body: [
350
+ JSON.stringify({ key: "header", value: { summary, description: "" } }),
351
+ JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: b64 } })
352
+ ].join("\n")
353
+ });
354
+ if (!res.ok) {
355
+ const err = await res.text().catch(() => "");
356
+ throw new Error(`HF upload failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
357
+ }
358
+ }
359
+ async function hfBatchArchive(moves, token, summary) {
360
+ const lines = [
361
+ JSON.stringify({ key: "header", value: { summary, description: "" } }),
362
+ ...moves.flatMap(({ from, to, content }) => {
363
+ const bytes = new TextEncoder().encode(content);
364
+ let binary = "";
365
+ bytes.forEach((b) => binary += String.fromCharCode(b));
366
+ return [
367
+ JSON.stringify({ key: "file", value: { path: to, encoding: "base64", content: btoa(binary) } }),
368
+ JSON.stringify({ key: "deletedFile", value: { path: from } })
369
+ ];
370
+ })
371
+ ];
372
+ const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
373
+ method: "POST",
374
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
375
+ body: lines.join("\n")
376
+ });
377
+ if (!res.ok) {
378
+ const err = await res.text().catch(() => "");
379
+ throw new Error(`HF batch archive failed: ${res.status} \u2014 ${err.slice(0, 200)}`);
380
+ }
381
+ }
382
+ function tsFromEventPath(repoPath) {
383
+ const filename = repoPath.split("/").pop() || "";
384
+ const iso = filename.replace(/_[^_]+\.json$/, "").replace(/-/g, (m, i) => i > 7 ? ":" : m);
385
+ const ts = Date.parse(iso.replace(/T(\d{2}):(\d{2}):(\d{2})\.(\d{3})Z/, "T$1:$2:$3.$4Z"));
386
+ return isNaN(ts) ? 0 : ts;
387
+ }
388
+ async function loadHFState(namespace, token) {
389
+ const files = await hfListDir(namespace, "", token);
390
+ const stateFiles = files.filter((f) => f.type === "file" && /state-[\dT\-]+Z\.zip$/.test(f.path.split("/").pop() || "")).sort((a, b) => b.path.localeCompare(a.path));
391
+ if (!stateFiles.length) return null;
392
+ const zipBytes = await hfDownloadBinaryByPath(stateFiles[0].path, token);
393
+ const zip = await import_jszip2.default.loadAsync(zipBytes);
394
+ const [metadataStr, tagsStr, metaStr] = await Promise.all([
395
+ zip.file("metadata.json")?.async("string"),
396
+ zip.file("tags.json")?.async("string"),
397
+ zip.file("state_meta.json")?.async("string")
398
+ ]);
399
+ if (!metadataStr || !tagsStr || !metaStr) throw new Error("state.zip: fehlende Pflicht-Dateien");
400
+ return {
401
+ metadata: JSON.parse(metadataStr),
402
+ tags: JSON.parse(tagsStr),
403
+ meta: JSON.parse(metaStr)
404
+ };
405
+ }
406
+ async function loadPendingEvents(namespace, token, sinceTs) {
407
+ const files = await hfListDir(namespace, "events", token);
408
+ const pending = files.filter((f) => f.type === "file" && tsFromEventPath(f.path) > sinceTs).sort((a, b) => a.path.localeCompare(b.path));
409
+ const events = await Promise.all(
410
+ pending.map((f) => hfDownloadJsonByPath(f.path, token))
411
+ );
412
+ return events;
413
+ }
414
+ function getSessionClientId() {
415
+ return SESSION_CLIENT_ID;
416
+ }
417
+ function tsToFilename(ts) {
418
+ return new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
419
+ }
420
+ async function writeHFEvent(namespace, token, type, payload, prevTs) {
421
+ const ts = Date.now();
422
+ const uuid = crypto.randomUUID().slice(0, 8);
423
+ const event = {
424
+ v: CURRENT_EVENT_VERSION,
425
+ type,
426
+ ts,
427
+ prevTs,
428
+ clientId: SESSION_CLIENT_ID,
429
+ payload
430
+ };
431
+ const filename = `${tsToFilename(ts)}_${uuid}.json`;
432
+ const repoPath = `${namespace}events/${filename}`;
433
+ await hfUploadSmallFile(repoPath, JSON.stringify(event, null, 2), token, `Event: ${type}`);
434
+ return event;
435
+ }
436
+ async function hfBootstrapFromLegacy(namespace, token, onProgress) {
437
+ const log = (msg) => {
438
+ if (onProgress) onProgress(msg);
439
+ };
440
+ log("Lese tags.json \u2026");
441
+ const tags = await fetch(
442
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/tags.json?download=true`,
443
+ { headers: { Authorization: `Bearer ${token}` } }
444
+ ).then((r) => r.ok ? r.json() : null).catch(() => null);
445
+ log("Lese metadata.json \u2026");
446
+ const metadata = await fetch(
447
+ `${HF_BASE}/datasets/${HF_REPO}/resolve/main/metadata.json?download=true`,
448
+ { headers: { Authorization: `Bearer ${token}` } }
449
+ ).then((r) => r.ok ? r.json() : []).catch(() => []);
450
+ const snapshot = {
451
+ metadata: Array.isArray(metadata) ? metadata : [],
452
+ tags: tags?.by_category ? tags : { by_category: {}, all: [] },
453
+ meta: { consolidatedAt: Date.now(), version: 1 }
454
+ };
455
+ log(`Erstelle state.zip (${snapshot.metadata.length} Bilder, ${snapshot.tags.all?.length ?? 0} Tags) \u2026`);
456
+ const zip = new import_jszip2.default();
457
+ zip.file("metadata.json", JSON.stringify(snapshot.metadata, null, 2));
458
+ zip.file("tags.json", JSON.stringify(snapshot.tags, null, 2));
459
+ zip.file("state_meta.json", JSON.stringify(snapshot.meta, null, 2));
460
+ const zipBytes = await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
461
+ const ts = snapshot.meta.consolidatedAt;
462
+ const stateFilename = `state-${new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-")}.zip`;
463
+ const repoPath = `${namespace}${stateFilename}`;
464
+ let binary = "";
465
+ zipBytes.forEach((b) => {
466
+ binary += String.fromCharCode(b);
467
+ });
468
+ const b64 = btoa(binary);
469
+ log("Lade state.zip hoch \u2026");
470
+ const commitRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
471
+ method: "POST",
472
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
473
+ body: [
474
+ JSON.stringify({ key: "header", value: { summary: `Bootstrap: initialer State aus Legacy-Daten`, description: "" } }),
475
+ JSON.stringify({ key: "file", value: { path: repoPath, encoding: "base64", content: b64 } })
476
+ ].join("\n")
477
+ });
478
+ if (!commitRes.ok) {
479
+ const err = await commitRes.text().catch(() => "");
480
+ throw new Error(`HF commit failed: ${commitRes.status} \u2014 ${err.slice(0, 300)}`);
481
+ }
482
+ log(`Fertig \u2014 ${stateFilename}`);
483
+ }
293
484
  async function hfListProjects(token) {
294
485
  const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/tree/main`, {
295
486
  headers: { Authorization: `Bearer ${token}` }
@@ -333,7 +524,6 @@ async function hfUploadProject(zipBase64, name, token) {
333
524
  if (!preRes.ok) throw new Error(`HF preupload failed: ${preRes.status} ${preRes.statusText}`);
334
525
  const preData = await preRes.json();
335
526
  const fileInfo = preData.files?.[0];
336
- const debugInfo = JSON.stringify({ uploadMode: fileInfo?.uploadMode, hasUploadUrl: !!fileInfo?.uploadUrl, hasVerifyUrl: !!fileInfo?.verifyUrl, headerKeys: Object.keys(fileInfo?.header || {}), verifyHeaderKeys: Object.keys(fileInfo?.verifyHeader || {}) });
337
527
  if (!fileInfo?.uploadMode) throw new Error(`HF preupload kein fileInfo: ${JSON.stringify(preData)}`);
338
528
  if (fileInfo.uploadMode === "lfs" && fileInfo.uploadUrl) {
339
529
  let uploadStatus = 0;
@@ -379,21 +569,8 @@ async function hfUploadProject(zipBase64, name, token) {
379
569
  }
380
570
  return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size, isLfs: true };
381
571
  }
382
- async function hfUploadProjectForm(zipBase64, name, token) {
383
- const filename = name.endsWith(".zip") ? name : `${name}.zip`;
384
- const binary = atob(zipBase64);
385
- const bytes = new Uint8Array(binary.length);
386
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
387
- const blob = new Blob([bytes], { type: "application/zip" });
388
- const { uploadFile } = await import("@huggingface/hub");
389
- await uploadFile({
390
- repo: { type: "dataset", name: HF_REPO },
391
- credentials: { accessToken: token },
392
- file: { path: filename, content: blob },
393
- branch: "main",
394
- commitTitle: `Upload ${filename}`
395
- });
396
- return { id: filename.replace(/\.zip$/, ""), name: filename.replace(/\.zip$/, ""), path: filename, size: bytes.length, isLfs: true };
572
+ async function hfUploadProjectForm(_zipBase64, _name, _token) {
573
+ throw new Error("hfUploadProjectForm is deprecated. Use hfUploadProject instead.");
397
574
  }
398
575
  async function hfDeleteProject(path, token) {
399
576
  const res = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
@@ -406,16 +583,8 @@ async function hfDeleteProject(path, token) {
406
583
  });
407
584
  if (!res.ok) throw new Error(`HF delete failed: ${res.status} ${res.statusText}`);
408
585
  }
409
- async function hfSaveTags(workspaceTags, token) {
410
- const content = new Blob([JSON.stringify(workspaceTags, null, 2)], { type: "application/json" });
411
- const { uploadFile } = await import("@huggingface/hub");
412
- await uploadFile({
413
- repo: { type: "dataset", name: HF_REPO },
414
- credentials: { accessToken: token },
415
- file: { path: "tags.json", content },
416
- branch: "main",
417
- commitTitle: "Auto-sync tags"
418
- });
586
+ async function hfSaveTags(_workspaceTags, _token) {
587
+ throw new Error("hfSaveTags is deprecated. Use writeHFEvent instead.");
419
588
  }
420
589
  async function hfLoadTags(token) {
421
590
  try {
@@ -429,16 +598,8 @@ async function hfLoadTags(token) {
429
598
  return null;
430
599
  }
431
600
  }
432
- async function hfSaveMetadata(entries, token) {
433
- const content = new Blob([JSON.stringify(entries, null, 2)], { type: "application/json" });
434
- const { uploadFile } = await import("@huggingface/hub");
435
- await uploadFile({
436
- repo: { type: "dataset", name: HF_REPO },
437
- credentials: { accessToken: token },
438
- file: { path: "metadata.json", content },
439
- branch: "main",
440
- commitTitle: "Auto-sync metadata"
441
- });
601
+ async function hfSaveMetadata(_entries, _token) {
602
+ throw new Error("hfSaveMetadata is deprecated. Use writeHFEvent instead.");
442
603
  }
443
604
  async function hfLoadMetadata(token) {
444
605
  try {
@@ -457,14 +618,32 @@ async function hfUploadImage(base64, id, token, mimeType = "image/jpeg") {
457
618
  const binary = atob(base64);
458
619
  const bytes = new Uint8Array(binary.length);
459
620
  for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
460
- const content = new Blob([bytes], { type: mimeType });
461
- const { uploadFile } = await import("@huggingface/hub");
462
- await uploadFile({
463
- repo: { type: "dataset", name: HF_REPO },
464
- credentials: { accessToken: token },
465
- file: { path: `images/${id}.${ext}`, content },
466
- branch: "main",
467
- commitTitle: `Add image ${id}`
621
+ const hashBuffer = await crypto.subtle.digest("SHA-256", bytes);
622
+ const oid = Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
623
+ const size = bytes.length;
624
+ const filename = `images/${id}.${ext}`;
625
+ const preRes = await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/preupload/main`, {
626
+ method: "POST",
627
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
628
+ body: JSON.stringify({ files: [{ path: filename, size }] })
629
+ });
630
+ if (!preRes.ok) throw new Error(`HF preupload failed: ${preRes.status}`);
631
+ const preData = await preRes.json();
632
+ const fileInfo = preData.files?.[0];
633
+ if (fileInfo?.uploadUrl) {
634
+ await fetch(fileInfo.uploadUrl, {
635
+ method: "PUT",
636
+ headers: { "Content-Type": mimeType, ...fileInfo.header || {} },
637
+ body: bytes
638
+ });
639
+ }
640
+ await fetch(`${HF_BASE}/api/datasets/${HF_REPO}/commit/main`, {
641
+ method: "POST",
642
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
643
+ body: [
644
+ JSON.stringify({ key: "header", value: { summary: `Add image ${id}`, description: "" } }),
645
+ JSON.stringify({ key: "lfsFile", value: { path: filename, algo: "sha256", oid, size } })
646
+ ].join("\n")
468
647
  });
469
648
  }
470
649
  async function hfLoadImageAsBase64(id, token) {
@@ -488,13 +667,16 @@ async function hfLoadImageAsBase64(id, token) {
488
667
  }
489
668
  return null;
490
669
  }
491
- var HF_BASE, HF_REPO, HF_TOKEN_KEY;
670
+ var import_jszip2, HF_BASE, HF_REPO, HF_TOKEN_KEY, SESSION_CLIENT_ID;
492
671
  var init_hfStateService = __esm({
493
672
  "src/lib/hfStateService.ts"() {
494
673
  "use strict";
674
+ import_jszip2 = __toESM(require("jszip"));
675
+ init_hfEventTypes();
495
676
  HF_BASE = "https://huggingface.co";
496
677
  HF_REPO = "RolandSch/fa-app-state";
497
678
  HF_TOKEN_KEY = "hf-token";
679
+ SESSION_CLIENT_ID = `client-${crypto.randomUUID().slice(0, 8)}`;
498
680
  }
499
681
  });
500
682
 
@@ -524,9 +706,12 @@ __export(index_exports, {
524
706
  SectionLabel: () => SectionLabel,
525
707
  SetupPanel: () => SetupPanel,
526
708
  TagManagerPanel: () => TagManagerPanel,
709
+ applyEvent: () => applyEvent,
710
+ applyEvents: () => applyEvents,
527
711
  autoLabel: () => autoLabel,
528
712
  buildBlendInstruction: () => buildBlendInstruction,
529
713
  buildCompareInstruction: () => buildCompareInstruction,
714
+ buildDag: () => buildDag,
530
715
  buildFallbackPrompt: () => buildFallbackPrompt,
531
716
  buildGenerationPrompt: () => buildGenerationPrompt,
532
717
  buildImageGenerationOptions: () => buildImageGenerationOptions,
@@ -538,29 +723,38 @@ __export(index_exports, {
538
723
  cleanAiResponse: () => cleanAiResponse,
539
724
  createFlowServices: () => createFlowServices,
540
725
  exportProjectToZip: () => exportProjectToZip,
726
+ findForks: () => findForks,
727
+ findTips: () => findTips,
541
728
  formatTreeToMarkdown: () => formatTreeToMarkdown,
542
729
  frameToGeneration: () => frameToGeneration,
543
730
  getFormattedTimestamp: () => getFormattedTimestamp,
544
731
  getHFToken: () => getHFToken,
732
+ getSessionClientId: () => getSessionClientId,
545
733
  groupGenerationsToLabItems: () => groupGenerationsToLabItems,
734
+ hfBatchArchive: () => hfBatchArchive,
735
+ hfBootstrapFromLegacy: () => hfBootstrapFromLegacy,
546
736
  hfDeleteProject: () => hfDeleteProject,
547
737
  hfDownloadProject: () => hfDownloadProject,
738
+ hfListDir: () => hfListDir,
548
739
  hfListProjects: () => hfListProjects,
549
740
  hfLoadImageAsBase64: () => hfLoadImageAsBase64,
550
- hfLoadMetadata: () => hfLoadMetadata,
551
- hfLoadTags: () => hfLoadTags,
552
- hfSaveMetadata: () => hfSaveMetadata,
553
- hfSaveTags: () => hfSaveTags,
554
741
  hfUploadImage: () => hfUploadImage,
555
742
  hfUploadProjectForm: () => hfUploadProjectForm,
743
+ hfUploadSmallFile: () => hfUploadSmallFile,
556
744
  importProjectFromZip: () => importProjectFromZip,
557
745
  injectXMPMetadata: () => injectXMPMetadata,
558
746
  interpretSdkError: () => interpretSdkError,
747
+ loadHFState: () => loadHFState,
748
+ loadPendingEvents: () => loadPendingEvents,
559
749
  parsePromptFile: () => parsePromptFile,
560
750
  parsePromptResponse: () => parsePromptResponse,
561
751
  setHFToken: () => setHFToken,
752
+ topoSort: () => topoSort,
753
+ tsFromEventPath: () => tsFromEventPath,
754
+ useHFState: () => useHFState,
562
755
  useKeyboardNavigation: () => useKeyboardNavigation,
563
- useOnClickOutside: () => useOnClickOutside
756
+ useOnClickOutside: () => useOnClickOutside,
757
+ writeHFEvent: () => writeHFEvent
564
758
  });
565
759
  module.exports = __toCommonJS(index_exports);
566
760
 
@@ -1343,7 +1537,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
1343
1537
  }
1344
1538
 
1345
1539
  // src/components/AvatarArchitectApp.tsx
1346
- var import_react21 = require("react");
1540
+ var import_react23 = require("react");
1347
1541
 
1348
1542
  // src/components/PromptTab.tsx
1349
1543
  var import_react12 = require("react");
@@ -1871,6 +2065,7 @@ var import_react13 = require("react");
1871
2065
  init_hfStateService();
1872
2066
  var import_jsx_runtime12 = require("react/jsx-runtime");
1873
2067
  var ProjectSyncTab = ({
2068
+ topSlot,
1874
2069
  onProjectExport,
1875
2070
  onProjectImport,
1876
2071
  onWorkspaceImport,
@@ -1962,6 +2157,7 @@ var ProjectSyncTab = ({
1962
2157
  if (hfToken) loadHfProjects(hfToken);
1963
2158
  }, [hfToken]);
1964
2159
  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: [
2160
+ topSlot && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { children: topSlot }),
1965
2161
  (onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-4", children: [
1966
2162
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SectionLabel, { children: "Projekt-ZIP" }),
1967
2163
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2", children: [
@@ -2370,14 +2566,255 @@ function toPromptImages(images) {
2370
2566
  // src/components/AvatarArchitectApp.tsx
2371
2567
  init_hfStateService();
2372
2568
 
2569
+ // src/hooks/useHFState.ts
2570
+ var import_react14 = require("react");
2571
+ init_hfStateService();
2572
+
2573
+ // src/lib/hfReducer.ts
2574
+ function applyEvent(state, event) {
2575
+ if (event.v.major > 1) return state;
2576
+ switch (event.type) {
2577
+ case "image_added": {
2578
+ const p = event.payload;
2579
+ if (state.metadata.some((m) => m.id === p.id)) return state;
2580
+ return { ...state, metadata: [...state.metadata, p] };
2581
+ }
2582
+ case "tag_upserted": {
2583
+ const p = event.payload;
2584
+ const tags = state.tags;
2585
+ const cat = tags.by_category[p.category] || [];
2586
+ const existing = cat.find((t) => t.value === p.value);
2587
+ const updated = { label: p.label, value: p.value, is_user_created: p.is_user_created, is_deleted: p.is_deleted };
2588
+ const newCat = existing ? cat.map((t) => t.value === p.value ? { ...t, ...updated } : t) : [...cat, updated];
2589
+ const newAll = tags.all.some((t) => t.value === p.value && t.category === p.category) ? tags.all.map((t) => t.value === p.value && t.category === p.category ? { ...t, ...updated, category: p.category } : t) : [...tags.all, { ...updated, category: p.category }];
2590
+ return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
2591
+ }
2592
+ case "metadata_updated": {
2593
+ const p = event.payload;
2594
+ return {
2595
+ ...state,
2596
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
2597
+ };
2598
+ }
2599
+ default:
2600
+ return state;
2601
+ }
2602
+ }
2603
+ function applyEvents(state, events) {
2604
+ return events.reduce(applyEvent, state);
2605
+ }
2606
+
2607
+ // src/lib/hfDag.ts
2608
+ function buildDag(events) {
2609
+ const dag = /* @__PURE__ */ new Map();
2610
+ for (const event of events) {
2611
+ dag.set(event.ts, { event, children: [] });
2612
+ }
2613
+ for (const event of events) {
2614
+ for (const parent of event.prevTs) {
2615
+ const node = dag.get(parent);
2616
+ if (node && !node.children.includes(event.ts)) {
2617
+ node.children.push(event.ts);
2618
+ }
2619
+ }
2620
+ }
2621
+ return dag;
2622
+ }
2623
+ function findTips(dag) {
2624
+ return [...dag.values()].filter((n) => n.children.length === 0).map((n) => n.event.ts);
2625
+ }
2626
+ function findForks(dag) {
2627
+ const forks = [];
2628
+ for (const [ts, node] of dag) {
2629
+ if (node.children.length > 1) {
2630
+ forks.push({ parentTs: ts, childTs: node.children });
2631
+ }
2632
+ }
2633
+ return forks;
2634
+ }
2635
+ function topoSort(events) {
2636
+ if (!events.length) return [];
2637
+ const inDegree = /* @__PURE__ */ new Map();
2638
+ const children = /* @__PURE__ */ new Map();
2639
+ const tsSet = new Set(events.map((e) => e.ts));
2640
+ for (const e of events) {
2641
+ inDegree.set(e.ts, 0);
2642
+ children.set(e.ts, []);
2643
+ }
2644
+ for (const e of events) {
2645
+ for (const p of e.prevTs) {
2646
+ if (tsSet.has(p)) {
2647
+ children.get(p).push(e.ts);
2648
+ inDegree.set(e.ts, (inDegree.get(e.ts) || 0) + 1);
2649
+ }
2650
+ }
2651
+ }
2652
+ const queue = events.filter((e) => (inDegree.get(e.ts) || 0) === 0).sort((a, b) => a.ts - b.ts);
2653
+ const result = [];
2654
+ const byTs = new Map(events.map((e) => [e.ts, e]));
2655
+ while (queue.length) {
2656
+ const node = queue.shift();
2657
+ result.push(node);
2658
+ for (const childTs of children.get(node.ts) || []) {
2659
+ const newDeg = (inDegree.get(childTs) || 0) - 1;
2660
+ inDegree.set(childTs, newDeg);
2661
+ if (newDeg === 0) {
2662
+ const child = byTs.get(childTs);
2663
+ const insertAt = queue.findIndex((q) => q.ts > child.ts);
2664
+ if (insertAt === -1) queue.push(child);
2665
+ else queue.splice(insertAt, 0, child);
2666
+ }
2667
+ }
2668
+ }
2669
+ return result;
2670
+ }
2671
+
2672
+ // src/hooks/useHFState.ts
2673
+ var OFFLINE_BUFFER_KEY = "hf-offline-buffer";
2674
+ var POLL_INTERVAL_MS = 3e4;
2675
+ function readOfflineBuffer() {
2676
+ try {
2677
+ return JSON.parse(localStorage.getItem(OFFLINE_BUFFER_KEY) || "[]");
2678
+ } catch {
2679
+ return [];
2680
+ }
2681
+ }
2682
+ function writeOfflineBuffer(events) {
2683
+ try {
2684
+ localStorage.setItem(OFFLINE_BUFFER_KEY, JSON.stringify(events));
2685
+ } catch {
2686
+ }
2687
+ }
2688
+ function useHFState(token, namespace) {
2689
+ const [state, setState] = (0, import_react14.useState)(null);
2690
+ const [isLoading, setIsLoading] = (0, import_react14.useState)(false);
2691
+ const [error, setError] = (0, import_react14.useState)(null);
2692
+ const [eventCount, setEventCount] = (0, import_react14.useState)(0);
2693
+ const [forks, setForks] = (0, import_react14.useState)([]);
2694
+ const [pendingBufferCount, setPendingBufferCount] = (0, import_react14.useState)(readOfflineBuffer().length);
2695
+ const [lastEventTs, setLastEventTs] = (0, import_react14.useState)(0);
2696
+ const [hasStateZip, setHasStateZip] = (0, import_react14.useState)(false);
2697
+ const knownEventPaths = (0, import_react14.useRef)(/* @__PURE__ */ new Set());
2698
+ const allEventsRef = (0, import_react14.useRef)([]);
2699
+ const applyNewEvents = (0, import_react14.useCallback)((snapshot, newEvents) => {
2700
+ if (!newEvents.length && allEventsRef.current.length === 0) {
2701
+ setEventCount(0);
2702
+ return snapshot;
2703
+ }
2704
+ const sorted = topoSort([...allEventsRef.current, ...newEvents]);
2705
+ allEventsRef.current = sorted;
2706
+ const afterConsolidation = sorted.filter((e) => e.ts > snapshot.meta.consolidatedAt);
2707
+ const dag = buildDag(afterConsolidation);
2708
+ setForks(findForks(dag));
2709
+ setEventCount(afterConsolidation.length);
2710
+ if (afterConsolidation.length) setLastEventTs(Math.max(...afterConsolidation.map((e) => e.ts)));
2711
+ return applyEvents(snapshot, afterConsolidation);
2712
+ }, []);
2713
+ const loadFull = (0, import_react14.useCallback)(async () => {
2714
+ if (!token || !namespace) return;
2715
+ setIsLoading(true);
2716
+ setError(null);
2717
+ try {
2718
+ const snapshot = await loadHFState(namespace, token);
2719
+ setHasStateZip(snapshot !== null);
2720
+ const base = snapshot ?? {
2721
+ metadata: [],
2722
+ tags: { by_category: {}, all: [] },
2723
+ meta: { consolidatedAt: 0, version: 1 }
2724
+ };
2725
+ const events = await loadPendingEvents(namespace, token, base.meta.consolidatedAt);
2726
+ events.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
2727
+ allEventsRef.current = [];
2728
+ const finalState = applyNewEvents(base, events);
2729
+ setState(finalState);
2730
+ const buffer = readOfflineBuffer();
2731
+ if (buffer.length) {
2732
+ for (const evt of buffer) {
2733
+ await writeHFEvent(namespace, token, evt.type, evt.payload, evt.prevTs).catch(() => {
2734
+ });
2735
+ }
2736
+ writeOfflineBuffer([]);
2737
+ setPendingBufferCount(0);
2738
+ const freshEvents = await loadPendingEvents(namespace, token, base.meta.consolidatedAt);
2739
+ freshEvents.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
2740
+ setState((prev) => prev ? applyNewEvents(base, freshEvents) : prev);
2741
+ }
2742
+ } catch (e) {
2743
+ setError(e.message);
2744
+ } finally {
2745
+ setIsLoading(false);
2746
+ }
2747
+ }, [token, namespace, applyNewEvents]);
2748
+ const pollNew = (0, import_react14.useCallback)(async () => {
2749
+ if (!token || !namespace || !state) return;
2750
+ try {
2751
+ const events = await loadPendingEvents(namespace, token, state.meta.consolidatedAt);
2752
+ const newEvents = events.filter((e) => !knownEventPaths.current.has(`${e.ts}_${e.clientId}`));
2753
+ if (!newEvents.length) return;
2754
+ newEvents.forEach((e) => knownEventPaths.current.add(`${e.ts}_${e.clientId}`));
2755
+ setState((prev) => prev ? applyNewEvents(prev, newEvents) : prev);
2756
+ } catch {
2757
+ }
2758
+ }, [token, namespace, state, applyNewEvents]);
2759
+ (0, import_react14.useEffect)(() => {
2760
+ if (token && namespace) loadFull();
2761
+ }, [token, namespace]);
2762
+ (0, import_react14.useEffect)(() => {
2763
+ if (!token || !namespace) return;
2764
+ const id = setInterval(pollNew, POLL_INTERVAL_MS);
2765
+ return () => clearInterval(id);
2766
+ }, [token, namespace, pollNew]);
2767
+ const writeEvent2 = (0, import_react14.useCallback)(async (type, payload) => {
2768
+ const prevTs = lastEventTs ? [lastEventTs] : [state?.meta.consolidatedAt ?? 0];
2769
+ console.log("[HF] writeEvent called:", { type, namespace, tokenOk: !!token, prevTs });
2770
+ await pollNew();
2771
+ try {
2772
+ console.log("[HF] writeHFEvent start, path will be:", `${namespace}events/...`);
2773
+ const event = await writeHFEvent(namespace, token, type, payload, prevTs);
2774
+ console.log("[HF] writeHFEvent success:", event.ts);
2775
+ knownEventPaths.current.add(`${event.ts}_${event.clientId}`);
2776
+ setState((prev) => prev ? applyNewEvents(prev, [event]) : prev);
2777
+ setLastEventTs(event.ts);
2778
+ await pollNew();
2779
+ } catch (e) {
2780
+ console.error("[HF] writeHFEvent FAILED, going to offline buffer:", e);
2781
+ const buffer = readOfflineBuffer();
2782
+ const offline = {
2783
+ v: { major: 1, minor: 0 },
2784
+ type,
2785
+ ts: Date.now(),
2786
+ prevTs,
2787
+ clientId: getSessionClientId(),
2788
+ payload
2789
+ };
2790
+ writeOfflineBuffer([...buffer, offline]);
2791
+ setPendingBufferCount(buffer.length + 1);
2792
+ setState((prev) => prev ? applyNewEvents(prev, [offline]) : prev);
2793
+ }
2794
+ }, [namespace, token, lastEventTs, state, pollNew, applyNewEvents]);
2795
+ return {
2796
+ state,
2797
+ isLoading,
2798
+ error,
2799
+ pendingBufferCount,
2800
+ eventCount,
2801
+ forks,
2802
+ writeEvent: writeEvent2,
2803
+ refresh: loadFull,
2804
+ lastEventTs,
2805
+ allEvents: allEventsRef.current,
2806
+ hasStateZip
2807
+ };
2808
+ }
2809
+
2373
2810
  // src/components/labs/LabsTab.tsx
2374
- var import_react19 = require("react");
2811
+ var import_react20 = require("react");
2375
2812
 
2376
2813
  // src/components/labs/LabRemix.tsx
2377
- var import_react15 = require("react");
2814
+ var import_react16 = require("react");
2378
2815
 
2379
2816
  // src/components/labs/LabImagePicker.tsx
2380
- var import_react14 = require("react");
2817
+ var import_react15 = require("react");
2381
2818
  var import_jsx_runtime13 = require("react/jsx-runtime");
2382
2819
  var LabImagePicker = ({
2383
2820
  availableItems,
@@ -2386,8 +2823,8 @@ var LabImagePicker = ({
2386
2823
  onClose,
2387
2824
  title = "Bild w\xE4hlen"
2388
2825
  }) => {
2389
- const [search, setSearch] = (0, import_react14.useState)("");
2390
- const [drillItem, setDrillItem] = (0, import_react14.useState)(null);
2826
+ const [search, setSearch] = (0, import_react15.useState)("");
2827
+ const [drillItem, setDrillItem] = (0, import_react15.useState)(null);
2391
2828
  const filtered = availableItems.filter(
2392
2829
  (item) => !search || item.prompt.toLowerCase().includes(search.toLowerCase())
2393
2830
  );
@@ -2489,13 +2926,13 @@ var LabImagePicker = ({
2489
2926
  // src/components/labs/LabRemix.tsx
2490
2927
  var import_jsx_runtime14 = require("react/jsx-runtime");
2491
2928
  var LabRemix = ({ services, onResult }) => {
2492
- const [showPicker, setShowPicker] = (0, import_react15.useState)(false);
2493
- const [selected, setSelected] = (0, import_react15.useState)(null);
2494
- const [instruction, setInstruction] = (0, import_react15.useState)("");
2495
- const [generatedPrompt, setGeneratedPrompt] = (0, import_react15.useState)("");
2496
- const [resultImage, setResultImage] = (0, import_react15.useState)(null);
2497
- const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react15.useState)(false);
2498
- const [isGeneratingImage, setIsGeneratingImage] = (0, import_react15.useState)(false);
2929
+ const [showPicker, setShowPicker] = (0, import_react16.useState)(false);
2930
+ const [selected, setSelected] = (0, import_react16.useState)(null);
2931
+ const [instruction, setInstruction] = (0, import_react16.useState)("");
2932
+ const [generatedPrompt, setGeneratedPrompt] = (0, import_react16.useState)("");
2933
+ const [resultImage, setResultImage] = (0, import_react16.useState)(null);
2934
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react16.useState)(false);
2935
+ const [isGeneratingImage, setIsGeneratingImage] = (0, import_react16.useState)(false);
2499
2936
  const handleSelectImage = (item, frame) => {
2500
2937
  services.onItemUsed(item);
2501
2938
  setSelected({
@@ -2678,16 +3115,16 @@ var LabRemix = ({ services, onResult }) => {
2678
3115
  };
2679
3116
 
2680
3117
  // src/components/labs/LabBlend.tsx
2681
- var import_react16 = require("react");
3118
+ var import_react17 = require("react");
2682
3119
  var import_jsx_runtime15 = require("react/jsx-runtime");
2683
3120
  var LabBlend = ({ services, onResult }) => {
2684
- const [showPickerFor, setShowPickerFor] = (0, import_react16.useState)(null);
2685
- const [selectedImages, setSelectedImages] = (0, import_react16.useState)([]);
2686
- const [instruction, setInstruction] = (0, import_react16.useState)("");
2687
- const [generatedPrompt, setGeneratedPrompt] = (0, import_react16.useState)("");
2688
- const [resultImage, setResultImage] = (0, import_react16.useState)(null);
2689
- const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react16.useState)(false);
2690
- const [isGeneratingImage, setIsGeneratingImage] = (0, import_react16.useState)(false);
3121
+ const [showPickerFor, setShowPickerFor] = (0, import_react17.useState)(null);
3122
+ const [selectedImages, setSelectedImages] = (0, import_react17.useState)([]);
3123
+ const [instruction, setInstruction] = (0, import_react17.useState)("");
3124
+ const [generatedPrompt, setGeneratedPrompt] = (0, import_react17.useState)("");
3125
+ const [resultImage, setResultImage] = (0, import_react17.useState)(null);
3126
+ const [isGeneratingPrompt, setIsGeneratingPrompt] = (0, import_react17.useState)(false);
3127
+ const [isGeneratingImage, setIsGeneratingImage] = (0, import_react17.useState)(false);
2691
3128
  const handleSelectImage = (index, item, frame) => {
2692
3129
  services.onItemUsed(item);
2693
3130
  const newImg = {
@@ -2874,17 +3311,17 @@ var LabBlend = ({ services, onResult }) => {
2874
3311
  };
2875
3312
 
2876
3313
  // src/components/labs/LabCompare.tsx
2877
- var import_react17 = require("react");
3314
+ var import_react18 = require("react");
2878
3315
  var import_jsx_runtime16 = require("react/jsx-runtime");
2879
3316
  var LabCompare = ({ services, onResult }) => {
2880
- const [showPickerFor, setShowPickerFor] = (0, import_react17.useState)(null);
2881
- const [selectedImages, setSelectedImages] = (0, import_react17.useState)([]);
2882
- const [instruction, setInstruction] = (0, import_react17.useState)("");
2883
- const [analysis, setAnalysis] = (0, import_react17.useState)("");
2884
- const [generatedPrompt, setGeneratedPrompt] = (0, import_react17.useState)("");
2885
- const [resultImage, setResultImage] = (0, import_react17.useState)(null);
2886
- const [isAnalyzing, setIsAnalyzing] = (0, import_react17.useState)(false);
2887
- const [isGeneratingImage, setIsGeneratingImage] = (0, import_react17.useState)(false);
3317
+ const [showPickerFor, setShowPickerFor] = (0, import_react18.useState)(null);
3318
+ const [selectedImages, setSelectedImages] = (0, import_react18.useState)([]);
3319
+ const [instruction, setInstruction] = (0, import_react18.useState)("");
3320
+ const [analysis, setAnalysis] = (0, import_react18.useState)("");
3321
+ const [generatedPrompt, setGeneratedPrompt] = (0, import_react18.useState)("");
3322
+ const [resultImage, setResultImage] = (0, import_react18.useState)(null);
3323
+ const [isAnalyzing, setIsAnalyzing] = (0, import_react18.useState)(false);
3324
+ const [isGeneratingImage, setIsGeneratingImage] = (0, import_react18.useState)(false);
2888
3325
  const handleSelectImage = (index, item, frame) => {
2889
3326
  services.onItemUsed(item);
2890
3327
  const newImg = {
@@ -3047,14 +3484,14 @@ var LabCompare = ({ services, onResult }) => {
3047
3484
  };
3048
3485
 
3049
3486
  // src/components/labs/LabLoop.tsx
3050
- var import_react18 = require("react");
3487
+ var import_react19 = require("react");
3051
3488
  var import_jsx_runtime17 = require("react/jsx-runtime");
3052
3489
  var LabLoop = ({ services, onResult }) => {
3053
- const [rounds, setRounds] = (0, import_react18.useState)([]);
3054
- const [currentInstruction, setCurrentInstruction] = (0, import_react18.useState)("");
3055
- const [showPickerForRound, setShowPickerForRound] = (0, import_react18.useState)(null);
3056
- const [pendingImages, setPendingImages] = (0, import_react18.useState)([]);
3057
- const [isGenerating, setIsGenerating] = (0, import_react18.useState)(false);
3490
+ const [rounds, setRounds] = (0, import_react19.useState)([]);
3491
+ const [currentInstruction, setCurrentInstruction] = (0, import_react19.useState)("");
3492
+ const [showPickerForRound, setShowPickerForRound] = (0, import_react19.useState)(null);
3493
+ const [pendingImages, setPendingImages] = (0, import_react19.useState)([]);
3494
+ const [isGenerating, setIsGenerating] = (0, import_react19.useState)(false);
3058
3495
  const currentPrompt = rounds.length > 0 ? rounds[rounds.length - 1].prompt : "";
3059
3496
  const handleAddImage = (item, frame) => {
3060
3497
  services.onItemUsed(item);
@@ -3218,7 +3655,7 @@ var TABS = [
3218
3655
  { key: "loop", label: "Loop", icon: "loop" }
3219
3656
  ];
3220
3657
  var LabsTab = ({ services, onResult }) => {
3221
- const [activeTab, setActiveTab] = (0, import_react19.useState)("remix");
3658
+ const [activeTab, setActiveTab] = (0, import_react20.useState)("remix");
3222
3659
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col h-full overflow-hidden", children: [
3223
3660
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex border-b border-white/5 shrink-0", children: TABS.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3224
3661
  "button",
@@ -3242,19 +3679,19 @@ var LabsTab = ({ services, onResult }) => {
3242
3679
  };
3243
3680
 
3244
3681
  // src/components/TagManagerPanel.tsx
3245
- var import_react20 = require("react");
3682
+ var import_react21 = require("react");
3246
3683
  var import_jsx_runtime19 = require("react/jsx-runtime");
3247
3684
  function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete, onTagReorder, onTagMove }) {
3248
3685
  const categories = Object.keys(workspaceTags.by_category).filter(
3249
3686
  (cat) => (workspaceTags.by_category[cat] || []).some((t) => !t.is_deleted)
3250
3687
  );
3251
- const [selectedCategory, setSelectedCategory] = (0, import_react20.useState)(categories[0] || "");
3688
+ const [selectedCategory, setSelectedCategory] = (0, import_react21.useState)(categories[0] || "");
3252
3689
  const effectiveCategory = categories.includes(selectedCategory) ? selectedCategory : categories[0] || "";
3253
- const [editingLabel, setEditingLabel] = (0, import_react20.useState)(null);
3254
- const [editState, setEditState] = (0, import_react20.useState)({ label: "", value: "" });
3255
- const [newTag, setNewTag] = (0, import_react20.useState)({ label: "", value: "" });
3256
- const [movingLabel, setMovingLabel] = (0, import_react20.useState)(null);
3257
- const [moveTarget, setMoveTarget] = (0, import_react20.useState)("");
3690
+ const [editingLabel, setEditingLabel] = (0, import_react21.useState)(null);
3691
+ const [editState, setEditState] = (0, import_react21.useState)({ label: "", value: "" });
3692
+ const [newTag, setNewTag] = (0, import_react21.useState)({ label: "", value: "" });
3693
+ const [movingLabel, setMovingLabel] = (0, import_react21.useState)(null);
3694
+ const [moveTarget, setMoveTarget] = (0, import_react21.useState)("");
3258
3695
  const tags = (workspaceTags.by_category[effectiveCategory] || []).filter((t) => !t.is_deleted);
3259
3696
  const otherCategories = categories.filter((c) => c !== effectiveCategory);
3260
3697
  const startEdit = (tag) => {
@@ -3449,10 +3886,449 @@ function TagManagerPanel({ workspaceTags, onTagCreate, onTagUpdate, onTagDelete,
3449
3886
  ] });
3450
3887
  }
3451
3888
 
3452
- // src/components/AvatarArchitectApp.tsx
3889
+ // src/components/HFTestTab.tsx
3890
+ var import_react22 = require("react");
3453
3891
  var import_jsx_runtime20 = require("react/jsx-runtime");
3454
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
3455
- (0, import_react21.useEffect)(() => {
3892
+ var HF_BASE2 = "https://huggingface.co";
3893
+ var HF_REPO2 = "RolandSch/fa-app-state";
3894
+ var TEST_DIR = "test";
3895
+ async function doFetch(label, url, init = {}) {
3896
+ const rawHeaders = init.headers || {};
3897
+ const sanitized = Object.fromEntries(
3898
+ Object.entries(rawHeaders).map(
3899
+ ([k, v]) => k.toLowerCase() === "authorization" ? [k, v.replace(/Bearer\s+\S+/, (m) => "Bearer " + m.slice(7, 14) + "***")] : [k, v]
3900
+ )
3901
+ );
3902
+ const s = {
3903
+ label,
3904
+ method: init.method || "GET",
3905
+ url,
3906
+ reqHeaders: sanitized,
3907
+ reqBody: init.body ? typeof init.body === "string" ? init.body.length > 800 ? init.body.slice(0, 800) + "\n\u2026[truncated]" : init.body : "[binary data]" : void 0
3908
+ };
3909
+ const t0 = Date.now();
3910
+ try {
3911
+ const r = await fetch(url, init);
3912
+ s.durationMs = Date.now() - t0;
3913
+ s.resStatus = r.status;
3914
+ s.resStatusText = r.statusText;
3915
+ const body = await r.text().catch(() => "");
3916
+ s.resBody = body.length > 3e3 ? body.slice(0, 3e3) + "\n\u2026[truncated]" : body;
3917
+ s.ok = r.ok;
3918
+ } catch (e) {
3919
+ s.durationMs = Date.now() - t0;
3920
+ s.error = String(e?.message ?? e);
3921
+ s.ok = false;
3922
+ }
3923
+ return s;
3924
+ }
3925
+ function toBase64(bytes) {
3926
+ let bin = "";
3927
+ bytes.forEach((b) => bin += String.fromCharCode(b));
3928
+ return btoa(bin);
3929
+ }
3930
+ async function sha256hex(bytes) {
3931
+ const buf = await crypto.subtle.digest("SHA-256", bytes);
3932
+ return Array.from(new Uint8Array(buf)).map((b) => b.toString(16).padStart(2, "0")).join("");
3933
+ }
3934
+ function b64ToBytes(b64) {
3935
+ const raw = b64.includes(",") ? b64.split(",")[1] : b64;
3936
+ const bin = atob(raw);
3937
+ const bytes = new Uint8Array(bin.length);
3938
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
3939
+ return bytes;
3940
+ }
3941
+ function rawB64(dataUrl) {
3942
+ return dataUrl.includes(",") ? dataUrl.split(",")[1] : dataUrl;
3943
+ }
3944
+ async function uploadDirect(token, path, b64content) {
3945
+ const ndjson = [
3946
+ JSON.stringify({ key: "header", value: { summary: `HF Test: upload ${path}`, description: "" } }),
3947
+ JSON.stringify({ key: "file", value: { path, encoding: "base64", content: b64content } })
3948
+ ].join("\n");
3949
+ return [await doFetch(
3950
+ "POST commit (direct, kein LFS)",
3951
+ `${HF_BASE2}/api/datasets/${HF_REPO2}/commit/main`,
3952
+ { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" }, body: ndjson }
3953
+ )];
3954
+ }
3955
+ async function uploadLFS(token, path, bytes) {
3956
+ const oid = await sha256hex(bytes);
3957
+ const size = bytes.length;
3958
+ const sample = toBase64(bytes.slice(0, 512));
3959
+ const steps = [];
3960
+ const pre = await doFetch("Preupload (LFS-URL anfordern)", `${HF_BASE2}/api/datasets/${HF_REPO2}/preupload/main`, {
3961
+ method: "POST",
3962
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
3963
+ body: JSON.stringify({ files: [{ path, size, sample }] })
3964
+ });
3965
+ steps.push(pre);
3966
+ if (!pre.ok) return steps;
3967
+ let fi;
3968
+ try {
3969
+ fi = JSON.parse(pre.resBody || "{}").files?.[0];
3970
+ } catch {
3971
+ }
3972
+ if (!fi) {
3973
+ steps.push({ label: "Parse", method: "-", url: "-", reqHeaders: {}, resBody: "Kein files[0] in preupload response", ok: false });
3974
+ return steps;
3975
+ }
3976
+ if (fi.uploadMode === "lfs" && fi.uploadUrl) {
3977
+ const up = await doFetch("PUT to LFS upload URL", fi.uploadUrl, {
3978
+ method: "PUT",
3979
+ headers: { "Content-Type": "application/octet-stream", ...fi.header || {} },
3980
+ body: bytes
3981
+ });
3982
+ steps.push(up);
3983
+ if (!up.ok) return steps;
3984
+ if (fi.verifyUrl) {
3985
+ const ver = await doFetch("POST LFS verify", fi.verifyUrl, {
3986
+ method: "POST",
3987
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...fi.verifyHeader || {} },
3988
+ body: JSON.stringify({ oid, size })
3989
+ });
3990
+ steps.push(ver);
3991
+ if (!ver.ok) return steps;
3992
+ }
3993
+ } else {
3994
+ steps.push({ label: "Info", method: "-", url: "-", reqHeaders: {}, resBody: `uploadMode="${fi.uploadMode}" \xB7 uploadUrl: ${fi.uploadUrl ? "ja" : "nein"} \u2192 ${!fi.uploadUrl ? "bereits in LFS (Deduplizierung)" : "kein LFS n\xF6tig"}`, ok: true });
3995
+ }
3996
+ const ndjson = [
3997
+ JSON.stringify({ key: "header", value: { summary: `HF Test: LFS commit ${path}`, description: "" } }),
3998
+ JSON.stringify({ key: "lfsFile", value: { path, algo: "sha256", oid, size } })
3999
+ ].join("\n");
4000
+ steps.push(await doFetch("Commit (lfsFile reference)", `${HF_BASE2}/api/datasets/${HF_REPO2}/commit/main`, {
4001
+ method: "POST",
4002
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" },
4003
+ body: ndjson
4004
+ }));
4005
+ return steps;
4006
+ }
4007
+ async function uploadViaHubLib(token, path, bytes, mimeType) {
4008
+ const s = { label: "uploadFile() via @huggingface/hub", method: "import()+call", url: "@huggingface/hub", reqHeaders: {} };
4009
+ const t0 = Date.now();
4010
+ try {
4011
+ const { uploadFile } = await import(
4012
+ /* @vite-ignore */
4013
+ "@huggingface/hub"
4014
+ );
4015
+ await uploadFile({
4016
+ repo: { type: "dataset", name: HF_REPO2 },
4017
+ credentials: { accessToken: token },
4018
+ file: { path, content: new Blob([bytes], { type: mimeType }) },
4019
+ branch: "main",
4020
+ commitTitle: `HF Test: hub lib upload ${path}`
4021
+ });
4022
+ s.durationMs = Date.now() - t0;
4023
+ s.resBody = "SUCCESS";
4024
+ s.ok = true;
4025
+ } catch (e) {
4026
+ s.durationMs = Date.now() - t0;
4027
+ s.error = String(e?.message ?? e);
4028
+ s.ok = false;
4029
+ }
4030
+ return [s];
4031
+ }
4032
+ async function uploadViaCdnLib(token, path, bytes, mimeType) {
4033
+ const s = { label: "uploadFile() via esm.sh/@huggingface/hub", method: "import()+call", url: "https://esm.sh/@huggingface/hub", reqHeaders: {} };
4034
+ const t0 = Date.now();
4035
+ try {
4036
+ const { uploadFile } = await import(
4037
+ /* @vite-ignore */
4038
+ "https://esm.sh/@huggingface/hub"
4039
+ );
4040
+ await uploadFile({
4041
+ repo: { type: "dataset", name: HF_REPO2 },
4042
+ credentials: { accessToken: token },
4043
+ file: { path, content: new Blob([bytes], { type: mimeType }) },
4044
+ branch: "main",
4045
+ commitTitle: `HF Test: CDN hub lib upload ${path}`
4046
+ });
4047
+ s.durationMs = Date.now() - t0;
4048
+ s.resBody = "SUCCESS";
4049
+ s.ok = true;
4050
+ } catch (e) {
4051
+ s.durationMs = Date.now() - t0;
4052
+ s.error = String(e?.message ?? e);
4053
+ s.ok = false;
4054
+ }
4055
+ return [s];
4056
+ }
4057
+ async function writeEvent(token, namespace) {
4058
+ const ns = namespace.endsWith("/") ? namespace : namespace ? namespace + "/" : "";
4059
+ const ts = Date.now();
4060
+ const uuid = crypto.randomUUID().slice(0, 8);
4061
+ const isoTs = new Date(ts).toISOString().replace(/:/g, "-").replace(".", "-");
4062
+ const eventPath = `${ns}${TEST_DIR}/events/${isoTs}_${uuid}.json`;
4063
+ const event = JSON.stringify({ v: { major: 1, minor: 0 }, type: "probe", ts, prevTs: [], clientId: "hf-test-tab", payload: { test: true } }, null, 2);
4064
+ const b64 = toBase64(new TextEncoder().encode(event));
4065
+ const ndjson = [
4066
+ JSON.stringify({ key: "header", value: { summary: "HF Test: write event", description: "" } }),
4067
+ JSON.stringify({ key: "file", value: { path: eventPath, encoding: "base64", content: b64 } })
4068
+ ].join("\n");
4069
+ return [await doFetch(
4070
+ `Event \u2192 ${eventPath}`,
4071
+ `${HF_BASE2}/api/datasets/${HF_REPO2}/commit/main`,
4072
+ { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/x-ndjson" }, body: ndjson }
4073
+ )];
4074
+ }
4075
+ function tryFmt(s) {
4076
+ try {
4077
+ return JSON.stringify(JSON.parse(s), null, 2);
4078
+ } catch {
4079
+ return s;
4080
+ }
4081
+ }
4082
+ function CopyBtn({ text }) {
4083
+ const [done, setDone] = (0, import_react22.useState)(false);
4084
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4085
+ "button",
4086
+ {
4087
+ onClick: () => {
4088
+ navigator.clipboard?.writeText(text).catch(() => {
4089
+ });
4090
+ setDone(true);
4091
+ setTimeout(() => setDone(false), 1500);
4092
+ },
4093
+ style: { background: "none", border: "1px solid rgba(255,255,255,0.15)", borderRadius: 5, color: done ? "#4ade80" : "rgba(255,255,255,0.45)", fontSize: 10, padding: "3px 8px", cursor: "pointer", fontFamily: "inherit", display: "flex", alignItems: "center", gap: 3 },
4094
+ children: [
4095
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 11 }, children: done ? "check" : "content_copy" }),
4096
+ done ? "Kopiert" : "Copy"
4097
+ ]
4098
+ }
4099
+ );
4100
+ }
4101
+ function StepView({ step }) {
4102
+ const isSpecial = step.method === "-" || step.method === "import()" || step.method === "import()+call";
4103
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginBottom: 6, background: "rgba(0,0,0,0.3)", borderRadius: 7, padding: "7px 9px", border: `1px solid ${step.ok === false ? "rgba(248,113,113,0.2)" : "rgba(255,255,255,0.05)"}` }, children: [
4104
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 4 }, children: [
4105
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 11, fontWeight: 700, color: step.ok === false ? "#f87171" : "#4ade80" }, children: step.ok === false ? "\u2717" : "\u2713" }),
4106
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 11, fontWeight: 600, color: "rgba(255,255,255,0.7)", flex: 1 }, children: step.label }),
4107
+ step.durationMs !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)" }, children: [
4108
+ step.durationMs,
4109
+ "ms"
4110
+ ] }),
4111
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CopyBtn, { text: JSON.stringify(step, null, 2) })
4112
+ ] }),
4113
+ !isSpecial && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginBottom: 5 }, children: [
4114
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.25)", marginBottom: 2 }, children: [
4115
+ "\u2192 ",
4116
+ step.method,
4117
+ " ",
4118
+ step.url
4119
+ ] }),
4120
+ Object.keys(step.reqHeaders).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.35)", margin: "2px 0", padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 60, overflow: "auto" }, children: Object.entries(step.reqHeaders).map(([k, v]) => `${k}: ${v}`).join("\n") }),
4121
+ step.reqBody && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.35)", margin: "2px 0", padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 80, overflow: "auto" }, children: step.reqBody })
4122
+ ] }),
4123
+ step.error && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { style: { fontSize: 11, color: "#f87171", margin: 0, padding: "3px 5px", background: "rgba(248,113,113,0.05)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: step.error }),
4124
+ !step.error && (step.resStatus !== void 0 || step.resBody) && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
4125
+ step.resStatus !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 11, fontWeight: 700, color: (step.resStatus || 0) < 300 ? "#4ade80" : "#f87171", marginBottom: 3 }, children: [
4126
+ "\u2190 ",
4127
+ step.resStatus,
4128
+ " ",
4129
+ step.resStatusText
4130
+ ] }),
4131
+ step.resBody && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("pre", { style: { fontSize: 9, color: "rgba(255,255,255,0.55)", margin: 0, padding: "3px 5px", background: "rgba(255,255,255,0.03)", borderRadius: 3, whiteSpace: "pre-wrap", wordBreak: "break-all", maxHeight: 180, overflow: "auto" }, children: tryFmt(step.resBody) })
4132
+ ] })
4133
+ ] });
4134
+ }
4135
+ function TestCard({
4136
+ id,
4137
+ label,
4138
+ icon,
4139
+ desc,
4140
+ disabled,
4141
+ state,
4142
+ onRun,
4143
+ expanded,
4144
+ onToggle
4145
+ }) {
4146
+ const hasResult = state && state.status !== "idle";
4147
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginBottom: 8, background: "rgba(255,255,255,0.03)", borderRadius: 10, border: `1px solid ${state?.status === "ok" ? "rgba(74,222,128,0.15)" : state?.status === "error" ? "rgba(248,113,113,0.15)" : "rgba(255,255,255,0.07)"}`, overflow: "hidden" }, children: [
4148
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, padding: "9px 10px" }, children: [
4149
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18, color: state?.status === "ok" ? "#4ade80" : state?.status === "error" ? "#f87171" : state?.status === "running" ? "#60a5fa" : "rgba(255,255,255,0.35)", flexShrink: 0 }, children: state?.status === "ok" ? "check_circle" : state?.status === "error" ? "error" : state?.status === "running" ? "hourglass_top" : icon }),
4150
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
4151
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: 13, fontWeight: 700, color: "#fff" }, children: label }),
4152
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: desc })
4153
+ ] }),
4154
+ hasResult && state?.status !== "running" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: onToggle, style: { background: "none", border: "none", color: "rgba(255,255,255,0.3)", cursor: "pointer", padding: 2, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: expanded ? "expand_less" : "expand_more" }) }),
4155
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4156
+ "button",
4157
+ {
4158
+ onClick: onRun,
4159
+ disabled,
4160
+ style: { background: disabled ? "transparent" : "#0284c7", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 6, color: "#fff", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.05em", padding: "5px 10px", cursor: disabled ? "default" : "pointer", opacity: disabled ? 0.35 : 1, fontFamily: "inherit", flexShrink: 0 },
4161
+ children: state?.status === "running" ? "\u2026" : "Run"
4162
+ }
4163
+ )
4164
+ ] }),
4165
+ hasResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { borderTop: "1px solid rgba(255,255,255,0.05)" }, children: [
4166
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "4px 10px 5px", display: "flex", alignItems: "center", gap: 8 }, children: [
4167
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 11, fontWeight: 700, color: state.status === "ok" ? "#4ade80" : "#f87171" }, children: state.status === "ok" ? "\u2713 OK" : state.status === "running" ? "\u2026" : "\u2717 Fehler" }),
4168
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)" }, children: [
4169
+ state.totalMs,
4170
+ "ms \xB7 ",
4171
+ state.steps.length,
4172
+ " Step",
4173
+ state.steps.length !== 1 ? "s" : ""
4174
+ ] }),
4175
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1 } }),
4176
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CopyBtn, { text: JSON.stringify(state, null, 2) })
4177
+ ] }),
4178
+ expanded && state.steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "0 10px 4px" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(StepView, { step }) }, i))
4179
+ ] })
4180
+ ] });
4181
+ }
4182
+ function HFTestTab({ token, namespace, galleryItems }) {
4183
+ const [selected, setSelected] = (0, import_react22.useState)(null);
4184
+ const [results, setResults] = (0, import_react22.useState)({});
4185
+ const [expanded, setExpanded] = (0, import_react22.useState)({});
4186
+ const withResults = galleryItems.filter((g) => g.base64 && g.status === "done");
4187
+ const setRunning = (id) => setResults((r) => ({ ...r, [id]: { status: "running", steps: [], totalMs: 0 } }));
4188
+ const setDone = (id, steps, t0) => {
4189
+ const ok = steps.length > 0 && steps.every((s) => s.ok !== false);
4190
+ setResults((r) => ({ ...r, [id]: { status: ok ? "ok" : "error", steps, totalMs: Date.now() - t0 } }));
4191
+ setExpanded((e) => ({ ...e, [id]: true }));
4192
+ };
4193
+ const run = async (id) => {
4194
+ const isImgTest = id !== "event";
4195
+ if (isImgTest && !selected?.base64) return;
4196
+ setRunning(id);
4197
+ const t0 = Date.now();
4198
+ let steps = [];
4199
+ try {
4200
+ if (isImgTest) {
4201
+ const bytes = b64ToBytes(selected.base64);
4202
+ const imgPath = `${TEST_DIR}/${selected.id}.jpg`;
4203
+ switch (id) {
4204
+ case "img-direct":
4205
+ steps = await uploadDirect(token, imgPath, rawB64(selected.base64));
4206
+ break;
4207
+ case "img-lfs":
4208
+ steps = await uploadLFS(token, imgPath, bytes);
4209
+ break;
4210
+ case "img-hub":
4211
+ steps = await uploadViaHubLib(token, imgPath, bytes, "image/jpeg");
4212
+ break;
4213
+ case "img-cdn":
4214
+ steps = await uploadViaCdnLib(token, imgPath, bytes, "image/jpeg");
4215
+ break;
4216
+ }
4217
+ } else {
4218
+ steps = await writeEvent(token, namespace);
4219
+ }
4220
+ } catch (e) {
4221
+ steps = [{ label: "Unexpected error", method: "-", url: "-", reqHeaders: {}, error: String(e?.message ?? e), ok: false }];
4222
+ }
4223
+ setDone(id, steps, t0);
4224
+ };
4225
+ const noToken = !token;
4226
+ const noImg = !selected;
4227
+ const imgTests = [
4228
+ { id: "img-direct", label: "Upload Direct", icon: "upload_file", desc: "POST commit + file+base64 (kein LFS)" },
4229
+ { id: "img-lfs", label: "Upload LFS", icon: "cloud_upload", desc: "preupload \u2192 PUT LFS \u2192 verify \u2192 commit" },
4230
+ { id: "img-hub", label: "Upload hub lib", icon: "package_2", desc: "uploadFile() via @huggingface/hub" },
4231
+ { id: "img-cdn", label: "Upload CDN lib", icon: "language", desc: "uploadFile() via esm.sh hub lib" }
4232
+ ];
4233
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflowY: "auto", padding: "12px 10px 80px", boxSizing: "border-box", fontFamily: "inherit" }, children: [
4234
+ noToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { marginBottom: 10, padding: "8px 12px", background: "rgba(248,113,113,0.08)", borderRadius: 8, border: "1px solid rgba(248,113,113,0.2)", fontSize: 12, color: "#f87171" }, children: "Kein HF-Token geladen \u2014 bitte zuerst Token im Sync-Tab eingeben." }),
4235
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginBottom: 14 }, children: [
4236
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 8 }, children: [
4237
+ "Bild ausw\xE4hlen (",
4238
+ withResults.length,
4239
+ " verf\xFCgbar)"
4240
+ ] }),
4241
+ withResults.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.3)", fontStyle: "italic" }, children: "Noch keine Bilder in der Galerie. Generiere zuerst ein Bild oder lade von HF." }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 6 }, children: withResults.slice(0, 12).map((g) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4242
+ "button",
4243
+ {
4244
+ onClick: () => setSelected(g),
4245
+ style: { padding: 0, border: `2px solid ${selected?.id === g.id ? "#0284c7" : "transparent"}`, borderRadius: 6, cursor: "pointer", overflow: "hidden", background: "none", lineHeight: 0 },
4246
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4247
+ "img",
4248
+ {
4249
+ src: g.base64,
4250
+ alt: g.prompt || g.id,
4251
+ style: { width: "100%", aspectRatio: "1", objectFit: "cover", display: "block", borderRadius: 4 }
4252
+ }
4253
+ )
4254
+ },
4255
+ g.id
4256
+ )) }),
4257
+ selected && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginTop: 10, display: "flex", gap: 10, alignItems: "flex-start" }, children: [
4258
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4259
+ "img",
4260
+ {
4261
+ src: selected.base64,
4262
+ style: { width: 80, height: 80, objectFit: "cover", borderRadius: 8, border: "1px solid rgba(255,255,255,0.1)", flexShrink: 0 }
4263
+ }
4264
+ ),
4265
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
4266
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: 11, fontWeight: 700, color: "rgba(255,255,255,0.7)", marginBottom: 2 }, children: "Ausgew\xE4hlt" }),
4267
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", wordBreak: "break-all" }, children: [
4268
+ "ID: ",
4269
+ selected.id
4270
+ ] }),
4271
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.3)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", marginTop: 2 }, children: [
4272
+ "Ziel: test/",
4273
+ selected.id,
4274
+ ".jpg"
4275
+ ] }),
4276
+ selected.prompt && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { fontSize: 10, color: "rgba(255,255,255,0.25)", marginTop: 2, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: selected.prompt })
4277
+ ] })
4278
+ ] })
4279
+ ] }),
4280
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { marginBottom: 18 }, children: [
4281
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8, borderBottom: "1px solid rgba(255,255,255,0.06)", paddingBottom: 4 }, children: [
4282
+ "Bild hochladen \u2192 test/",
4283
+ "{",
4284
+ "id",
4285
+ "}",
4286
+ ".jpg"
4287
+ ] }),
4288
+ imgTests.map((t) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4289
+ TestCard,
4290
+ {
4291
+ id: t.id,
4292
+ label: t.label,
4293
+ icon: t.icon,
4294
+ desc: t.desc,
4295
+ disabled: noToken || noImg || results[t.id]?.status === "running",
4296
+ state: results[t.id],
4297
+ onRun: () => run(t.id),
4298
+ expanded: !!expanded[t.id],
4299
+ onToggle: () => setExpanded((e) => ({ ...e, [t.id]: !e[t.id] }))
4300
+ },
4301
+ t.id
4302
+ ))
4303
+ ] }),
4304
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
4305
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { fontSize: 10, fontWeight: 700, color: "rgba(255,255,255,0.3)", textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8, borderBottom: "1px solid rgba(255,255,255,0.06)", paddingBottom: 4 }, children: [
4306
+ "Event schreiben \u2192 ",
4307
+ namespace || "(kein namespace)",
4308
+ "test/events/"
4309
+ ] }),
4310
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4311
+ TestCard,
4312
+ {
4313
+ id: "event",
4314
+ label: "Write Event",
4315
+ icon: "bolt",
4316
+ desc: "Kleines JSON-Event hochladen (direct commit)",
4317
+ disabled: noToken || results["event"]?.status === "running",
4318
+ state: results["event"],
4319
+ onRun: () => run("event"),
4320
+ expanded: !!expanded["event"],
4321
+ onToggle: () => setExpanded((e) => ({ ...e, event: !e.event }))
4322
+ }
4323
+ )
4324
+ ] })
4325
+ ] });
4326
+ }
4327
+
4328
+ // src/components/AvatarArchitectApp.tsx
4329
+ var import_jsx_runtime21 = require("react/jsx-runtime");
4330
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
4331
+ (0, import_react23.useEffect)(() => {
3456
4332
  const id = "flow-styles";
3457
4333
  if (!document.getElementById(id)) {
3458
4334
  const style = document.createElement("style");
@@ -3461,22 +4337,92 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3461
4337
  document.head.appendChild(style);
3462
4338
  }
3463
4339
  }, []);
3464
- const [showStart, setShowStart] = (0, import_react21.useState)(true);
3465
- const [layoutChoice, setLayoutChoice] = (0, import_react21.useState)(() => {
4340
+ const [showStart, setShowStart] = (0, import_react23.useState)(true);
4341
+ const [layoutChoice, setLayoutChoice] = (0, import_react23.useState)(() => {
3466
4342
  try {
3467
4343
  return localStorage.getItem("aa-layout") || null;
3468
4344
  } catch {
3469
4345
  return null;
3470
4346
  }
3471
4347
  });
3472
- const [projectLoaded, setProjectLoaded] = (0, import_react21.useState)(false);
3473
- const [hfToken, setHfToken] = (0, import_react21.useState)(initialHfToken || "");
3474
- const [hfTokenInput, setHfTokenInput] = (0, import_react21.useState)(initialHfToken || "");
3475
- const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react21.useState)(false);
3476
- const [hfMetadata, setHfMetadata] = (0, import_react21.useState)([]);
3477
- const hfTagSaveTimer = (0, import_react21.useRef)(null);
3478
- const hfMetaSaveQueue = (0, import_react21.useRef)(Promise.resolve());
3479
- const wsInputRef = (0, import_react21.useRef)(null);
4348
+ const [projectLoaded, setProjectLoaded] = (0, import_react23.useState)(false);
4349
+ const [hfToken, setHfToken] = (0, import_react23.useState)(initialHfToken || "");
4350
+ const [hfTokenInput, setHfTokenInput] = (0, import_react23.useState)(initialHfToken || "");
4351
+ const [isLoadingFromHF, setIsLoadingFromHF] = (0, import_react23.useState)(false);
4352
+ const [hfNamespaceLocal, setHfNamespaceLocal] = (0, import_react23.useState)(() => {
4353
+ try {
4354
+ const stored = localStorage.getItem("aa-hf-namespace");
4355
+ if (stored !== null) return stored;
4356
+ return allowDevNamespace ? "app.art-by-rolands.de/" : "";
4357
+ } catch {
4358
+ return "";
4359
+ }
4360
+ });
4361
+ const [hfNamespaceFromServer, setHfNamespaceFromServer] = (0, import_react23.useState)(null);
4362
+ (0, import_react23.useEffect)(() => {
4363
+ if (hfNamespace !== void 0) return;
4364
+ const backendUrl = typeof window !== "undefined" ? window.BACKEND_URL || window.location.origin : null;
4365
+ if (!backendUrl) return;
4366
+ fetch(`${backendUrl}/api/status`).then((r) => r.json()).then((d) => {
4367
+ if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
4368
+ }).catch(() => {
4369
+ });
4370
+ }, [hfNamespace]);
4371
+ const effectiveNamespace = hfNamespace ?? hfNamespaceFromServer ?? hfNamespaceLocal;
4372
+ const {
4373
+ state: hfState,
4374
+ isLoading: isHfRefreshing,
4375
+ pendingBufferCount,
4376
+ eventCount,
4377
+ writeEvent: hfWriteEvent,
4378
+ refresh: refreshHF,
4379
+ hasStateZip
4380
+ } = useHFState(hfToken, effectiveNamespace);
4381
+ const [bootstrapLog, setBootstrapLog] = (0, import_react23.useState)([]);
4382
+ const [isBootstrapping, setIsBootstrapping] = (0, import_react23.useState)(false);
4383
+ const syncTopSlot = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
4384
+ pendingBufferCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { background: "linear-gradient(90deg,#f59e0b,#ef4444)", padding: "4px 10px", fontSize: 11, color: "#fff", borderRadius: 4, marginBottom: 4 }, children: [
4385
+ pendingBufferCount,
4386
+ " \xC4nderung",
4387
+ pendingBufferCount > 1 ? "en" : "",
4388
+ " lokal \u2014 bei Flow-Reload verloren wenn nicht synchronisiert"
4389
+ ] }),
4390
+ eventCount > 100 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { background: "#dc2626", color: "#fff", padding: "5px 10px", borderRadius: 4, marginBottom: 4, fontWeight: 600, fontSize: 11 }, children: [
4391
+ "\u26A0 ",
4392
+ eventCount,
4393
+ " Events nicht konsolidiert \u2014 Konsolidierung dringend empfohlen"
4394
+ ] }),
4395
+ eventCount > 50 && eventCount <= 100 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { background: "#44403c", color: "#a8a29e", padding: "4px 10px", borderRadius: 4, marginBottom: 4, fontSize: 11 }, children: [
4396
+ eventCount,
4397
+ " Events seit letzter Konsolidierung \u2014 Konsolidierung empfohlen"
4398
+ ] }),
4399
+ hfToken && !hasStateZip && !isHfRefreshing && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { background: "#1c1917", border: "1px solid #44403c", borderRadius: 6, padding: "10px 12px" }, children: [
4400
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { fontSize: 12, color: "#a8a29e", marginBottom: 6 }, children: effectiveNamespace ? `Kein State-Snapshot in HF (${effectiveNamespace}) \u2014 aus Legacy-Daten (tags.json + metadata.json) migrieren?` : "Namespace wird geladen\u2026" }),
4401
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4402
+ "button",
4403
+ {
4404
+ disabled: isBootstrapping || !effectiveNamespace,
4405
+ onClick: async () => {
4406
+ setIsBootstrapping(true);
4407
+ setBootstrapLog([]);
4408
+ try {
4409
+ await hfBootstrapFromLegacy(effectiveNamespace, hfToken, (msg) => setBootstrapLog((prev) => [...prev, msg]));
4410
+ setBootstrapLog((prev) => [...prev, "\u2713 Fertig"]);
4411
+ setTimeout(() => refreshHF(), 1500);
4412
+ } catch (e) {
4413
+ setBootstrapLog((prev) => [...prev, `Fehler: ${e.message}`]);
4414
+ } finally {
4415
+ setIsBootstrapping(false);
4416
+ }
4417
+ },
4418
+ style: { padding: "5px 12px", background: "#0ea5e9", color: "#fff", border: "none", borderRadius: 4, cursor: isBootstrapping || !effectiveNamespace ? "not-allowed" : "pointer", fontSize: 11, fontWeight: 600, opacity: isBootstrapping || !effectiveNamespace ? 0.6 : 1 },
4419
+ children: isBootstrapping ? "Migriere\u2026" : "Legacy-Migration starten"
4420
+ }
4421
+ ),
4422
+ bootstrapLog.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { marginTop: 6, fontFamily: "monospace", fontSize: 10, color: "#78716c", lineHeight: 1.6 }, children: bootstrapLog.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { children: l }, i)) })
4423
+ ] })
4424
+ ] });
4425
+ const wsInputRef = (0, import_react23.useRef)(null);
3480
4426
  const startApp = (choice) => {
3481
4427
  try {
3482
4428
  localStorage.setItem("aa-layout", choice);
@@ -3485,70 +4431,110 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3485
4431
  setLayoutChoice(choice);
3486
4432
  setShowStart(false);
3487
4433
  };
3488
- const [nodes, setNodes] = (0, import_react21.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
3489
- const [edges, setEdges] = (0, import_react21.useState)([]);
3490
- const [history, setHistory] = (0, import_react21.useState)([]);
3491
- const [galleryItems, setGalleryItems] = (0, import_react21.useState)([]);
3492
- const galleryItemsRef = (0, import_react21.useRef)([]);
3493
- (0, import_react21.useEffect)(() => {
4434
+ const [nodes, setNodes] = (0, import_react23.useState)([{ id: "1", type: "custom", position: { x: 0, y: 0 }, data: { label: "Fine Art Project", placeholder: "Name..." } }]);
4435
+ const [edges, setEdges] = (0, import_react23.useState)([]);
4436
+ const [history, setHistory] = (0, import_react23.useState)([]);
4437
+ const [galleryItems, setGalleryItems] = (0, import_react23.useState)([]);
4438
+ const galleryItemsRef = (0, import_react23.useRef)([]);
4439
+ (0, import_react23.useEffect)(() => {
3494
4440
  galleryItemsRef.current = galleryItems;
3495
4441
  }, [galleryItems]);
3496
- const [activePrompt, setActivePrompt] = (0, import_react21.useState)("");
3497
- const [isSynthesizing, setIsSynthesizing] = (0, import_react21.useState)(false);
3498
- const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react21.useState)(0);
3499
- const [currentResult, setCurrentResult] = (0, import_react21.useState)(null);
3500
- const [focusedNodeId, setFocusedNodeId] = (0, import_react21.useState)(null);
3501
- const [leftTab, setLeftTab] = (0, import_react21.useState)("prompt");
3502
- const [promptFeedback, setPromptFeedback] = (0, import_react21.useState)(null);
3503
- const [lastPromptPayload, setLastPromptPayload] = (0, import_react21.useState)(null);
3504
- const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react21.useState)(false);
3505
- const [activeTab, setActiveTab] = (0, import_react21.useState)("history");
3506
- const [mobileTab, setMobileTab] = (0, import_react21.useState)("stage");
3507
- const [middlePanel, setMiddlePanel] = (0, import_react21.useState)("stage");
3508
- const [recentLabItems, setRecentLabItems] = (0, import_react21.useState)([]);
3509
- const [aspectRatio, setAspectRatio] = (0, import_react21.useState)("1:1");
3510
- const [selectedModel, setSelectedModel] = (0, import_react21.useState)("\u{1F34C} Nano Banana Pro");
3511
- const [seed, setSeed] = (0, import_react21.useState)(Math.floor(Math.random() * 1e6));
3512
- const [seedMode, setSeedMode] = (0, import_react21.useState)("random");
3513
- const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react21.useState)(false);
3514
- const [isRightCollapsed, setIsRightCollapsed] = (0, import_react21.useState)(false);
3515
- const [leftPanelWidth, setLeftPanelWidth] = (0, import_react21.useState)(() => {
4442
+ const hfImageNotFoundRef = (0, import_react23.useRef)(/* @__PURE__ */ new Set());
4443
+ (0, import_react23.useEffect)(() => {
4444
+ if (!hfState) return;
4445
+ if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
4446
+ const hfIds = new Set(hfState.metadata.map((m) => m.id));
4447
+ const skeletons = hfState.metadata.map((m) => ({
4448
+ id: m.id,
4449
+ nodeId: m.id,
4450
+ prompt: m.prompt,
4451
+ seed: m.seed,
4452
+ model: m.model,
4453
+ tags: m.tags || [],
4454
+ timestamp: m.timestamp,
4455
+ status: "done"
4456
+ }));
4457
+ setGalleryItems((prev) => {
4458
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
4459
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4460
+ return [...localOnly, ...merged];
4461
+ });
4462
+ setHistory((prev) => {
4463
+ const localOnly = prev.filter((g) => !hfIds.has(g.id));
4464
+ const merged = skeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4465
+ return [...localOnly, ...merged];
4466
+ });
4467
+ for (const entry of hfState.metadata) {
4468
+ if (hfImageNotFoundRef.current.has(entry.id)) continue;
4469
+ hfLoadImageAsBase64(entry.id, hfToken).then((b64) => {
4470
+ if (!b64) {
4471
+ hfImageNotFoundRef.current.add(entry.id);
4472
+ return;
4473
+ }
4474
+ const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4475
+ setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4476
+ setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4477
+ }).catch(() => {
4478
+ hfImageNotFoundRef.current.add(entry.id);
4479
+ });
4480
+ }
4481
+ }, [hfState]);
4482
+ const [activePrompt, setActivePrompt] = (0, import_react23.useState)("");
4483
+ const [isSynthesizing, setIsSynthesizing] = (0, import_react23.useState)(false);
4484
+ const [activeGenerationsCount, setActiveGenerationsCount] = (0, import_react23.useState)(0);
4485
+ const [currentResult, setCurrentResult] = (0, import_react23.useState)(null);
4486
+ const [focusedNodeId, setFocusedNodeId] = (0, import_react23.useState)(null);
4487
+ const [leftTab, setLeftTab] = (0, import_react23.useState)("prompt");
4488
+ const [promptFeedback, setPromptFeedback] = (0, import_react23.useState)(null);
4489
+ const [lastPromptPayload, setLastPromptPayload] = (0, import_react23.useState)(null);
4490
+ const [isPromptTabGenerating, setIsPromptTabGenerating] = (0, import_react23.useState)(false);
4491
+ const [activeTab, setActiveTab] = (0, import_react23.useState)("history");
4492
+ const [mobileTab, setMobileTab] = (0, import_react23.useState)("stage");
4493
+ const [middlePanel, setMiddlePanel] = (0, import_react23.useState)("stage");
4494
+ const [recentLabItems, setRecentLabItems] = (0, import_react23.useState)([]);
4495
+ const [aspectRatio, setAspectRatio] = (0, import_react23.useState)("1:1");
4496
+ const [selectedModel, setSelectedModel] = (0, import_react23.useState)("\u{1F34C} Nano Banana Pro");
4497
+ const [seed, setSeed] = (0, import_react23.useState)(Math.floor(Math.random() * 1e6));
4498
+ const [seedMode, setSeedMode] = (0, import_react23.useState)("random");
4499
+ const [isLeftCollapsed, setIsLeftCollapsed] = (0, import_react23.useState)(false);
4500
+ const [isRightCollapsed, setIsRightCollapsed] = (0, import_react23.useState)(false);
4501
+ const [leftPanelWidth, setLeftPanelWidth] = (0, import_react23.useState)(() => {
3516
4502
  try {
3517
4503
  return parseInt(localStorage.getItem("aa-left-width") || "260", 10);
3518
4504
  } catch {
3519
4505
  return 260;
3520
4506
  }
3521
4507
  });
3522
- const [rightPanelWidth, setRightPanelWidth] = (0, import_react21.useState)(() => {
4508
+ const [rightPanelWidth, setRightPanelWidth] = (0, import_react23.useState)(() => {
3523
4509
  try {
3524
4510
  return parseInt(localStorage.getItem("aa-right-width") || "320", 10);
3525
4511
  } catch {
3526
4512
  return 320;
3527
4513
  }
3528
4514
  });
3529
- const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react21.useState)(false);
3530
- const [projectActionState, setProjectActionState] = (0, import_react21.useState)("idle");
3531
- const syncServerDataRef = (0, import_react21.useRef)(null);
3532
- const [workspaceTags, setWorkspaceTags] = (0, import_react21.useState)(null);
3533
- const [serverProjects, setServerProjects] = (0, import_react21.useState)([]);
3534
- const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react21.useState)(false);
3535
- const [highContrast, setHighContrast] = (0, import_react21.useState)(() => {
4515
+ const [isPromptCollapsed, setIsPromptCollapsed] = (0, import_react23.useState)(false);
4516
+ const [projectActionState, setProjectActionState] = (0, import_react23.useState)("idle");
4517
+ const syncServerDataRef = (0, import_react23.useRef)(null);
4518
+ const [workspaceTags, setWorkspaceTags] = (0, import_react23.useState)(null);
4519
+ const [serverProjects, setServerProjects] = (0, import_react23.useState)([]);
4520
+ const [isLoadingFromServer, setIsLoadingFromServer] = (0, import_react23.useState)(false);
4521
+ const [highContrast, setHighContrast] = (0, import_react23.useState)(() => {
3536
4522
  try {
3537
4523
  return localStorage.getItem("aa-contrast") === "high";
3538
4524
  } catch {
3539
4525
  return false;
3540
4526
  }
3541
4527
  });
3542
- const [activeReferenceId, setActiveReferenceId] = (0, import_react21.useState)(null);
3543
- const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react21.useState)(null);
3544
- const [isScanningImage, setIsScanningImage] = (0, import_react21.useState)(false);
3545
- const [touchStartX, setTouchStartX] = (0, import_react21.useState)(null);
3546
- const [isFullscreen, setIsFullscreen] = (0, import_react21.useState)(false);
3547
- const [zoomScale, setZoomScale] = (0, import_react21.useState)(1);
3548
- const [zoomOffset, setZoomOffset] = (0, import_react21.useState)({ x: 0, y: 0 });
3549
- const lastPinchDist = (0, import_react21.useRef)(null);
3550
- const lastTapTime = (0, import_react21.useRef)(0);
3551
- const dragStart = (0, import_react21.useRef)(null);
4528
+ const [activeReferenceId, setActiveReferenceId] = (0, import_react23.useState)(null);
4529
+ const [activeReferenceThumbnail, setActiveReferenceThumbnail] = (0, import_react23.useState)(null);
4530
+ const [isScanningImage, setIsScanningImage] = (0, import_react23.useState)(false);
4531
+ const [touchStartX, setTouchStartX] = (0, import_react23.useState)(null);
4532
+ const [isFullscreen, setIsFullscreen] = (0, import_react23.useState)(false);
4533
+ const [zoomScale, setZoomScale] = (0, import_react23.useState)(1);
4534
+ const [zoomOffset, setZoomOffset] = (0, import_react23.useState)({ x: 0, y: 0 });
4535
+ const lastPinchDist = (0, import_react23.useRef)(null);
4536
+ const lastTapTime = (0, import_react23.useRef)(0);
4537
+ const dragStart = (0, import_react23.useRef)(null);
3552
4538
  const openFullscreen = () => {
3553
4539
  setIsFullscreen(true);
3554
4540
  setZoomScale(1);
@@ -3611,7 +4597,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3611
4597
  setActiveReferenceId(null);
3612
4598
  setActiveReferenceThumbnail(null);
3613
4599
  };
3614
- const labServices = (0, import_react21.useMemo)(() => {
4600
+ const labServices = (0, import_react23.useMemo)(() => {
3615
4601
  const available = groupGenerationsToLabItems([...galleryItems, ...history]);
3616
4602
  return {
3617
4603
  availableItems: available,
@@ -3691,17 +4677,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3691
4677
  setIsScanningImage(false);
3692
4678
  }
3693
4679
  };
3694
- const currentIndex = (0, import_react21.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
3695
- const goToPrev = (0, import_react21.useCallback)(() => {
4680
+ const currentIndex = (0, import_react23.useMemo)(() => history.findIndex((h) => h.id === currentResult?.id), [history, currentResult]);
4681
+ const goToPrev = (0, import_react23.useCallback)(() => {
3696
4682
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
3697
4683
  }, [currentIndex, history]);
3698
- const goToNext = (0, import_react21.useCallback)(() => {
4684
+ const goToNext = (0, import_react23.useCallback)(() => {
3699
4685
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
3700
4686
  }, [currentIndex, history]);
3701
4687
  const hcStyle = highContrast ? { filter: "brightness(1.6) contrast(1.05)" } : void 0;
3702
4688
  const isGenerating = activeGenerationsCount > 0;
3703
4689
  useKeyboardNavigation(history, currentResult, setCurrentResult);
3704
- const getSubtreeFormat = (0, import_react21.useCallback)((nodeId, depth = 0) => {
4690
+ const getSubtreeFormat = (0, import_react23.useCallback)((nodeId, depth = 0) => {
3705
4691
  const node = nodes.find((n) => n.id === nodeId);
3706
4692
  if (!node) return "";
3707
4693
  const childrenIds = edges.filter((e) => e.source === nodeId).map((e) => e.target);
@@ -3709,7 +4695,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3709
4695
  return `${indent}- ${node.data.label || "(unbenannt)"}
3710
4696
  ` + childrenIds.map((id) => getSubtreeFormat(id, depth + 1)).join("");
3711
4697
  }, [nodes, edges]);
3712
- const activePath = (0, import_react21.useMemo)(() => {
4698
+ const activePath = (0, import_react23.useMemo)(() => {
3713
4699
  if (!focusedNodeId) return /* @__PURE__ */ new Set();
3714
4700
  const path = /* @__PURE__ */ new Set([focusedNodeId]);
3715
4701
  let currId = focusedNodeId;
@@ -3741,7 +4727,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3741
4727
  if (prev.id === genId || !options.silent) return finishedGen;
3742
4728
  return prev;
3743
4729
  });
3744
- if (hfToken && base64) {
4730
+ console.log("[HF] handleGenerateImage \u2014 condition check:", { hfToken: !!hfToken, base64: !!base64, effectiveNamespace });
4731
+ if (hfToken && base64 && effectiveNamespace) {
4732
+ hfUploadImage(base64, genId, hfToken).catch((e) => {
4733
+ console.error("[HF] hfUploadImage failed:", e);
4734
+ });
3745
4735
  const entry = {
3746
4736
  id: genId,
3747
4737
  prompt: promptToUse || void 0,
@@ -3751,20 +4741,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3751
4741
  timestamp: Date.now(),
3752
4742
  mimeType: "image/jpeg"
3753
4743
  };
3754
- hfUploadImage(base64, genId, hfToken).catch(() => {
3755
- });
3756
- const token = hfToken;
3757
- hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
3758
- try {
3759
- const existing = await hfLoadMetadata(token);
3760
- const ids = new Set((existing || []).map((e) => e.id));
3761
- if (!ids.has(genId)) {
3762
- const next = [...existing || [], entry];
3763
- await hfSaveMetadata(next, token);
3764
- setHfMetadata(next);
3765
- }
3766
- } catch {
3767
- }
4744
+ console.log("[HF] calling hfWriteEvent, namespace:", effectiveNamespace);
4745
+ hfWriteEvent("image_added", entry).catch((e) => {
4746
+ console.error("[HF] hfWriteEvent outer catch:", e);
3768
4747
  });
3769
4748
  }
3770
4749
  } catch (err) {
@@ -3800,6 +4779,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3800
4779
  all: [...prev.all, { ...newTagOption, category: tag.category }]
3801
4780
  };
3802
4781
  });
4782
+ if (hfToken && effectiveNamespace) {
4783
+ const p = { category: tag.category, label: tag.label, value: tag.value, is_user_created: true };
4784
+ hfWriteEvent("tag_upserted", p).catch(() => {
4785
+ });
4786
+ }
3803
4787
  };
3804
4788
  const handleTagUpdate = (originalLabel, originalCategory, updates) => {
3805
4789
  setWorkspaceTags((prev) => {
@@ -3812,8 +4796,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3812
4796
  );
3813
4797
  return { by_category: { ...prev.by_category, [originalCategory]: updatedCat }, all: updatedAll };
3814
4798
  });
4799
+ if (hfToken && effectiveNamespace) {
4800
+ const p = { category: originalCategory, label: updates.label, value: updates.value, is_user_created: true };
4801
+ hfWriteEvent("tag_upserted", p).catch(() => {
4802
+ });
4803
+ }
3815
4804
  };
3816
4805
  const handleTagDelete = (label, category) => {
4806
+ const tagValue = workspaceTags?.by_category[category]?.find((t) => t.label === label)?.value;
3817
4807
  setWorkspaceTags((prev) => {
3818
4808
  if (!prev) return prev;
3819
4809
  const updatedCat = (prev.by_category[category] || []).map(
@@ -3824,6 +4814,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3824
4814
  );
3825
4815
  return { by_category: { ...prev.by_category, [category]: updatedCat }, all: updatedAll };
3826
4816
  });
4817
+ if (hfToken && effectiveNamespace && tagValue) {
4818
+ const p = { category, label, value: tagValue, is_deleted: true };
4819
+ hfWriteEvent("tag_upserted", p).catch(() => {
4820
+ });
4821
+ }
3827
4822
  };
3828
4823
  const handleTagReorder = (category, reorderedTags) => {
3829
4824
  setWorkspaceTags((prev) => {
@@ -3973,38 +4968,39 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
3973
4968
  await fetchServerProjects();
3974
4969
  };
3975
4970
  const handleHfInitialSync = async (onProgress) => {
3976
- if (!hfToken) return;
3977
- const gens = galleryItems.filter((g) => g.base64 && g.status === "done");
4971
+ if (!hfToken || !effectiveNamespace) return;
4972
+ const existingIds = new Set((hfState?.metadata || []).map((m) => m.id));
4973
+ const gens = galleryItems.filter((g) => g.base64 && g.status === "done" && !existingIds.has(g.id));
3978
4974
  const total = gens.length;
3979
4975
  onProgress(0, total);
3980
4976
  let done = 0;
3981
4977
  for (const gen of gens) {
3982
4978
  const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
3983
4979
  const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
3984
- await hfUploadImage(raw, gen.id, hfToken, mimeType);
4980
+ await hfUploadImage(raw, gen.id, hfToken, mimeType).catch(() => {
4981
+ });
4982
+ const entry = {
4983
+ id: gen.id,
4984
+ prompt: gen.prompt || void 0,
4985
+ seed: gen.seed,
4986
+ model: gen.model,
4987
+ tags: gen.tags || [],
4988
+ timestamp: gen.timestamp,
4989
+ mimeType
4990
+ };
4991
+ await hfWriteEvent("image_added", entry).catch(() => {
4992
+ });
3985
4993
  done++;
3986
4994
  onProgress(done, total);
3987
4995
  }
3988
- const localEntries = gens.map((g) => ({
3989
- id: g.id,
3990
- prompt: g.prompt || void 0,
3991
- seed: g.seed,
3992
- model: g.model,
3993
- tags: g.tags || [],
3994
- timestamp: g.timestamp,
3995
- mimeType: g.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg"
3996
- }));
3997
- const existingMeta = await hfLoadMetadata(hfToken);
3998
- const existingIds = new Set((existingMeta || []).map((e) => e.id));
3999
- const newEntries = localEntries.filter((e) => !existingIds.has(e.id));
4000
- const mergedMeta = [...existingMeta || [], ...newEntries];
4001
- await hfSaveMetadata(mergedMeta, hfToken);
4002
- setHfMetadata(mergedMeta);
4003
4996
  if (workspaceTags) {
4004
- const remoteTags = await hfLoadTags(hfToken).catch(() => null);
4005
- const mergedTags = mergeWorkspaceTags(workspaceTags, remoteTags);
4006
- await hfSaveTags(mergedTags, hfToken);
4007
- setWorkspaceTags(mergedTags);
4997
+ for (const [category, tags] of Object.entries(workspaceTags.by_category)) {
4998
+ for (const tag of tags) {
4999
+ const p = { category, label: tag.label, value: tag.value, is_user_created: tag.is_user_created, is_deleted: tag.is_deleted };
5000
+ await hfWriteEvent("tag_upserted", p).catch(() => {
5001
+ });
5002
+ }
5003
+ }
4008
5004
  }
4009
5005
  };
4010
5006
  const handleComputeSyncDiff = async () => {
@@ -4043,87 +5039,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4043
5039
  setTimeout(() => setProjectActionState("idle"), 4e3);
4044
5040
  }
4045
5041
  };
4046
- (0, import_react21.useEffect)(() => {
5042
+ (0, import_react23.useEffect)(() => {
4047
5043
  if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
4048
5044
  }, [activeTab]);
4049
- const [isHfRefreshing, setIsHfRefreshing] = (0, import_react21.useState)(false);
4050
- const loadFromHF = (0, import_react21.useCallback)(async (token) => {
4051
- setIsHfRefreshing(true);
4052
- try {
4053
- hfLoadTags(token).then((tags) => {
4054
- if (tags?.by_category) setWorkspaceTags(tags);
4055
- }).catch(() => {
4056
- });
4057
- const entries = await hfLoadMetadata(token);
4058
- if (!Array.isArray(entries) || entries.length === 0) return;
4059
- setHfMetadata(entries);
4060
- const hfIds = new Set(entries.map((e) => e.id));
4061
- const hfSkeletons = entries.map((e) => ({
4062
- id: e.id,
4063
- nodeId: e.id,
4064
- prompt: e.prompt,
4065
- seed: e.seed,
4066
- model: e.model,
4067
- tags: e.tags || [],
4068
- timestamp: e.timestamp,
4069
- status: "done"
4070
- }));
4071
- setGalleryItems((prev) => {
4072
- const localOnly = prev.filter((g) => !hfIds.has(g.id));
4073
- const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4074
- return [...localOnly, ...merged];
4075
- });
4076
- setHistory((prev) => {
4077
- const localOnly = prev.filter((g) => !hfIds.has(g.id));
4078
- const merged = hfSkeletons.map((s) => prev.find((g) => g.id === s.id) ?? s);
4079
- return [...localOnly, ...merged];
4080
- });
4081
- for (const entry of entries) {
4082
- hfLoadImageAsBase64(entry.id, token).then((b64) => {
4083
- if (!b64) return;
4084
- const prefix = `data:${entry.mimeType || "image/jpeg"};base64,`;
4085
- setGalleryItems((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4086
- setHistory((prev) => prev.map((g) => g.id === entry.id && !g.base64 ? { ...g, base64: prefix + b64 } : g));
4087
- }).catch(() => {
4088
- });
4089
- }
4090
- const localOnlyItems = galleryItemsRef.current.filter((g) => !hfIds.has(g.id) && g.base64 && g.status === "done");
4091
- if (localOnlyItems.length > 0) {
4092
- hfMetaSaveQueue.current = hfMetaSaveQueue.current.then(async () => {
4093
- try {
4094
- let currentMeta = await hfLoadMetadata(token);
4095
- const existingIds = new Set((currentMeta || []).map((e) => e.id));
4096
- for (const gen of localOnlyItems) {
4097
- if (existingIds.has(gen.id)) continue;
4098
- const raw = gen.base64.includes(",") ? gen.base64.split(",")[1] : gen.base64;
4099
- const mimeType = gen.base64.startsWith("data:image/png") ? "image/png" : "image/jpeg";
4100
- await hfUploadImage(raw, gen.id, token, mimeType).catch(() => {
4101
- });
4102
- const entry = {
4103
- id: gen.id,
4104
- prompt: gen.prompt || void 0,
4105
- seed: gen.seed,
4106
- model: gen.model,
4107
- tags: gen.tags || [],
4108
- timestamp: gen.timestamp,
4109
- mimeType
4110
- };
4111
- currentMeta = [...currentMeta || [], entry];
4112
- existingIds.add(gen.id);
4113
- }
4114
- await hfSaveMetadata(currentMeta, token);
4115
- setHfMetadata(currentMeta);
4116
- } catch {
4117
- }
4118
- });
4119
- }
4120
- } finally {
4121
- setIsHfRefreshing(false);
4122
- }
4123
- }, []);
4124
- (0, import_react21.useEffect)(() => {
4125
- if (hfToken) loadFromHF(hfToken);
4126
- }, [hfToken]);
4127
5045
  const mergeWorkspaceTags = (local, remote) => {
4128
5046
  if (!remote?.by_category) return local;
4129
5047
  const merged = {};
@@ -4143,25 +5061,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4143
5061
  const all = Object.entries(merged).flatMap(([cat, tags]) => tags.map((t) => ({ ...t, category: cat })));
4144
5062
  return { by_category: merged, all };
4145
5063
  };
4146
- (0, import_react21.useEffect)(() => {
4147
- if (!hfToken || !workspaceTags) return;
4148
- if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
4149
- hfTagSaveTimer.current = setTimeout(async () => {
4150
- const remote = await hfLoadTags(hfToken).catch(() => null);
4151
- const merged = mergeWorkspaceTags(workspaceTags, remote);
4152
- await hfSaveTags(merged, hfToken).catch(() => {
4153
- });
4154
- if (Object.values(merged.by_category).some(
4155
- (tags, i) => tags.length !== Object.values(workspaceTags.by_category)[i]?.length
4156
- )) setWorkspaceTags(merged);
4157
- }, 1500);
4158
- return () => {
4159
- if (hfTagSaveTimer.current) clearTimeout(hfTagSaveTimer.current);
4160
- };
4161
- }, [workspaceTags, hfToken]);
4162
5064
  if (isFullscreen && currentResult?.base64) {
4163
5065
  const fsBase64 = currentResult.base64.startsWith("data:") ? currentResult.base64 : `data:image/png;base64,${currentResult.base64}`;
4164
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5066
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4165
5067
  "div",
4166
5068
  {
4167
5069
  className: "fixed inset-0 bg-black z-50 flex items-center justify-center overflow-hidden touch-none",
@@ -4169,7 +5071,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4169
5071
  onTouchMove: handleFsTouchMove,
4170
5072
  onTouchEnd: handleFsTouchEnd,
4171
5073
  children: [
4172
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5074
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4173
5075
  "img",
4174
5076
  {
4175
5077
  src: fsBase64,
@@ -4186,77 +5088,77 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4186
5088
  }
4187
5089
  }
4188
5090
  ),
4189
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: closeFullscreen, className: "absolute top-4 right-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
4190
- zoomScale > 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
5091
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "close" }) }),
5092
+ zoomScale > 1 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => {
4191
5093
  setZoomScale(1);
4192
5094
  setZoomOffset({ x: 0, y: 0 });
4193
- }, className: "absolute top-4 left-4 w-10 h-10 flex items-center justify-center rounded-full bg-black/70 border border-white/20 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
4194
- history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4195
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
5095
+ }, 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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
5096
+ history.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5097
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => {
4196
5098
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
4197
- }, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
4198
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
5099
+ }, 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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
5100
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => {
4199
5101
  if (currentIndex < history.length - 1) setCurrentResult(history[currentIndex + 1]);
4200
- }, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
4201
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute bottom-6 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
5102
+ }, 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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
5103
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
4202
5104
  currentIndex + 1,
4203
5105
  " / ",
4204
5106
  history.length
4205
5107
  ] })
4206
5108
  ] }),
4207
- zoomScale === 1 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
5109
+ zoomScale === 1 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute bottom-6 right-4 text-[9px] text-white/20 font-mono", children: "Pinch zum Zoomen \xB7 Doppeltipp 2.5\xD7" })
4208
5110
  ]
4209
5111
  }
4210
5112
  );
4211
5113
  }
4212
5114
  if (showStart) {
4213
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
4214
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
5115
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
5116
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
4215
5117
  const f = e.target.files?.[0];
4216
5118
  if (f) handleProjectImport(f);
4217
5119
  e.target.value = "";
4218
5120
  } }),
4219
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
4220
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
4221
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
4222
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { className: "text-white text-[13px] font-mono", children: [
5121
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-1", children: [
5122
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
5123
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
5124
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-white text-[13px] font-mono", children: [
4223
5125
  "v",
4224
5126
  LIB_VERSION
4225
5127
  ] })
4226
5128
  ] }),
4227
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5129
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4228
5130
  "button",
4229
5131
  {
4230
5132
  onClick: toggleContrast,
4231
5133
  className: "flex items-center gap-3 px-5 py-3 rounded-2xl border transition-colors",
4232
5134
  style: { borderColor: highContrast ? "rgba(255,255,255,0.3)" : "rgba(255,255,255,0.08)", background: highContrast ? "rgba(255,255,255,0.08)" : "transparent" },
4233
5135
  children: [
4234
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
4235
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-start", children: [
4236
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
4237
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
5136
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.35)" }, children: highContrast ? "light_mode" : "dark_mode" }),
5137
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-start", children: [
5138
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[13px] font-bold", style: { color: highContrast ? "#fff" : "rgba(255,255,255,0.5)" }, children: highContrast ? "Hoher Kontrast" : "Normaler Kontrast" }),
5139
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px]", style: { color: "rgba(255,255,255,0.25)" }, children: "Tippen zum Umschalten" })
4238
5140
  ] })
4239
5141
  ]
4240
5142
  }
4241
5143
  ),
4242
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4243
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5144
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
5145
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4244
5146
  "button",
4245
5147
  {
4246
5148
  onClick: () => wsInputRef.current?.click(),
4247
5149
  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",
4248
5150
  style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
4249
5151
  children: [
4250
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
5152
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
4251
5153
  projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
4252
5154
  ]
4253
5155
  }
4254
5156
  ),
4255
- !projectLoaded && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
5157
+ !projectLoaded && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
4256
5158
  ] }),
4257
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4258
- !initialHfToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex gap-2 w-full", children: [
4259
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5159
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
5160
+ !initialHfToken && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-2 w-full", children: [
5161
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4260
5162
  "input",
4261
5163
  {
4262
5164
  type: "password",
@@ -4272,7 +5174,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4272
5174
  style: { height: 44, background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.7)" }
4273
5175
  }
4274
5176
  ),
4275
- hfTokenInput.trim() && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5177
+ hfTokenInput.trim() && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4276
5178
  "button",
4277
5179
  {
4278
5180
  type: "button",
@@ -4283,7 +5185,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4283
5185
  }
4284
5186
  )
4285
5187
  ] }),
4286
- hfToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5188
+ hfToken && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4287
5189
  "button",
4288
5190
  {
4289
5191
  disabled: isLoadingFromHF,
@@ -4305,15 +5207,15 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4305
5207
  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",
4306
5208
  style: { height: 56, background: "#f59e0b" },
4307
5209
  children: [
4308
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromHF ? " animate-spin" : ""}`, children: isLoadingFromHF ? "sync" : "cloud_download" }),
5210
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromHF ? " animate-spin" : ""}`, children: isLoadingFromHF ? "sync" : "cloud_download" }),
4309
5211
  isLoadingFromHF ? "Laden\u2026" : "Von HF laden"
4310
5212
  ]
4311
5213
  }
4312
5214
  ),
4313
- hfToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand von Hugging Face laden" })
5215
+ hfToken && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand von Hugging Face laden" })
4314
5216
  ] }),
4315
- onFetchServerProjects && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4316
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5217
+ onFetchServerProjects && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
5218
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4317
5219
  "button",
4318
5220
  {
4319
5221
  disabled: isLoadingFromServer,
@@ -4334,35 +5236,57 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4334
5236
  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",
4335
5237
  style: { height: 56, background: "#7c3aed" },
4336
5238
  children: [
4337
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
5239
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
4338
5240
  isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
4339
5241
  ]
4340
5242
  }
4341
5243
  ),
4342
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
5244
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
4343
5245
  ] }),
4344
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
4345
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
4346
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
5246
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
5247
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "Layout w\xE4hlen & starten" }),
5248
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "grid grid-cols-2 gap-2 w-full", children: [
4347
5249
  { id: "mobile", icon: "smartphone", label: "Mobile" },
4348
5250
  { id: "mobile-desktop", icon: "phonelink", label: "Mobile+" },
4349
5251
  { id: "desktop", icon: "desktop_windows", label: "Desktop" },
4350
5252
  { id: "tablet-landscape", icon: "tablet", label: "Landscape" }
4351
- ].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5253
+ ].map((opt) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4352
5254
  "button",
4353
5255
  {
4354
5256
  onClick: () => startApp(opt.id),
4355
5257
  className: "flex flex-col items-center gap-2 py-4 rounded-2xl border transition-colors",
4356
5258
  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" },
4357
5259
  children: [
4358
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
4359
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
5260
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[24px]", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.icon }),
5261
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[11px] font-bold", style: { color: layoutChoice === opt.id ? "#fff" : "rgba(255,255,255,0.4)" }, children: opt.label })
4360
5262
  ]
4361
5263
  },
4362
5264
  opt.id
4363
5265
  )) }),
4364
- layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
4365
- layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
5266
+ layoutChoice === "mobile-desktop" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "Mobil-Layout skaliert f\xFCr Desktop-Modus" }),
5267
+ layoutChoice === "tablet-landscape" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/20 text-[9px] text-center", children: "2-Spalten-Layout f\xFCr Landscape-Tablet im Desktop-Mode" })
5268
+ ] }),
5269
+ !hfNamespace && !hfNamespaceFromServer && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-3 w-full max-w-[280px]", children: [
5270
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-white/25 text-[10px] uppercase tracking-widest font-bold", children: "State:" }),
5271
+ ["app.art-by-rolands.de/", "dev-app.art-by-rolands.de/"].map((ns, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5272
+ "button",
5273
+ {
5274
+ onClick: () => {
5275
+ setHfNamespaceLocal(ns);
5276
+ try {
5277
+ localStorage.setItem("aa-hf-namespace", ns);
5278
+ } catch {
5279
+ }
5280
+ },
5281
+ className: "px-3 py-1 rounded-lg text-[11px] font-bold transition-colors",
5282
+ style: {
5283
+ background: hfNamespaceLocal === ns ? "#e7e5e4" : "#44403c",
5284
+ color: hfNamespaceLocal === ns ? "#1c1917" : "#e7e5e4"
5285
+ },
5286
+ children: i === 0 ? "PROD" : "DEV"
5287
+ },
5288
+ ns
5289
+ ))
4366
5290
  ] })
4367
5291
  ] });
4368
5292
  }
@@ -4371,21 +5295,21 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4371
5295
  const mdScale = mdMode ? window.innerWidth / 430 : 1;
4372
5296
  const mdW = mdMode ? 430 : void 0;
4373
5297
  const mdH = mdMode ? Math.ceil(window.innerHeight / mdScale) : void 0;
4374
- const mobileRoot = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
5298
+ const mobileRoot = /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col bg-[#0e0e0e] text-white overflow-hidden", style: {
4375
5299
  width: mdMode ? mdW : "100vw",
4376
5300
  height: mdMode ? mdH : "100dvh",
4377
5301
  transform: mdMode ? `scale(${mdScale})` : void 0,
4378
5302
  transformOrigin: mdMode ? "top left" : void 0,
4379
5303
  ...hcStyle || {}
4380
5304
  }, children: [
4381
- mobileTab === "labs" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LabsTab, { services: labServices, onResult: (item) => {
5305
+ mobileTab === "labs" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LabsTab, { services: labServices, onResult: (item) => {
4382
5306
  const frame = item.frames[0];
4383
5307
  if (frame?.base64) {
4384
5308
  setCurrentResult(frameToGeneration(frame, item));
4385
5309
  setMobileTab("stage");
4386
5310
  }
4387
5311
  } }) }),
4388
- mobileTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5312
+ mobileTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-col flex-1 min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4389
5313
  TagManagerPanel,
4390
5314
  {
4391
5315
  workspaceTags,
@@ -4396,21 +5320,21 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4396
5320
  onTagMove: handleTagMove
4397
5321
  }
4398
5322
  ) }),
4399
- mobileTab === "stage" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
4400
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
4401
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4402
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
4403
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1" }),
4404
- activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
4405
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
4406
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
4407
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4408
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
4409
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
4410
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
5323
+ mobileTab === "stage" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
5324
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2 px-3 border-b border-white/5 bg-black/30 shrink-0", style: { height: 52 }, children: [
5325
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] }),
5326
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] }),
5327
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1" }),
5328
+ activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden mr-2", style: { height: 28 }, children: [
5329
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
5330
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
5331
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 active:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
5332
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: handleSelectReference, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "add_photo_alternate" }) }),
5333
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: toggleContrast, className: "text-white/20 active:text-white/60 transition-colors mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: highContrast ? "light_mode" : "dark_mode" }) }),
5334
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/20 active:text-white/60 transition-colors mr-1", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "desktop_windows" }) })
4411
5335
  ] }),
4412
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
4413
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5336
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "px-3 pt-3 pb-2 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `relative rounded-xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
5337
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4414
5338
  "textarea",
4415
5339
  {
4416
5340
  value: activePrompt,
@@ -4420,26 +5344,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4420
5344
  placeholder: "Prompt eingeben..."
4421
5345
  }
4422
5346
  ),
4423
- activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center text-white/20 active:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
5347
+ activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "close" }) })
4424
5348
  ] }) }),
4425
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5349
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "px-3 pb-3 shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4426
5350
  "button",
4427
5351
  {
4428
5352
  onClick: () => handleGenerateImage(),
4429
5353
  disabled: !activePrompt.trim() || isGenerating,
4430
5354
  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",
4431
5355
  style: { height: 48, background: activePrompt.trim() && !isGenerating ? "#0284c7" : void 0, border: "1px solid rgba(255,255,255,0.1)" },
4432
- children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4433
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
4434
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generiere..." })
4435
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4436
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
4437
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generieren" })
5356
+ children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5357
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-4 h-4 border-t-2 border-white rounded-full animate-spin" }),
5358
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Generiere..." })
5359
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5360
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "bolt" }),
5361
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Generieren" })
4438
5362
  ] })
4439
5363
  }
4440
5364
  ) }),
4441
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
4442
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
5365
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 min-h-0 px-3 pb-3 flex flex-col", children: [
5366
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4443
5367
  "div",
4444
5368
  {
4445
5369
  className: "w-full rounded-2xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center",
@@ -4453,25 +5377,25 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4453
5377
  setTouchStartX(null);
4454
5378
  },
4455
5379
  children: [
4456
- currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
4457
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
4458
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5380
+ currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
5381
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5382
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[11px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
4459
5383
  ] }),
4460
- currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
4461
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
4462
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
4463
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => handleGenerateImage(currentResult.prompt), className: "px-4 py-2 rounded-lg border border-white/20 text-[13px] text-white/70 active:bg-white/10", children: "Erneut versuchen" })
5384
+ currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "p-6 text-center flex flex-col items-center gap-3", children: [
5385
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-red-400 text-[36px]", children: "warning" }),
5386
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-white/50 text-[13px]", children: currentResult.error?.message }),
5387
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" })
4464
5388
  ] }),
4465
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
4466
- !currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
4467
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
4468
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5389
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: currentResult.base64, className: "w-full h-full object-contain" }),
5390
+ !currentResult && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5391
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[64px]", children: "palette" }),
5392
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
4469
5393
  ] }),
4470
- currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: openFullscreen, className: "absolute top-2 right-2 w-8 h-8 flex items-center justify-center rounded-full bg-black/60 border border-white/10 z-10", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
4471
- history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4472
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: goToPrev, disabled: currentIndex <= 0, className: "absolute left-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
4473
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, className: "absolute right-2 top-1/2 -translate-y-1/2 w-10 h-10 flex items-center justify-center rounded-full bg-black/60 border border-white/10 disabled:opacity-0 transition-opacity", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
4474
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 bg-black/60 rounded-full px-3 py-0.5 text-[10px] text-white/40 font-mono", children: [
5394
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
5395
+ history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5396
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
5397
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
5398
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
4475
5399
  currentIndex + 1,
4476
5400
  " / ",
4477
5401
  history.length
@@ -4480,33 +5404,33 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4480
5404
  ]
4481
5405
  }
4482
5406
  ),
4483
- currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex gap-2 mt-3", children: [
4484
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
4485
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
4486
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/60", children: "Prompt" })
5407
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-2 mt-3", children: [
5408
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5409
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "replay" }),
5410
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[12px] text-white/60", children: "Prompt" })
4487
5411
  ] }),
4488
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl bg-white/10 active:bg-white/15 transition-colors", style: { height: 44 }, children: [
4489
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
4490
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
5412
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5413
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/80", children: "auto_fix_high" }),
5414
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[12px] text-white/80 font-bold", children: "Referenz" })
4491
5415
  ] }),
4492
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: handleDownloadSingle, className: "flex-1 flex items-center justify-center gap-1.5 rounded-xl border border-white/10 bg-white/5 active:bg-white/10 transition-colors", style: { height: 44 }, children: [
4493
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
4494
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] text-white/60", children: "Laden" })
5416
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5417
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px] text-white/60", children: "download" }),
5418
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[12px] text-white/60", children: "Laden" })
4495
5419
  ] })
4496
5420
  ] })
4497
5421
  ] })
4498
5422
  ] }),
4499
- mobileTab === "browse" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
4500
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
4501
- ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActiveTab(tab), className: `flex-1 flex items-center justify-center gap-1.5 transition-colors text-[11px] font-bold uppercase tracking-wide ${activeTab === tab ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)),
4502
- hfToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => loadFromHF(hfToken), disabled: isHfRefreshing, className: "w-12 flex items-center justify-center text-white/20 active:text-white transition-colors disabled:opacity-30", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
5423
+ mobileTab === "browse" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col flex-1 min-h-0", children: [
5424
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
5425
+ ["history", "gallery", "inspect"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : "info" }) }, tab)),
5426
+ hfToken && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => refreshHF(), disabled: isHfRefreshing, className: "w-12 flex items-center justify-center text-white/20 active:text-white transition-colors disabled:opacity-30", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: `material-symbols-outlined text-[20px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) })
4503
5427
  ] }),
4504
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
4505
- activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
5428
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
5429
+ activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: (g) => {
4506
5430
  setCurrentResult(g);
4507
5431
  setMobileTab("stage");
4508
5432
  }, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
4509
- activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5433
+ activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4510
5434
  MediaLibrary,
4511
5435
  {
4512
5436
  items: galleryItems,
@@ -4526,39 +5450,43 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4526
5450
  }
4527
5451
  }
4528
5452
  ),
4529
- activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
5453
+ activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectPanel, { currentResult, history, onSelect: (g) => {
4530
5454
  setCurrentResult(g);
4531
5455
  } })
4532
5456
  ] })
4533
5457
  ] }),
4534
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
4535
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
4536
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => {
5458
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: mobileTab === "tools" ? "flex" : "none" }, className: "flex flex-col flex-1 min-h-0", children: [
5459
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex border-b border-white/5 shrink-0", style: { height: 52 }, children: [
5460
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("button", { onClick: () => {
4537
5461
  setLeftTab("prompt");
4538
5462
  if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
4539
5463
  }, 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: [
4540
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
5464
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "auto_fix_high" }),
4541
5465
  "Prompt"
4542
5466
  ] }),
4543
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => {
5467
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("button", { onClick: () => {
4544
5468
  setLeftTab("hierarchy");
4545
5469
  if (activeTab === "setup" || activeTab === "sync") setActiveTab("history");
4546
5470
  }, 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: [
4547
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
5471
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "account_tree" }),
4548
5472
  "Hierarchie"
4549
5473
  ] }),
4550
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setActiveTab("setup"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "setup" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
4551
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
5474
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5475
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "settings" }),
4552
5476
  "Setup"
4553
5477
  ] }),
4554
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setActiveTab("sync"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "sync" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
4555
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
5478
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5479
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "cloud_sync" }),
4556
5480
  "Sync"
4557
5481
  ] }),
4558
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
5482
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("button", { onClick: () => setActiveTab("hftest"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "hftest" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: [
5483
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "biotech" }),
5484
+ "HF"
5485
+ ] }),
5486
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4559
5487
  ] }),
4560
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
4561
- leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5488
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
5489
+ leftTab === "hierarchy" && activeTab !== "setup" && activeTab !== "sync" && activeTab !== "hftest" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4562
5490
  ListView,
4563
5491
  {
4564
5492
  nodes,
@@ -4589,14 +5517,15 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4589
5517
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
4590
5518
  }
4591
5519
  ) }),
4592
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
5520
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { display: leftTab === "prompt" && activeTab !== "setup" && activeTab !== "sync" && activeTab !== "hftest" ? "flex" : "none" }, className: "absolute inset-0 flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => {
4593
5521
  handleGenerateImage(prompt);
4594
5522
  setMobileTab("stage");
4595
5523
  }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
4596
- activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
4597
- activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5524
+ activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
5525
+ activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4598
5526
  ProjectSyncTab,
4599
5527
  {
5528
+ topSlot: syncTopSlot,
4600
5529
  onProjectExport: handleProjectExport,
4601
5530
  onProjectImport: (f) => handleProjectImport(f),
4602
5531
  onWorkspaceImport: handleWorkspaceImport,
@@ -4617,7 +5546,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4617
5546
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
4618
5547
  }
4619
5548
  ),
4620
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5549
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4621
5550
  TagManagerPanel,
4622
5551
  {
4623
5552
  workspaceTags,
@@ -4627,22 +5556,23 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4627
5556
  onTagReorder: handleTagReorder,
4628
5557
  onTagMove: handleTagMove
4629
5558
  }
4630
- )
5559
+ ),
5560
+ activeTab === "hftest" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HFTestTab, { token: hfToken, namespace: effectiveNamespace, galleryItems }) })
4631
5561
  ] })
4632
5562
  ] }),
4633
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
5563
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex border-t border-white/10 bg-black shrink-0", style: { height: 56, paddingBottom: "env(safe-area-inset-bottom, 0px)" }, children: [
4634
5564
  { id: "tools", icon: "auto_fix_high", label: "Prompt" },
4635
5565
  { id: "stage", icon: "palette", label: "Stage" },
4636
5566
  { id: "labs", icon: "science", label: "Labs" },
4637
5567
  ...workspaceTags ? [{ id: "tags", icon: "label", label: "Tags" }] : [],
4638
5568
  { id: "browse", icon: "photo_library", label: "Galerie" }
4639
- ].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setMobileTab(tab.id), className: `flex-1 flex flex-col items-center justify-center gap-0.5 transition-colors ${mobileTab === tab.id ? "text-white" : "text-white/30"}`, children: [
4640
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
4641
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
5569
+ ].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5570
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[24px]", children: tab.icon }),
5571
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] font-bold uppercase tracking-wide", children: tab.label })
4642
5572
  ] }, tab.id)) })
4643
5573
  ] });
4644
5574
  if (mdMode) {
4645
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
5575
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { position: "fixed", inset: 0, overflow: "hidden", background: "#0e0e0e" }, children: mobileRoot });
4646
5576
  }
4647
5577
  return mobileRoot;
4648
5578
  }
@@ -4650,17 +5580,17 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4650
5580
  const tlScale = Math.min(window.innerWidth / 920, window.innerHeight / 520);
4651
5581
  const tlW = 920;
4652
5582
  const tlH = 520;
4653
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
4654
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { width: 320, height: tlH, display: "flex", flexDirection: "column", borderRight: "1px solid rgba(255,255,255,0.05)", background: "#000", flexShrink: 0 }, children: [
4655
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { height: 52, borderBottom: "1px solid rgba(255,255,255,0.05)", display: "flex", alignItems: "center", gap: 8, padding: "0 12px", flexShrink: 0 }, children: [
4656
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4657
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] }),
4658
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1 } }),
4659
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: toggleContrast, style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
4660
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), style: { color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 4, lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
5583
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { position: "fixed", inset: 0, background: "#0e0e0e", display: "flex", alignItems: "center", justifyContent: "center", overflow: "hidden", ...hcStyle || {} }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { width: tlW, height: tlH, transform: `scale(${tlScale})`, transformOrigin: "center center", display: "flex", flexDirection: "row", color: "#fff", overflow: "hidden", borderRadius: 0 }, children: [
5584
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5585
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5586
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] }),
5587
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] }),
5588
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { flex: 1 } }),
5589
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: highContrast ? "light_mode" : "dark_mode" }) }),
5590
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "apps" }) })
4661
5591
  ] }),
4662
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { position: "relative", borderRadius: 12, border: `1px solid ${isSynthesizing ? "rgba(255,255,255,0.2)" : "rgba(255,255,255,0.1)"}`, background: "rgba(255,255,255,0.05)" }, children: [
4663
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5592
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { padding: "12px 12px 8px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5593
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4664
5594
  "textarea",
4665
5595
  {
4666
5596
  value: activePrompt,
@@ -4669,27 +5599,27 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4669
5599
  placeholder: "Prompt eingeben..."
4670
5600
  }
4671
5601
  ),
4672
- activePrompt && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActivePrompt(""), style: { position: "absolute", top: 6, right: 6, width: 22, height: 22, display: "flex", alignItems: "center", justifyContent: "center", color: "rgba(255,255,255,0.2)", background: "none", border: "none", cursor: "pointer", padding: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
5602
+ activePrompt && /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 15 }, children: "close" }) })
4673
5603
  ] }) }),
4674
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5604
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { padding: "0 12px 10px", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4675
5605
  "button",
4676
5606
  {
4677
5607
  onClick: () => handleGenerateImage(),
4678
5608
  disabled: !activePrompt.trim() || isGenerating,
4679
5609
  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" },
4680
- children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4681
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4682
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generiere..." })
4683
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4684
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
4685
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Generieren" })
5610
+ children: isGenerating ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5611
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { width: 14, height: 14, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
5612
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Generiere..." })
5613
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5614
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "bolt" }),
5615
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Generieren" })
4686
5616
  ] })
4687
5617
  }
4688
5618
  ) }),
4689
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
5619
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { flex: 1, overflow: "hidden", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }) })
4690
5620
  ] }),
4691
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
4692
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5621
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { flex: 1, height: tlH, display: "flex", flexDirection: "column", background: "#0b0b0b" }, children: [
5622
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4693
5623
  "div",
4694
5624
  {
4695
5625
  style: { flex: 1, padding: 16, display: "flex", alignItems: "center", justifyContent: "center", position: "relative" },
@@ -4701,26 +5631,26 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4701
5631
  else if (dx > 50) goToPrev();
4702
5632
  setTouchStartX(null);
4703
5633
  },
4704
- children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { height: "100%", width: "100%", borderRadius: 20, border: "1px solid rgba(255,255,255,0.05)", background: "rgba(0,0,0,0.4)", position: "relative", overflow: "hidden", display: "flex", alignItems: "center", justifyContent: "center" }, children: [
4705
- currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4706
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
4707
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
5634
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5635
+ currentResult?.status === "processing" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
5636
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { width: 36, height: 36, borderTop: "2px solid #fff", borderRadius: "50%", animation: "spin 1s linear infinite" } }),
5637
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 10, color: "rgba(255,255,255,0.4)", textTransform: "uppercase", fontWeight: "bold", letterSpacing: "0.15em" }, children: "Erstelle Bild..." })
4708
5638
  ] }),
4709
- currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
4710
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
4711
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
4712
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => handleGenerateImage(currentResult.prompt), style: { padding: "8px 16px", borderRadius: 8, border: "1px solid rgba(255,255,255,0.2)", fontSize: 11, color: "rgba(255,255,255,0.7)", background: "none", cursor: "pointer", fontFamily: "inherit" }, children: "Erneut versuchen" })
5639
+ currentResult?.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { padding: 24, textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 12 }, children: [
5640
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 32, color: "#f87171" }, children: "warning" }),
5641
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { style: { fontSize: 11, color: "rgba(255,255,255,0.5)", margin: 0 }, children: currentResult.error?.message }),
5642
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" })
4713
5643
  ] }),
4714
- currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
4715
- !currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
4716
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
4717
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
5644
+ currentResult?.status === "done" && currentResult.base64 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: currentResult.base64, style: { maxWidth: "100%", maxHeight: "100%", objectFit: "contain" } }),
5645
+ !currentResult && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 8, opacity: 0.1 }, children: [
5646
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 64 }, children: "palette" }),
5647
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
4718
5648
  ] }),
4719
- currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: openFullscreen, style: { position: "absolute", top: 8, right: 8, width: 32, height: 32, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
4720
- history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
4721
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: goToPrev, disabled: currentIndex <= 0, style: { position: "absolute", left: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex <= 0 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
4722
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: goToNext, disabled: currentIndex >= history.length - 1, style: { position: "absolute", right: 8, top: "50%", transform: "translateY(-50%)", width: 36, height: 36, display: "flex", alignItems: "center", justifyContent: "center", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "1px solid rgba(255,255,255,0.1)", cursor: "pointer", color: "#fff", opacity: currentIndex >= history.length - 1 ? 0 : 1, transition: "opacity 0.2s" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
4723
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { position: "absolute", bottom: 8, left: "50%", transform: "translateX(-50%)", background: "rgba(0,0,0,0.6)", borderRadius: 999, padding: "2px 12px", fontSize: 10, color: "rgba(255,255,255,0.4)", fontFamily: "monospace" }, children: [
5649
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
5650
+ history.length > 1 && currentResult && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
5651
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
5652
+ /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
5653
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
4724
5654
  currentIndex + 1,
4725
5655
  " / ",
4726
5656
  history.length
@@ -4729,42 +5659,42 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4729
5659
  ] })
4730
5660
  }
4731
5661
  ),
4732
- currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
4733
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setActivePrompt(currentResult.prompt || ""), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4734
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
4735
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Prompt" })
5662
+ currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { padding: "0 16px 16px", display: "flex", gap: 8, flexShrink: 0 }, children: [
5663
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5664
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "replay" }),
5665
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Prompt" })
4736
5666
  ] }),
4737
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "none", background: "rgba(255,255,255,0.1)", color: "rgba(255,255,255,0.8)", fontSize: 11, fontWeight: "bold", cursor: "pointer", fontFamily: "inherit" }, children: [
4738
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
4739
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Referenz" })
5667
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5668
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "auto_fix_high" }),
5669
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Referenz" })
4740
5670
  ] }),
4741
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: handleDownloadSingle, style: { flex: 1, height: 40, display: "flex", alignItems: "center", justifyContent: "center", gap: 6, borderRadius: 10, border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.05)", color: "rgba(255,255,255,0.6)", fontSize: 11, cursor: "pointer", fontFamily: "inherit" }, children: [
4742
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
4743
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Laden" })
5671
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5672
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", style: { fontSize: 16 }, children: "download" }),
5673
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Laden" })
4744
5674
  ] })
4745
5675
  ] })
4746
5676
  ] })
4747
5677
  ] }) });
4748
5678
  }
4749
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
4750
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
4751
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
4752
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
4753
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-1 gap-1", children: [
4754
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setLeftTab("prompt"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "prompt" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4755
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
5679
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex h-screen w-screen bg-[#0e0e0e] text-white overflow-hidden", style: hcStyle, children: [
5680
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute top-2 right-2 z-50", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setShowStart(true), className: "text-white/10 hover:text-white/30 transition-colors text-[10px]", children: "\u21C4" }) }),
5681
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col border-r border-white/5 overflow-hidden relative bg-black/10 shrink-0", style: { width: isLeftCollapsed ? 48 : leftPanelWidth, transition: "none" }, children: [
5682
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center justify-between shrink-0 px-1", children: [
5683
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-1 gap-1", children: [
5684
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5685
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "auto_fix_high" }),
4756
5686
  "Prompt"
4757
5687
  ] }),
4758
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: () => setLeftTab("hierarchy"), className: `flex-1 flex items-center justify-center gap-1 h-8 rounded-lg text-[8px] font-bold uppercase tracking-wide transition-colors ${leftTab === "hierarchy" ? "bg-white/10 text-white" : "text-white/30 hover:text-white/60"}`, children: [
4759
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
5688
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5689
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "account_tree" }),
4760
5690
  "Hierarchie"
4761
5691
  ] }),
4762
- workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
5692
+ workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setActiveTab("tags"), className: `flex-1 flex items-center justify-center gap-1.5 text-[11px] font-bold uppercase tracking-wide transition-colors ${activeTab === "tags" ? "text-white border-b-2 border-white" : "text-white/30"}`, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: "label" }) })
4763
5693
  ] }),
4764
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsLeftCollapsed(!isLeftCollapsed), className: "material-symbols-outlined text-[18px] text-white/40 hover:text-white transition-all w-10 flex items-center justify-center", children: isLeftCollapsed ? "chevron_right" : "chevron_left" })
5694
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" })
4765
5695
  ] }),
4766
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
4767
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5696
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
5697
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4768
5698
  TagManagerPanel,
4769
5699
  {
4770
5700
  workspaceTags,
@@ -4775,11 +5705,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4775
5705
  onTagMove: handleTagMove
4776
5706
  }
4777
5707
  ),
4778
- activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
4779
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
4780
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
5708
+ activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
5709
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
5710
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
4781
5711
  ] }) }),
4782
- leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5712
+ leftTab === "hierarchy" && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "absolute inset-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4783
5713
  ListView,
4784
5714
  {
4785
5715
  nodes,
@@ -4804,18 +5734,18 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4804
5734
  isGeneratingNodeId: (id) => isSynthesizing && focusedNodeId === id
4805
5735
  }
4806
5736
  ) }),
4807
- leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
5737
+ leftTab === "prompt" && workspaceTags && activeTab !== "tags" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PromptTab, { workspaceTags, onGenerate: handlePromptTabGenerate, isGenerating: isPromptTabGenerating, feedback: promptFeedback, promptResult: activePrompt || null, lastPayload: lastPromptPayload, onGenerateImage: (prompt) => handleGenerateImage(prompt), onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage })
4808
5738
  ] })
4809
5739
  ] }),
4810
- !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
4811
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
4812
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-14 border-b border-white/5 flex items-center px-4 gap-2 justify-between shrink-0 bg-black/20", children: [
4813
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1.5", children: [
4814
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: aspectRatio, onChange: setAspectRatio, options: [{ label: "1:1", value: "1:1" }, { label: "16:9", value: "16:9" }, { label: "9:16", value: "9:16" }] }),
4815
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CompactDropdown, { value: selectedModel, onChange: setSelectedModel, options: [{ value: "\u{1F34C} Nano Banana Pro", label: "\u{1F34C} Nano Banana Pro" }, { value: "\u{1F34C} Nano Banana 2", label: "\u{1F34C} Nano Banana 2" }, { value: "Imagen 4", label: "Imagen 4" }] })
5740
+ !isLeftCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { onMouseDown: startLeftResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
5741
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 flex flex-col bg-[#0b0b0b] overflow-hidden", children: [
5742
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5743
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1.5", children: [
5744
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] }),
5745
+ /* @__PURE__ */ (0, import_jsx_runtime21.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" }] })
4816
5746
  ] }),
4817
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 mx-auto", children: [
4818
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5747
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1 mx-auto", children: [
5748
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4819
5749
  "button",
4820
5750
  {
4821
5751
  onClick: () => setMiddlePanel("stage"),
@@ -4823,7 +5753,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4823
5753
  children: "Stage"
4824
5754
  }
4825
5755
  ),
4826
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5756
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4827
5757
  "button",
4828
5758
  {
4829
5759
  onClick: () => setMiddlePanel("labs"),
@@ -4832,68 +5762,68 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4832
5762
  }
4833
5763
  )
4834
5764
  ] }),
4835
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2", children: [
4836
- activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
4837
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
4838
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
4839
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4840
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
4841
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
4842
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "Ref" })
5765
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
5766
+ activeReferenceThumbnail ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-1 rounded-lg border border-white/20 bg-white/5 overflow-hidden", style: { height: 28 }, children: [
5767
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: activeReferenceThumbnail, className: "h-full aspect-square object-cover" }),
5768
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-white/60 font-bold uppercase tracking-wide px-1", children: "Ref" }),
5769
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: clearReference, className: "w-6 h-full flex items-center justify-center text-white/30 hover:text-white/80 transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
5770
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("button", { onClick: handleSelectReference, className: "flex items-center gap-1 h-7 px-2 rounded-lg border border-white/10 text-white/30 hover:text-white/60 hover:border-white/20 transition-colors text-[10px] font-bold uppercase tracking-wide", children: [
5771
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "add_photo_alternate" }),
5772
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: "Ref" })
4843
5773
  ] }),
4844
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
4845
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
5774
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setIsPromptCollapsed(!isPromptCollapsed), className: "text-white/40 hover:text-white transition-colors", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined", children: isPromptCollapsed ? "expand_more" : "expand_less" }) }),
5775
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PillButton, { variant: "solid", icon: "bolt", loading: isGenerating, disabled: !activePrompt.trim(), onClick: () => handleGenerateImage(), children: "Generieren" })
4846
5776
  ] })
4847
5777
  ] }),
4848
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
4849
- !isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
4850
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("textarea", { value: activePrompt, onChange: (e) => setActivePrompt(e.target.value), className: "w-full bg-transparent border-none outline-none text-[12px] leading-relaxed text-white/80 resize-none h-20 dark-scrollbar", placeholder: "W\xE4hle einen Knoten oder tippe einen Prompt..." }),
4851
- activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setActivePrompt(""), className: "absolute top-2 right-2 w-6 h-6 rounded-full bg-white/5 hover:bg-white/10 flex items-center justify-center transition-colors text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
5778
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 flex flex-col overflow-hidden relative", children: [
5779
+ !isPromptCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "px-6 py-4 border-b border-white/5 bg-black/10 overflow-hidden shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `relative min-h-[60px] p-4 rounded-2xl border transition-all ${isSynthesizing ? "prompt-loading" : "bg-white/5 border-white/10"}`, children: [
5780
+ /* @__PURE__ */ (0, import_jsx_runtime21.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..." }),
5781
+ activePrompt && !isSynthesizing && /* @__PURE__ */ (0, import_jsx_runtime21.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_runtime21.jsx)("span", { className: "material-symbols-outlined text-[14px]", children: "close" }) })
4852
5782
  ] }) }),
4853
- middlePanel === "labs" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(LabsTab, { services: labServices, onResult: (item) => {
5783
+ middlePanel === "labs" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LabsTab, { services: labServices, onResult: (item) => {
4854
5784
  const frame = item.frames[0];
4855
5785
  if (frame?.base64) setCurrentResult(frameToGeneration(frame, item));
4856
- } }) }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-full w-full max-w-4xl aspect-square rounded-3xl border border-white/5 bg-black/40 relative overflow-hidden flex items-center justify-center group shadow-2xl", children: [
4857
- isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute top-6 right-6 z-30 bg-black/60 backdrop-blur-md px-4 py-2 rounded-full border border-white/10 flex items-center gap-3", children: [
4858
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
4859
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
5786
+ } }) }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 p-6 overflow-hidden flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5787
+ isGenerating && currentResult?.status === "done" && /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5788
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-3 h-3 border-t-2 border-white rounded-full animate-spin" }),
5789
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-white/60 uppercase font-bold tracking-widest", children: "Neue Referenz..." })
4860
5790
  ] }),
4861
- currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
4862
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
4863
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
4864
- ] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
4865
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
4866
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col gap-2", children: [
4867
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
4868
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
5791
+ currentResult ? currentResult.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-4", children: [
5792
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-10 h-10 border-t-2 border-white rounded-full animate-spin" }),
5793
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[10px] text-white/40 uppercase font-bold tracking-widest", children: "Erstelle Bild..." })
5794
+ ] }) : currentResult.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "p-10 text-center flex flex-col items-center gap-5 max-w-md", children: [
5795
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-16 h-16 rounded-full bg-red-500/10 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-red-500 text-[32px]", children: "warning" }) }),
5796
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col gap-2", children: [
5797
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h3", { className: "text-[11px] font-bold uppercase tracking-widest text-red-400", children: "Generierungsfehler" }),
5798
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-white/60 text-[12px] leading-relaxed", children: currentResult.error?.message })
4869
5799
  ] }),
4870
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
4871
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
4872
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
4873
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "absolute bottom-6 flex gap-2 opacity-0 group-hover:opacity-100 transition-all translate-y-4 group-hover:translate-y-0 z-20", children: [
4874
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
4875
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
4876
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
5800
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PillButton, { variant: "outline", icon: "refresh", onClick: () => handleGenerateImage(currentResult.prompt), children: "Erneut versuchen" })
5801
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "h-full w-full relative flex items-center justify-center", children: [
5802
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("img", { src: currentResult.base64, className: "max-h-full max-w-full object-contain rounded-xl shadow-2xl" }),
5803
+ /* @__PURE__ */ (0, import_jsx_runtime21.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: [
5804
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PillButton, { variant: "outline", icon: "replay", onClick: () => setActivePrompt(currentResult.prompt || ""), children: "Prompt" }),
5805
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PillButton, { variant: "solid", icon: "auto_fix_high", onClick: () => handleGenerateImage(currentResult.prompt || activePrompt, currentResult.mediaId, void 0, { silent: true }), children: "Referenz" }),
5806
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PillButton, { variant: "outline", icon: "download", onClick: handleDownloadSingle, children: "Speichern" })
4877
5807
  ] })
4878
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
4879
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
4880
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
5808
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col items-center gap-2 opacity-10", children: [
5809
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[100px]", children: "palette" }),
5810
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[12px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
4881
5811
  ] })
4882
5812
  ] }) })
4883
5813
  ] })
4884
5814
  ] }),
4885
- !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
4886
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
4887
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
4888
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => {
5815
+ !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { onMouseDown: startRightResize, className: "w-1 shrink-0 cursor-col-resize hover:bg-white/20 active:bg-white/30 transition-colors", style: { background: "transparent" } }),
5816
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col border-l border-white/5 bg-[#0e0e0e] shrink-0", style: { width: isRightCollapsed ? 60 : rightPanelWidth, transition: "none" }, children: [
5817
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex border-b border-white/5 h-14 shrink-0 overflow-hidden", children: [
5818
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-1", children: ["history", "gallery", "inspect", "setup", "sync", "tags"].map((tab) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => {
4889
5819
  setActiveTab(tab);
4890
5820
  setIsRightCollapsed(false);
4891
- }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : tab === "sync" ? "cloud_sync" : "label" }) }, tab)) }),
4892
- hfToken && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => loadFromHF(hfToken), disabled: isHfRefreshing, className: "w-10 flex items-center justify-center text-white/20 hover:text-white/60 transition-colors disabled:opacity-30", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: `material-symbols-outlined text-[18px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) }),
4893
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
5821
+ }, className: `flex-1 flex items-center justify-center relative transition-colors ${activeTab === tab ? "text-white" : "text-white/20"}`, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[20px]", children: tab === "history" ? "history" : tab === "gallery" ? "photo_library" : tab === "inspect" ? "info" : tab === "setup" ? "settings" : tab === "sync" ? "cloud_sync" : "label" }) }, tab)) }),
5822
+ hfToken && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => refreshHF(), disabled: isHfRefreshing, className: "w-10 flex items-center justify-center text-white/20 hover:text-white/60 transition-colors disabled:opacity-30", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: `material-symbols-outlined text-[18px]${isHfRefreshing ? " animate-spin" : ""}`, children: "sync" }) }),
5823
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { onClick: () => setIsRightCollapsed(!isRightCollapsed), className: "w-10 flex items-center justify-center text-white/20 hover:text-white", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[18px]", children: isRightCollapsed ? "chevron_left" : "chevron_right" }) })
4894
5824
  ] }),
4895
- !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
4896
- activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5825
+ !isRightCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex-1 overflow-hidden relative", children: [
5826
+ activeTab === "tags" && workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4897
5827
  TagManagerPanel,
4898
5828
  {
4899
5829
  workspaceTags,
@@ -4904,12 +5834,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4904
5834
  onTagMove: handleTagMove
4905
5835
  }
4906
5836
  ),
4907
- activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { children: [
4908
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
4909
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
5837
+ activeTab === "tags" && !workspaceTags && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex items-center justify-center h-full p-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { children: [
5838
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "material-symbols-outlined text-[40px] text-white/10 block mb-3", children: "label_off" }),
5839
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[11px] text-white/20", children: "Erst Workspace importieren um Tags zu verwalten." })
4910
5840
  ] }) }),
4911
- activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
4912
- activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5841
+ activeTab === "history" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(HistoryPanel, { history, currentResultId: currentResult?.id || null, onSelect: setCurrentResult, onDelete: (id) => setHistory((h) => h.filter((x) => x.id !== id)) }),
5842
+ activeTab === "gallery" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4913
5843
  MediaLibrary,
4914
5844
  {
4915
5845
  items: galleryItems,
@@ -4923,11 +5853,12 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4923
5853
  onGenerateReference: (item) => handleGenerateImage(item.prompt || activePrompt, item.mediaId, void 0, { silent: true })
4924
5854
  }
4925
5855
  ),
4926
- activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
4927
- activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
4928
- activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
5856
+ activeTab === "inspect" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
5857
+ activeTab === "setup" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo }),
5858
+ activeTab === "sync" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4929
5859
  ProjectSyncTab,
4930
5860
  {
5861
+ topSlot: syncTopSlot,
4931
5862
  onProjectExport: handleProjectExport,
4932
5863
  onProjectImport: (f) => handleProjectImport(f),
4933
5864
  onWorkspaceImport: handleWorkspaceImport,
@@ -4954,7 +5885,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
4954
5885
  }
4955
5886
 
4956
5887
  // src/components/FaApp.tsx
4957
- var import_jsx_runtime21 = require("react/jsx-runtime");
5888
+ var import_react24 = require("react");
5889
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4958
5890
  function FaApp({
4959
5891
  onGenerateImage,
4960
5892
  onGeneratePrompt,
@@ -4965,17 +5897,27 @@ function FaApp({
4965
5897
  onFlowUpload: _onFlowUpload,
4966
5898
  onFlowMediaUpload: _onFlowMediaUpload,
4967
5899
  libToken,
5900
+ allowDevNamespace,
5901
+ serverBaseUrl,
4968
5902
  onFetchServerProjects,
4969
5903
  onServerSave,
4970
5904
  onServerLoad,
4971
5905
  onServerDelete,
4972
5906
  buildInfo
4973
5907
  }) {
5908
+ const [hfNamespace, setHfNamespace] = (0, import_react24.useState)(void 0);
5909
+ (0, import_react24.useEffect)(() => {
5910
+ if (!serverBaseUrl) return;
5911
+ fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
5912
+ if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
5913
+ }).catch(() => {
5914
+ });
5915
+ }, [serverBaseUrl]);
4974
5916
  const wrappedPrompt = async (text, options) => {
4975
5917
  const result = await onGeneratePrompt(text, options);
4976
5918
  return result.text;
4977
5919
  };
4978
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
5920
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4979
5921
  AvatarArchitectApp,
4980
5922
  {
4981
5923
  onGenerateImage,
@@ -4983,6 +5925,8 @@ function FaApp({
4983
5925
  onDownload,
4984
5926
  onSelectMedia,
4985
5927
  initialHfToken: libToken ? libToken.startsWith("hf_") ? libToken : `hf_${libToken}` : void 0,
5928
+ hfNamespace,
5929
+ allowDevNamespace: !hfNamespace && allowDevNamespace,
4986
5930
  onFetchServerProjects,
4987
5931
  onServerSave,
4988
5932
  onServerLoad,
@@ -4994,7 +5938,8 @@ function FaApp({
4994
5938
 
4995
5939
  // src/index.ts
4996
5940
  init_hfStateService();
4997
- var LIB_VERSION = "1.3.18";
5941
+ init_hfStateService();
5942
+ var LIB_VERSION = "2.0.13";
4998
5943
  // Annotate the CommonJS export names for ESM import in node:
4999
5944
  0 && (module.exports = {
5000
5945
  AvatarArchitectApp,
@@ -5020,9 +5965,12 @@ var LIB_VERSION = "1.3.18";
5020
5965
  SectionLabel,
5021
5966
  SetupPanel,
5022
5967
  TagManagerPanel,
5968
+ applyEvent,
5969
+ applyEvents,
5023
5970
  autoLabel,
5024
5971
  buildBlendInstruction,
5025
5972
  buildCompareInstruction,
5973
+ buildDag,
5026
5974
  buildFallbackPrompt,
5027
5975
  buildGenerationPrompt,
5028
5976
  buildImageGenerationOptions,
@@ -5034,27 +5982,36 @@ var LIB_VERSION = "1.3.18";
5034
5982
  cleanAiResponse,
5035
5983
  createFlowServices,
5036
5984
  exportProjectToZip,
5985
+ findForks,
5986
+ findTips,
5037
5987
  formatTreeToMarkdown,
5038
5988
  frameToGeneration,
5039
5989
  getFormattedTimestamp,
5040
5990
  getHFToken,
5991
+ getSessionClientId,
5041
5992
  groupGenerationsToLabItems,
5993
+ hfBatchArchive,
5994
+ hfBootstrapFromLegacy,
5042
5995
  hfDeleteProject,
5043
5996
  hfDownloadProject,
5997
+ hfListDir,
5044
5998
  hfListProjects,
5045
5999
  hfLoadImageAsBase64,
5046
- hfLoadMetadata,
5047
- hfLoadTags,
5048
- hfSaveMetadata,
5049
- hfSaveTags,
5050
6000
  hfUploadImage,
5051
6001
  hfUploadProjectForm,
6002
+ hfUploadSmallFile,
5052
6003
  importProjectFromZip,
5053
6004
  injectXMPMetadata,
5054
6005
  interpretSdkError,
6006
+ loadHFState,
6007
+ loadPendingEvents,
5055
6008
  parsePromptFile,
5056
6009
  parsePromptResponse,
5057
6010
  setHFToken,
6011
+ topoSort,
6012
+ tsFromEventPath,
6013
+ useHFState,
5058
6014
  useKeyboardNavigation,
5059
- useOnClickOutside
6015
+ useOnClickOutside,
6016
+ writeHFEvent
5060
6017
  });