@rslsp1/fa-app-tools 6.1.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,37 +2,26 @@ import {
2
2
  SzenenView,
3
3
  alsFlowTextZugang,
4
4
  applyEvents,
5
- createFlowSzenenViewPort
6
- } from "./chunk-GHKN2VX3.mjs";
7
- import "./chunk-FUY2OCVC.mjs";
8
- import {
9
- exportProjectToZip,
10
- importProjectFromZip,
11
- injectXMPMetadata
12
- } from "./chunk-UFXDXENC.mjs";
13
- import {
5
+ createFlowSzenenViewPort,
14
6
  getHFToken,
15
7
  getSessionClientId,
16
8
  hfBatchArchive,
17
9
  hfBootstrapFromLegacy,
18
- hfDeleteProject,
19
10
  hfDownloadBinaryByPath,
20
- hfDownloadProject,
21
11
  hfListDir,
22
- hfListProjects,
23
12
  hfLoadImageAsBase64,
24
13
  hfUploadImage,
25
- hfUploadProjectForm,
26
14
  hfUploadSmallFile,
27
15
  loadHFState,
28
16
  loadPendingEvents,
29
17
  setHFToken,
30
18
  tsFromEventPath,
31
19
  writeHFEvent
32
- } from "./chunk-NBUS7AWR.mjs";
20
+ } from "./chunk-B2YV4TNI.mjs";
21
+ import "./chunk-FUY2OCVC.mjs";
33
22
 
34
23
  // package.json
35
- var version = "6.1.0";
24
+ var version = "7.0.1";
36
25
 
37
26
  // src/hooks/useOnClickOutside.ts
38
27
  import { useEffect } from "react";
@@ -128,6 +117,125 @@ var GLOBAL_STYLES = `
128
117
  @media (max-width: 760px) { .flow-szenen-shell__content { padding: 8px; } }
129
118
  ` + SZENEN_VIEW_STYLES;
130
119
 
120
+ // src/lib/metadata.ts
121
+ var crcTable = (() => {
122
+ let c;
123
+ const table = new Uint32Array(256);
124
+ for (let n = 0; n < 256; n++) {
125
+ c = n;
126
+ for (let k = 0; k < 8; k++) {
127
+ c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
128
+ }
129
+ table[n] = c;
130
+ }
131
+ return table;
132
+ })();
133
+ function calculateCRC(bytes) {
134
+ let crc = 4294967295;
135
+ for (let i = 0; i < bytes.length; i++) {
136
+ crc = crcTable[(crc ^ bytes[i]) & 255] ^ crc >>> 8;
137
+ }
138
+ return (crc ^ 4294967295) >>> 0;
139
+ }
140
+ function indexOfBytes(buffer, search, start = 0) {
141
+ for (let i = start; i <= buffer.length - search.length; i++) {
142
+ let found = true;
143
+ for (let j = 0; j < search.length; j++) {
144
+ if (buffer[i + j] !== search[j]) {
145
+ found = false;
146
+ break;
147
+ }
148
+ }
149
+ if (found) return i;
150
+ }
151
+ return -1;
152
+ }
153
+ function createXMPPacket(prompt, seed, model, id, tags = [], hierarchy) {
154
+ const sanitize = (str) => (str || "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
155
+ const escPrompt = sanitize(prompt);
156
+ const escModel = sanitize(model || "AI Model");
157
+ const escHierarchy = sanitize(hierarchy || "");
158
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
159
+ const allTags = [.../* @__PURE__ */ new Set([...tags, "Avatar Architect", escModel])];
160
+ const rdfTags = allTags.map((t) => `<rdf:li>${sanitize(t)}</rdf:li>`).join("");
161
+ const BOM = "\uFEFF";
162
+ return `<?xpacket begin="${BOM}" id="W5M0MpCehiHzreSzNTczkc9d"?>
163
+ <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c140">
164
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
165
+ <rdf:Description rdf:about=""
166
+ xmlns:xmp="http://ns.adobe.com/xap/1.0/"
167
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
168
+ xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/"
169
+ xmlns:ai="http://ns.flow.creative/ai/1.0/">
170
+ <dc:format>image/png</dc:format>
171
+ <dc:description><rdf:Alt><rdf:li xml:lang="x-default">${escPrompt}</rdf:li></rdf:Alt></dc:description>
172
+ <dc:subject><rdf:Bag>${rdfTags}</rdf:Bag></dc:subject>
173
+ <photoshop:Instructions>Seed: ${seed} | Model: ${escModel}</photoshop:Instructions>
174
+ <xmp:CreateDate>${timestamp}</xmp:CreateDate>
175
+ <ai:prompt>${escPrompt}</ai:prompt>
176
+ <ai:seed>${seed ?? "0"}</ai:seed>
177
+ <ai:model>${escModel}</ai:model>
178
+ <ai:hierarchy>${escHierarchy}</ai:hierarchy>
179
+ </rdf:Description>
180
+ </rdf:RDF>
181
+ </x:xmpmeta>${" ".repeat(1024)}<?xpacket end="w"?>`;
182
+ }
183
+ function bytesToBase64(bytes) {
184
+ let binary = "";
185
+ const len = bytes.byteLength;
186
+ for (let i = 0; i < len; i++) binary += String.fromCharCode(bytes[i]);
187
+ return btoa(binary);
188
+ }
189
+ function base64ToBytes(base64) {
190
+ const clean = base64.includes(",") ? base64.split(",")[1] : base64.trim();
191
+ try {
192
+ const binary = atob(clean);
193
+ const bytes = new Uint8Array(binary.length);
194
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
195
+ return bytes;
196
+ } catch (e) {
197
+ return new Uint8Array(0);
198
+ }
199
+ }
200
+ function injectXMPMetadata(base64, prompt, seed, model, id, tags = [], hierarchy) {
201
+ try {
202
+ const bytes = base64ToBytes(base64);
203
+ if (bytes.length < 8 || bytes[0] !== 137 || bytes[1] !== 80) {
204
+ console.warn("Metadaten-Injektion \xFCbersprungen: Datei ist kein PNG.");
205
+ return base64;
206
+ }
207
+ const xmpData = createXMPPacket(prompt, seed, model, id, tags, hierarchy);
208
+ const encoder = new TextEncoder();
209
+ const keyword = encoder.encode("XML:com.adobe.xmp");
210
+ const textBytes = encoder.encode(xmpData);
211
+ const chunkData = new Uint8Array(keyword.length + 5 + textBytes.length);
212
+ chunkData.set(keyword, 0);
213
+ chunkData.set(textBytes, keyword.length + 5);
214
+ const type = encoder.encode("iTXt");
215
+ const crcInput = new Uint8Array(type.length + chunkData.length);
216
+ crcInput.set(type, 0);
217
+ crcInput.set(chunkData, type.length);
218
+ const crc = calculateCRC(crcInput);
219
+ const fullChunk = new Uint8Array(4 + 4 + chunkData.length + 4);
220
+ const view = new DataView(fullChunk.buffer);
221
+ view.setUint32(0, chunkData.length);
222
+ fullChunk.set(type, 4);
223
+ fullChunk.set(chunkData, 8);
224
+ view.setUint32(8 + chunkData.length, crc);
225
+ const IDAT = encoder.encode("IDAT");
226
+ const idatPos = indexOfBytes(bytes, IDAT);
227
+ const insertPos = idatPos !== -1 ? idatPos - 4 : 8;
228
+ const result = new Uint8Array(bytes.length + fullChunk.length);
229
+ result.set(bytes.slice(0, insertPos), 0);
230
+ result.set(fullChunk, insertPos);
231
+ result.set(bytes.slice(insertPos), insertPos + fullChunk.length);
232
+ return bytesToBase64(result);
233
+ } catch (e) {
234
+ console.error("Fehler beim Einbetten der Metadaten:", e);
235
+ return base64;
236
+ }
237
+ }
238
+
131
239
  // src/lib/aiHelpers.ts
132
240
  function buildGenerationPrompt(hierarchyText, mode = "creative") {
133
241
  const safe = hierarchyText.length > 2500 ? hierarchyText.slice(0, 2500) + "..." : hierarchyText;
@@ -1446,7 +1554,7 @@ function ListView({ nodes, edges, onNodeChange, onAddChild, onDeleteNode, onMove
1446
1554
  }
1447
1555
 
1448
1556
  // src/components/AvatarArchitectApp.tsx
1449
- import { useState as useState25, useCallback as useCallback4, useMemo as useMemo6, useEffect as useEffect10, useRef as useRef11 } from "react";
1557
+ import { useState as useState25, useCallback as useCallback4, useMemo as useMemo6, useEffect as useEffect9, useRef as useRef10 } from "react";
1450
1558
 
1451
1559
  // src/components/PromptTab.tsx
1452
1560
  import { useRef as useRef5, useState as useState8 } from "react";
@@ -2033,374 +2141,50 @@ var TaskExecutor = ({ openTasks, onExecute, onTaskCompleted }) => {
2033
2141
  ] });
2034
2142
  };
2035
2143
 
2036
- // src/components/ProjectSyncTab.tsx
2037
- import { useRef as useRef7, useState as useState10, useEffect as useEffect6 } from "react";
2038
- import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2039
- var ProjectSyncTab = ({
2040
- topSlot,
2041
- onProjectExport,
2042
- onProjectImport,
2043
- onWorkspaceImport,
2044
- projectActionState,
2045
- serverProjects,
2046
- onServerSave,
2047
- onServerLoad,
2048
- onServerDelete,
2049
- onRefreshServerProjects,
2050
- onComputeSyncDiff,
2051
- onExecuteSync,
2052
- hfToken,
2053
- onHfLoad,
2054
- onProjectExportBase64,
2055
- onHfInitialSync
2056
- }) => {
2057
- const projectInputRef = useRef7(null);
2058
- const workspaceInputRef = useRef7(null);
2059
- const [saveName, setSaveName] = useState10("");
2060
- const [isSaving, setIsSaving] = useState10(false);
2061
- const [isExporting, setIsExporting] = useState10(false);
2062
- const [syncState, setSyncState] = useState10("idle");
2063
- const [syncDiff, setSyncDiff] = useState10(null);
2064
- const [selectedLocalIds, setSelectedLocalIds] = useState10(/* @__PURE__ */ new Set());
2065
- const handleExport = async () => {
2066
- if (!onProjectExport) return;
2067
- setIsExporting(true);
2068
- try {
2069
- await onProjectExport();
2070
- } finally {
2071
- setIsExporting(false);
2072
- }
2073
- };
2074
- const handleComputeDiff = async () => {
2075
- if (!onComputeSyncDiff) return;
2076
- setSyncState("computing");
2077
- setSyncDiff(null);
2078
- try {
2079
- const diff = await onComputeSyncDiff();
2080
- setSyncDiff(diff);
2081
- setSelectedLocalIds(new Set(diff.localOnly.map((g) => g.id)));
2082
- setSyncState("ready");
2083
- } catch {
2084
- setSyncState("error");
2085
- setTimeout(() => setSyncState("idle"), 4e3);
2086
- }
2087
- };
2088
- const handleExecuteSync = async () => {
2089
- if (!onExecuteSync || !syncDiff) return;
2090
- setSyncState("executing");
2091
- try {
2092
- await onExecuteSync([...selectedLocalIds]);
2093
- setSyncState("done");
2094
- setSyncDiff(null);
2095
- setTimeout(() => setSyncState("idle"), 3e3);
2096
- } catch {
2097
- setSyncState("error");
2098
- setTimeout(() => setSyncState("idle"), 4e3);
2099
- }
2100
- };
2101
- const toggleLocalId = (id) => {
2102
- setSelectedLocalIds((prev) => {
2103
- const next = new Set(prev);
2104
- if (next.has(id)) next.delete(id);
2105
- else next.add(id);
2106
- return next;
2107
- });
2108
- };
2109
- const isWorking = projectActionState === "working" || projectActionState === "working-full";
2110
- const [hfProjects, setHfProjects] = useState10([]);
2111
- const [hfLoading, setHfLoading] = useState10(false);
2112
- const [hfSaving, setHfSaving] = useState10(false);
2113
- const [hfError, setHfError] = useState10(null);
2114
- const [hfSaveName, setHfSaveName] = useState10("");
2115
- const [hfSyncProgress, setHfSyncProgress] = useState10(null);
2116
- const [hfSyncing, setHfSyncing] = useState10(false);
2117
- const loadHfProjects = async (token) => {
2118
- setHfLoading(true);
2119
- setHfError(null);
2120
- try {
2121
- setHfProjects(await hfListProjects(token));
2122
- } catch (e) {
2123
- setHfError(e.message);
2124
- } finally {
2125
- setHfLoading(false);
2126
- }
2127
- };
2128
- useEffect6(() => {
2129
- if (hfToken) loadHfProjects(hfToken);
2130
- }, [hfToken]);
2144
+ // src/components/HfMediaSyncTab.tsx
2145
+ import { useState as useState10 } from "react";
2146
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
2147
+ var HfMediaSyncTab = ({ topSlot, onHfInitialSync }) => {
2148
+ const [error, setError] = useState10(null);
2149
+ const [progress, setProgress] = useState10(null);
2150
+ const [syncing, setSyncing] = useState10(false);
2131
2151
  return /* @__PURE__ */ jsx15("div", { className: "absolute inset-0 overflow-y-auto dark-scrollbar", children: /* @__PURE__ */ jsxs13("div", { className: "p-6 flex flex-col gap-8", children: [
2132
2152
  topSlot && /* @__PURE__ */ jsx15("div", { children: topSlot }),
2133
- (onProjectExport || onProjectImport || onWorkspaceImport) && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-4", children: [
2134
- /* @__PURE__ */ jsx15(SectionLabel, { children: "Projekt-ZIP" }),
2153
+ onHfInitialSync && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-4", children: [
2154
+ /* @__PURE__ */ jsx15(SectionLabel, { children: "Hugging Face" }),
2135
2155
  /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-2", children: [
2136
- onProjectExport && /* @__PURE__ */ jsx15(PillButton, { variant: "filled", icon: "download", loading: isExporting || isWorking, onClick: handleExport, children: "Exportieren (.zip)" }),
2137
- onProjectImport && /* @__PURE__ */ jsxs13(Fragment5, { children: [
2138
- /* @__PURE__ */ jsx15(PillButton, { variant: "outline", icon: "upload", onClick: () => projectInputRef.current?.click(), children: "ZIP laden" }),
2139
- /* @__PURE__ */ jsx15(
2140
- "input",
2141
- {
2142
- ref: projectInputRef,
2143
- type: "file",
2144
- accept: ".zip",
2145
- className: "hidden",
2146
- onChange: (e) => {
2147
- const f = e.target.files?.[0];
2148
- if (f) onProjectImport(f);
2149
- e.target.value = "";
2150
- }
2151
- }
2152
- )
2153
- ] })
2154
- ] }),
2155
- onWorkspaceImport && /* @__PURE__ */ jsxs13(Fragment5, { children: [
2156
- /* @__PURE__ */ jsx15(PillButton, { variant: "outline", icon: "upload_file", onClick: () => workspaceInputRef.current?.click(), children: "Tags laden (.json)" }),
2157
- /* @__PURE__ */ jsx15(
2158
- "input",
2159
- {
2160
- ref: workspaceInputRef,
2161
- type: "file",
2162
- accept: ".json",
2163
- className: "hidden",
2164
- onChange: (e) => {
2165
- const f = e.target.files?.[0];
2166
- if (f) onWorkspaceImport(f);
2167
- e.target.value = "";
2168
- }
2169
- }
2170
- )
2171
- ] })
2172
- ] }),
2173
- (onServerSave || serverProjects || onServerDelete) && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-4", children: [
2174
- /* @__PURE__ */ jsx15(SectionLabel, { children: "Server-Speicher" }),
2175
- onServerSave && /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
2176
- /* @__PURE__ */ jsx15(
2177
- "input",
2178
- {
2179
- type: "text",
2180
- value: saveName,
2181
- onChange: (e) => setSaveName(e.target.value),
2182
- placeholder: "Name (optional)",
2183
- className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 text-[12px] text-white/70 outline-none placeholder:text-white/20",
2184
- style: { height: 40 }
2185
- }
2186
- ),
2187
- /* @__PURE__ */ jsx15(
2188
- PillButton,
2189
- {
2190
- variant: "filled",
2191
- icon: "cloud_upload",
2192
- loading: isSaving,
2193
- onClick: async () => {
2194
- setIsSaving(true);
2195
- try {
2196
- await onServerSave(saveName || void 0);
2197
- setSaveName("");
2198
- await onRefreshServerProjects?.();
2199
- } finally {
2200
- setIsSaving(false);
2201
- }
2202
- },
2203
- children: "Speichern"
2204
- }
2205
- )
2206
- ] }),
2207
- (!serverProjects || serverProjects.length === 0) && /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts gespeichert." }),
2208
- serverProjects && serverProjects.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex flex-col gap-1", children: serverProjects.map((p) => /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
2209
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
2210
- /* @__PURE__ */ jsx15("p", { className: "text-[11px] text-white/70 font-bold truncate", children: p.name }),
2211
- /* @__PURE__ */ jsxs13("p", { className: "text-[9px] text-white/25", children: [
2212
- new Date(p.saved_at).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }),
2213
- " \xB7 ",
2214
- (p.size / 1024).toFixed(0),
2215
- " KB"
2216
- ] })
2217
- ] }),
2218
- onServerLoad && /* @__PURE__ */ jsx15("button", { onClick: () => onServerLoad(p.id), className: "w-8 h-8 flex items-center justify-center text-white/30 active:text-white transition-colors", children: /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[18px]", children: "cloud_download" }) }),
2219
- /* @__PURE__ */ jsx15("button", { onClick: () => onServerDelete?.(p.id), className: "w-8 h-8 flex items-center justify-center text-white/20 active:text-red-400 transition-colors", children: /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[18px]", children: "delete" }) })
2220
- ] }, p.id)) })
2221
- ] }),
2222
- hfToken && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-4", children: [
2223
- /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between", children: [
2224
- /* @__PURE__ */ jsx15(SectionLabel, { children: "Hugging Face" }),
2225
- /* @__PURE__ */ jsx15("button", { onClick: () => loadHfProjects(hfToken), className: "text-white/30 hover:text-white/60 transition", children: /* @__PURE__ */ jsx15("span", { className: `material-symbols-outlined text-[18px]${hfLoading ? " animate-spin" : ""}`, children: "refresh" }) })
2226
- ] }),
2227
- onProjectExportBase64 && /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
2228
- /* @__PURE__ */ jsx15(
2229
- "input",
2230
- {
2231
- type: "text",
2232
- value: hfSaveName,
2233
- onChange: (e) => setHfSaveName(e.target.value),
2234
- placeholder: "Name (optional)",
2235
- className: "flex-1 bg-white/5 border border-white/10 rounded-xl px-3 text-[12px] text-white/70 outline-none placeholder:text-white/20",
2236
- style: { height: 40 }
2237
- }
2238
- ),
2239
- /* @__PURE__ */ jsx15(
2240
- PillButton,
2241
- {
2242
- variant: "filled",
2243
- icon: "cloud_upload",
2244
- loading: hfSaving,
2245
- onClick: async () => {
2246
- if (!onProjectExportBase64) return;
2247
- setHfSaving(true);
2248
- setHfError(null);
2249
- try {
2250
- const base64 = await onProjectExportBase64();
2251
- const name = hfSaveName.trim() || `project_${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16).replace("T", "_").replace(":", "")}`;
2252
- await hfUploadProjectForm(base64, name, hfToken);
2253
- setHfSaveName("");
2254
- await loadHfProjects(hfToken);
2255
- } catch (e) {
2256
- setHfError(e.message);
2257
- } finally {
2258
- setHfSaving(false);
2259
- }
2260
- },
2261
- children: "Speichern"
2262
- }
2263
- )
2264
- ] }),
2265
- onHfInitialSync && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-2", children: [
2266
2156
  /* @__PURE__ */ jsx15(
2267
2157
  PillButton,
2268
2158
  {
2269
2159
  variant: "outline",
2270
2160
  icon: "cloud_sync",
2271
- loading: hfSyncing,
2161
+ loading: syncing,
2272
2162
  onClick: async () => {
2273
- setHfSyncing(true);
2274
- setHfError(null);
2275
- setHfSyncProgress({ done: 0, total: 0 });
2163
+ setSyncing(true);
2164
+ setError(null);
2165
+ setProgress({ done: 0, total: 0 });
2276
2166
  try {
2277
- await onHfInitialSync((done, total) => setHfSyncProgress({ done, total }));
2278
- setHfSyncProgress(null);
2167
+ await onHfInitialSync((done, total) => setProgress({ done, total }));
2168
+ setProgress(null);
2279
2169
  } catch (e) {
2280
- setHfError(e.message);
2281
- setHfSyncProgress(null);
2170
+ setError(e instanceof Error ? e.message : String(e));
2171
+ setProgress(null);
2282
2172
  } finally {
2283
- setHfSyncing(false);
2173
+ setSyncing(false);
2284
2174
  }
2285
2175
  },
2286
- children: hfSyncing && hfSyncProgress ? `${hfSyncProgress.done} / ${hfSyncProgress.total} Bilder` : "Initial-Sync nach HF"
2176
+ children: syncing && progress ? `${progress.done} / ${progress.total} Bilder` : "Initial-Sync nach HF"
2287
2177
  }
2288
2178
  ),
2289
- hfSyncing && hfSyncProgress && hfSyncProgress.total > 0 && /* @__PURE__ */ jsx15("div", { className: "w-full h-1 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx15(
2179
+ syncing && progress && progress.total > 0 && /* @__PURE__ */ jsx15("div", { className: "w-full h-1 bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsx15(
2290
2180
  "div",
2291
2181
  {
2292
2182
  className: "h-full bg-blue-400 rounded-full transition-all",
2293
- style: { width: `${Math.round(hfSyncProgress.done / hfSyncProgress.total * 100)}%` }
2183
+ style: { width: `${Math.round(progress.done / progress.total * 100)}%` }
2294
2184
  }
2295
2185
  ) })
2296
2186
  ] }),
2297
- hfError && /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-red-400 font-mono px-1", children: hfError }),
2298
- !hfLoading && hfProjects.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/20 px-2", children: "Noch nichts auf HF gespeichert." }),
2299
- hfProjects.map((p) => /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 px-3 py-2 rounded-xl bg-white/5 border border-white/5", children: [
2300
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
2301
- /* @__PURE__ */ jsx15("p", { className: "text-[11px] text-white/70 font-bold truncate", children: p.name }),
2302
- /* @__PURE__ */ jsxs13("p", { className: "text-[9px] text-white/25", children: [
2303
- (p.size / 1024 / 1024).toFixed(1),
2304
- " MB",
2305
- p.isLfs ? " \xB7 LFS" : ""
2306
- ] })
2307
- ] }),
2308
- onHfLoad && /* @__PURE__ */ jsx15(
2309
- "button",
2310
- {
2311
- onClick: async () => {
2312
- try {
2313
- const { hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-W7KO2FOH.mjs");
2314
- const file = await hfDownloadProject2(p.path, hfToken);
2315
- onHfLoad(file);
2316
- } catch (e) {
2317
- setHfError(e.message);
2318
- }
2319
- },
2320
- className: "w-8 h-8 flex items-center justify-center text-white/30 active:text-white transition-colors",
2321
- children: /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[18px]", children: "cloud_download" })
2322
- }
2323
- ),
2324
- /* @__PURE__ */ jsx15(
2325
- "button",
2326
- {
2327
- onClick: async () => {
2328
- if (!confirm(`"${p.name}" auf HF l\xF6schen?`)) return;
2329
- try {
2330
- await hfDeleteProject(p.path, hfToken);
2331
- await loadHfProjects(hfToken);
2332
- } catch (e) {
2333
- setHfError(e.message);
2334
- }
2335
- },
2336
- className: "w-8 h-8 flex items-center justify-center text-white/20 active:text-red-400 transition-colors",
2337
- children: /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[18px]", children: "delete" })
2338
- }
2339
- )
2340
- ] }, p.id))
2341
- ] }),
2342
- onComputeSyncDiff && onExecuteSync && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-4", children: [
2343
- /* @__PURE__ */ jsx15(SectionLabel, { children: "Sync" }),
2344
- (syncState === "idle" || syncState === "computing") && /* @__PURE__ */ jsx15(PillButton, { variant: "outline", icon: "compare_arrows", loading: syncState === "computing", onClick: handleComputeDiff, children: "Diff berechnen" }),
2345
- syncState === "done" && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 px-2 text-[11px] text-green-400", children: [
2346
- /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[16px]", children: "check_circle" }),
2347
- "Sync abgeschlossen"
2348
- ] }),
2349
- syncState === "error" && /* @__PURE__ */ jsx15("p", { className: "text-[11px] text-red-400 px-2", children: "Fehler beim Sync." }),
2350
- syncDiff && (syncState === "ready" || syncState === "executing") && /* @__PURE__ */ jsxs13(Fragment5, { children: [
2351
- syncDiff.localOnly.length === 0 && syncDiff.serverOnly.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/30 px-2", children: "Alles synchron \u2014 keine Unterschiede." }),
2352
- syncDiff.localOnly.length > 0 && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-2", children: [
2353
- /* @__PURE__ */ jsxs13("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
2354
- "Nur lokal (",
2355
- syncDiff.localOnly.length,
2356
- ")"
2357
- ] }),
2358
- syncDiff.localOnly.map((gen) => /* @__PURE__ */ jsxs13(
2359
- "button",
2360
- {
2361
- onClick: () => toggleLocalId(gen.id),
2362
- className: `flex items-center gap-3 px-3 py-2 rounded-xl border transition-all text-left ${selectedLocalIds.has(gen.id) ? "bg-blue-500/10 border-blue-500/30" : "bg-white/3 border-white/5 opacity-60"}`,
2363
- children: [
2364
- /* @__PURE__ */ jsx15("div", { className: `w-4 h-4 rounded border flex items-center justify-center flex-shrink-0 ${selectedLocalIds.has(gen.id) ? "bg-blue-500 border-blue-500" : "border-white/20"}`, children: selectedLocalIds.has(gen.id) && /* @__PURE__ */ jsx15("span", { className: "material-symbols-outlined text-[11px] text-white", children: "check" }) }),
2365
- gen.base64 && /* @__PURE__ */ jsx15("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
2366
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
2367
- /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
2368
- /* @__PURE__ */ jsx15("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
2369
- ] })
2370
- ]
2371
- },
2372
- gen.id
2373
- ))
2374
- ] }),
2375
- syncDiff.serverOnly.length > 0 && /* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-2", children: [
2376
- /* @__PURE__ */ jsxs13("p", { className: "text-[9px] text-white/30 uppercase tracking-widest px-1 font-bold", children: [
2377
- "Nur auf Server (",
2378
- syncDiff.serverOnly.length,
2379
- ")"
2380
- ] }),
2381
- syncDiff.serverOnly.map((gen) => /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-3 px-3 py-2 rounded-xl bg-white/3 border border-white/5 opacity-40", children: [
2382
- gen.base64 && /* @__PURE__ */ jsx15("img", { src: gen.base64, className: "w-10 h-10 rounded object-cover flex-shrink-0" }),
2383
- /* @__PURE__ */ jsxs13("div", { className: "flex-1 min-w-0", children: [
2384
- /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-white/60 truncate", children: gen.prompt || "Kein Prompt" }),
2385
- /* @__PURE__ */ jsx15("p", { className: "text-[9px] text-white/25", children: new Date(gen.timestamp).toLocaleString("de-DE", { dateStyle: "short", timeStyle: "short" }) })
2386
- ] })
2387
- ] }, gen.id))
2388
- ] }),
2389
- (syncDiff.localOnly.length > 0 || syncDiff.serverOnly.length > 0) && /* @__PURE__ */ jsxs13(
2390
- PillButton,
2391
- {
2392
- variant: "solid",
2393
- icon: "sync",
2394
- loading: syncState === "executing",
2395
- disabled: selectedLocalIds.size === 0,
2396
- onClick: handleExecuteSync,
2397
- children: [
2398
- selectedLocalIds.size,
2399
- " \xFCbertragen"
2400
- ]
2401
- }
2402
- )
2403
- ] })
2187
+ error && /* @__PURE__ */ jsx15("p", { className: "text-[10px] text-red-400 font-mono px-1", children: error })
2404
2188
  ] })
2405
2189
  ] }) });
2406
2190
  };
@@ -2532,7 +2316,7 @@ function toPromptImages(images) {
2532
2316
  }
2533
2317
 
2534
2318
  // src/hooks/useHFState.ts
2535
- import { useState as useState11, useEffect as useEffect7, useRef as useRef8, useCallback } from "react";
2319
+ import { useState as useState11, useEffect as useEffect6, useRef as useRef7, useCallback } from "react";
2536
2320
 
2537
2321
  // src/lib/hfDag.ts
2538
2322
  function buildDag(events) {
@@ -2622,9 +2406,9 @@ function useHFState(token, namespace) {
2622
2406
  const [pendingBufferCount, setPendingBufferCount] = useState11(readOfflineBuffer().length);
2623
2407
  const [lastEventTs, setLastEventTs] = useState11(0);
2624
2408
  const [hasStateZip, setHasStateZip] = useState11(false);
2625
- const knownEventPaths = useRef8(/* @__PURE__ */ new Set());
2626
- const knownFilePaths = useRef8(/* @__PURE__ */ new Set());
2627
- const allEventsRef = useRef8([]);
2409
+ const knownEventPaths = useRef7(/* @__PURE__ */ new Set());
2410
+ const knownFilePaths = useRef7(/* @__PURE__ */ new Set());
2411
+ const allEventsRef = useRef7([]);
2628
2412
  const applyNewEvents = useCallback((snapshot, newEvents) => {
2629
2413
  if (!newEvents.length && allEventsRef.current.length === 0) {
2630
2414
  setEventCount(0);
@@ -2701,10 +2485,10 @@ function useHFState(token, namespace) {
2701
2485
  } catch {
2702
2486
  }
2703
2487
  }, [token, namespace, state, applyNewEvents]);
2704
- useEffect7(() => {
2488
+ useEffect6(() => {
2705
2489
  if (token && namespace) loadFull();
2706
2490
  }, [token, namespace]);
2707
- useEffect7(() => {
2491
+ useEffect6(() => {
2708
2492
  if (!token || !namespace) return;
2709
2493
  const id = setInterval(pollNew, POLL_INTERVAL_MS);
2710
2494
  return () => clearInterval(id);
@@ -2759,7 +2543,7 @@ function useHFState(token, namespace) {
2759
2543
  }
2760
2544
 
2761
2545
  // src/hooks/useTagBase.ts
2762
- import { useState as useState12, useEffect as useEffect8, useCallback as useCallback2, useRef as useRef9 } from "react";
2546
+ import { useState as useState12, useEffect as useEffect7, useCallback as useCallback2, useRef as useRef8 } from "react";
2763
2547
 
2764
2548
  // src/lib/hfTagBaseService.ts
2765
2549
  import JSZip from "jszip";
@@ -2895,13 +2679,13 @@ function useTagBase(token, namespace) {
2895
2679
  const [selectedContextId, setSelectedContextId] = useState12(() => readStoredId(CONTEXT_ID_KEY));
2896
2680
  const [pickerMessage, setPickerMessage] = useState12(null);
2897
2681
  const [pendingTags, setPendingTags] = useState12([]);
2898
- const pendingIdCounter = useRef9(-1);
2899
- const selectedUserIdRef = useRef9(selectedUserId);
2900
- const selectedContextIdRef = useRef9(selectedContextId);
2901
- useEffect8(() => {
2682
+ const pendingIdCounter = useRef8(-1);
2683
+ const selectedUserIdRef = useRef8(selectedUserId);
2684
+ const selectedContextIdRef = useRef8(selectedContextId);
2685
+ useEffect7(() => {
2902
2686
  selectedUserIdRef.current = selectedUserId;
2903
2687
  }, [selectedUserId]);
2904
- useEffect8(() => {
2688
+ useEffect7(() => {
2905
2689
  selectedContextIdRef.current = selectedContextId;
2906
2690
  }, [selectedContextId]);
2907
2691
  const load = useCallback2(async () => {
@@ -2946,7 +2730,7 @@ function useTagBase(token, namespace) {
2946
2730
  setIsLoading(false);
2947
2731
  }
2948
2732
  }, [token, namespace]);
2949
- useEffect8(() => {
2733
+ useEffect7(() => {
2950
2734
  load();
2951
2735
  }, [load]);
2952
2736
  const selectUser = useCallback2((userId) => {
@@ -3045,7 +2829,7 @@ import { useState as useState15 } from "react";
3045
2829
 
3046
2830
  // src/components/labs/LabImagePicker.tsx
3047
2831
  import { useState as useState13 } from "react";
3048
- import { Fragment as Fragment6, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
2832
+ import { Fragment as Fragment5, jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
3049
2833
  var LabImagePicker = ({
3050
2834
  availableItems,
3051
2835
  recentItems,
@@ -3142,7 +2926,7 @@ var LabImagePicker = ({
3142
2926
  }
3143
2927
  ) }),
3144
2928
  /* @__PURE__ */ jsxs14("div", { className: "flex-1 overflow-y-auto dark-scrollbar px-3 pb-3", children: [
3145
- recentItems.length > 0 && !search && /* @__PURE__ */ jsxs14(Fragment6, { children: [
2929
+ recentItems.length > 0 && !search && /* @__PURE__ */ jsxs14(Fragment5, { children: [
3146
2930
  /* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2 mt-1", children: "Zuletzt verwendet" }),
3147
2931
  /* @__PURE__ */ jsx16("div", { className: "grid grid-cols-3 gap-2 mb-4", children: recentItems.slice(0, 6).map(renderItemCard) }),
3148
2932
  /* @__PURE__ */ jsx16("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/30 mb-2", children: "Alle Bilder" })
@@ -3257,7 +3041,7 @@ function useGenerationSettings() {
3257
3041
  }
3258
3042
 
3259
3043
  // src/components/labs/LabRemix.tsx
3260
- import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
3044
+ import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
3261
3045
  var LabRemix = ({ services, onResult }) => {
3262
3046
  const [showPicker, setShowPicker] = useState15(false);
3263
3047
  const [selected, setSelected] = useState15(null);
@@ -3413,10 +3197,10 @@ var LabRemix = ({ services, onResult }) => {
3413
3197
  onClick: handleGeneratePrompt,
3414
3198
  disabled: !selected || !instruction.trim() || isGeneratingPrompt,
3415
3199
  className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
3416
- children: isGeneratingPrompt ? /* @__PURE__ */ jsxs16(Fragment7, { children: [
3200
+ children: isGeneratingPrompt ? /* @__PURE__ */ jsxs16(Fragment6, { children: [
3417
3201
  /* @__PURE__ */ jsx18("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
3418
3202
  /* @__PURE__ */ jsx18("span", { children: "Generiere Prompt..." })
3419
- ] }) : /* @__PURE__ */ jsxs16(Fragment7, { children: [
3203
+ ] }) : /* @__PURE__ */ jsxs16(Fragment6, { children: [
3420
3204
  /* @__PURE__ */ jsx18("span", { className: "material-symbols-outlined text-[16px]", children: "auto_fix_high" }),
3421
3205
  /* @__PURE__ */ jsx18("span", { children: "Prompt generieren" })
3422
3206
  ] })
@@ -3463,7 +3247,7 @@ var LabRemix = ({ services, onResult }) => {
3463
3247
 
3464
3248
  // src/components/labs/LabBlend.tsx
3465
3249
  import { useState as useState16 } from "react";
3466
- import { Fragment as Fragment8, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
3250
+ import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
3467
3251
  var LabBlend = ({ services, onResult }) => {
3468
3252
  const [showPickerFor, setShowPickerFor] = useState16(null);
3469
3253
  const [selectedImages, setSelectedImages] = useState16([]);
@@ -3558,7 +3342,7 @@ var LabBlend = ({ services, onResult }) => {
3558
3342
  /* @__PURE__ */ jsx19("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder" }),
3559
3343
  selectedImages.map((img, i) => /* @__PURE__ */ jsxs17("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
3560
3344
  /* @__PURE__ */ jsx19("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
3561
- img ? /* @__PURE__ */ jsxs17(Fragment8, { children: [
3345
+ img ? /* @__PURE__ */ jsxs17(Fragment7, { children: [
3562
3346
  /* @__PURE__ */ jsx19("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0", onClick: () => setShowPickerFor(i) }),
3563
3347
  /* @__PURE__ */ jsxs17("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
3564
3348
  /* @__PURE__ */ jsx19("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
@@ -3624,10 +3408,10 @@ var LabBlend = ({ services, onResult }) => {
3624
3408
  onClick: handleGeneratePrompt,
3625
3409
  disabled: selectedImages.filter(Boolean).length < 2 || !instruction.trim() || isGeneratingPrompt,
3626
3410
  className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
3627
- children: isGeneratingPrompt ? /* @__PURE__ */ jsxs17(Fragment8, { children: [
3411
+ children: isGeneratingPrompt ? /* @__PURE__ */ jsxs17(Fragment7, { children: [
3628
3412
  /* @__PURE__ */ jsx19("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
3629
3413
  /* @__PURE__ */ jsx19("span", { children: "Blend l\xE4uft..." })
3630
- ] }) : /* @__PURE__ */ jsxs17(Fragment8, { children: [
3414
+ ] }) : /* @__PURE__ */ jsxs17(Fragment7, { children: [
3631
3415
  /* @__PURE__ */ jsx19("span", { className: "material-symbols-outlined text-[16px]", children: "merge" }),
3632
3416
  /* @__PURE__ */ jsx19("span", { children: "Prompts blenden" })
3633
3417
  ] })
@@ -3674,7 +3458,7 @@ var LabBlend = ({ services, onResult }) => {
3674
3458
 
3675
3459
  // src/components/labs/LabCompare.tsx
3676
3460
  import { useState as useState17 } from "react";
3677
- import { Fragment as Fragment9, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
3461
+ import { Fragment as Fragment8, jsx as jsx20, jsxs as jsxs18 } from "react/jsx-runtime";
3678
3462
  var LabCompare = ({ services, onResult }) => {
3679
3463
  const [showPickerFor, setShowPickerFor] = useState17(null);
3680
3464
  const [selectedImages, setSelectedImages] = useState17([]);
@@ -3756,7 +3540,7 @@ var LabCompare = ({ services, onResult }) => {
3756
3540
  /* @__PURE__ */ jsx20("p", { className: "text-[9px] font-bold uppercase tracking-widest text-white/40", children: "Bilder zum Vergleichen" }),
3757
3541
  selectedImages.map((img, i) => /* @__PURE__ */ jsxs18("div", { className: "flex gap-3 p-3 rounded-xl border border-white/10 bg-white/5", children: [
3758
3542
  /* @__PURE__ */ jsx20("span", { className: "text-[10px] font-bold text-white/40 w-6 shrink-0 pt-1", children: autoLabel(i) }),
3759
- img ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
3543
+ img ? /* @__PURE__ */ jsxs18(Fragment8, { children: [
3760
3544
  /* @__PURE__ */ jsx20("img", { src: img.frame.base64, className: "w-12 h-12 object-cover rounded-lg shrink-0 cursor-pointer", onClick: () => setShowPickerFor(i) }),
3761
3545
  /* @__PURE__ */ jsxs18("div", { className: "flex-1 min-w-0 flex flex-col gap-1", children: [
3762
3546
  /* @__PURE__ */ jsx20("p", { className: "text-[10px] text-white/50 line-clamp-1", children: img.item.prompt }),
@@ -3800,10 +3584,10 @@ var LabCompare = ({ services, onResult }) => {
3800
3584
  onClick: handleAnalyze,
3801
3585
  disabled: selectedImages.filter(Boolean).length < 1 || !instruction.trim() || isAnalyzing,
3802
3586
  className: "w-full py-3 rounded-xl bg-white/10 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-white/15 flex items-center justify-center gap-2",
3803
- children: isAnalyzing ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
3587
+ children: isAnalyzing ? /* @__PURE__ */ jsxs18(Fragment8, { children: [
3804
3588
  /* @__PURE__ */ jsx20("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
3805
3589
  /* @__PURE__ */ jsx20("span", { children: "Analysiere..." })
3806
- ] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
3590
+ ] }) : /* @__PURE__ */ jsxs18(Fragment8, { children: [
3807
3591
  /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[16px]", children: "compare" }),
3808
3592
  /* @__PURE__ */ jsx20("span", { children: "Analysieren" })
3809
3593
  ] })
@@ -3830,10 +3614,10 @@ var LabCompare = ({ services, onResult }) => {
3830
3614
  onClick: handleGenerateImage,
3831
3615
  disabled: !generatedPrompt.trim() || isGeneratingImage,
3832
3616
  className: "w-full mt-2 py-3 rounded-xl bg-blue-600/80 text-white font-bold text-[12px] uppercase tracking-wide disabled:opacity-30 active:bg-blue-600 flex items-center justify-center gap-2",
3833
- children: isGeneratingImage ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
3617
+ children: isGeneratingImage ? /* @__PURE__ */ jsxs18(Fragment8, { children: [
3834
3618
  /* @__PURE__ */ jsx20("div", { className: "w-3 h-3 border-t border-white rounded-full animate-spin" }),
3835
3619
  /* @__PURE__ */ jsx20("span", { children: "Generiere..." })
3836
- ] }) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
3620
+ ] }) : /* @__PURE__ */ jsxs18(Fragment8, { children: [
3837
3621
  /* @__PURE__ */ jsx20("span", { className: "material-symbols-outlined text-[16px]", children: "image" }),
3838
3622
  /* @__PURE__ */ jsx20("span", { children: "Bild generieren" })
3839
3623
  ] })
@@ -4017,7 +3801,7 @@ var LabLoop = ({ services, onResult }) => {
4017
3801
  };
4018
3802
 
4019
3803
  // src/components/labs/LabFrameExtractor.tsx
4020
- import { useRef as useRef10, useState as useState19, useCallback as useCallback3 } from "react";
3804
+ import { useRef as useRef9, useState as useState19, useCallback as useCallback3 } from "react";
4021
3805
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
4022
3806
  var formatTime = (s) => {
4023
3807
  const m = Math.floor(s / 60);
@@ -4031,9 +3815,9 @@ var LabFrameExtractor = ({
4031
3815
  onResult,
4032
3816
  resolveVideoUrl
4033
3817
  }) => {
4034
- const videoRef = useRef10(null);
4035
- const canvasRef = useRef10(null);
4036
- const cancelledRef = useRef10(false);
3818
+ const videoRef = useRef9(null);
3819
+ const canvasRef = useRef9(null);
3820
+ const cancelledRef = useRef9(false);
4037
3821
  const [selectedItem, setSelectedItem] = useState19(null);
4038
3822
  const [videoSrc, setVideoSrc] = useState19(null);
4039
3823
  const [videoReady, setVideoReady] = useState19(false);
@@ -5425,7 +5209,7 @@ function HFTestTab({ token, namespace, galleryItems, allEvents = [], confirmedEv
5425
5209
  }
5426
5210
 
5427
5211
  // src/components/ServerTab.tsx
5428
- import { useState as useState23, useEffect as useEffect9 } from "react";
5212
+ import { useState as useState23, useEffect as useEffect8 } from "react";
5429
5213
  import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
5430
5214
  function StarRating({ rating = 0 }) {
5431
5215
  return /* @__PURE__ */ jsx27("div", { className: "flex gap-[2px]", children: [1, 2, 3, 4, 5].map((i) => /* @__PURE__ */ jsx27("span", { className: `material-symbols-outlined text-[12px] ${i <= rating ? "text-yellow-400" : "text-white/15"}`, children: "star" }, i)) });
@@ -5452,7 +5236,7 @@ function ServerTab({ serverBaseUrl }) {
5452
5236
  const [libError, setLibError] = useState23(null);
5453
5237
  const [activeTag, setActiveTag] = useState23(null);
5454
5238
  const [preview, setPreview] = useState23(null);
5455
- useEffect9(() => {
5239
+ useEffect8(() => {
5456
5240
  if (!serverBaseUrl) return;
5457
5241
  setUsersLoading(true);
5458
5242
  setUsersError(null);
@@ -5604,7 +5388,7 @@ function ServerTab({ serverBaseUrl }) {
5604
5388
 
5605
5389
  // src/components/FlowWorkspacePicker.tsx
5606
5390
  import { useMemo as useMemo5, useState as useState24 } from "react";
5607
- import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
5391
+ import { Fragment as Fragment9, jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
5608
5392
  function FlowWorkspacePicker({
5609
5393
  tagBase,
5610
5394
  selectedUserId,
@@ -5646,7 +5430,7 @@ function FlowWorkspacePicker({
5646
5430
  tagBase.meta.version,
5647
5431
  " enth\xE4lt keine Benutzer."
5648
5432
  ] })
5649
- ] }) : /* @__PURE__ */ jsxs26(Fragment10, { children: [
5433
+ ] }) : /* @__PURE__ */ jsxs26(Fragment9, { children: [
5650
5434
  /* @__PURE__ */ jsx28("button", { className: "flow-workspace-picker__back", type: "button", onClick: back, children: "\u2190 Anderen Benutzer w\xE4hlen" }),
5651
5435
  /* @__PURE__ */ jsxs26("div", { className: "flow-workspace-picker__choices", "aria-label": "Kontext w\xE4hlen", children: [
5652
5436
  contexts.map((context) => /* @__PURE__ */ jsxs26("button", { type: "button", onClick: () => onSelectContext(context.id), children: [
@@ -5725,9 +5509,9 @@ function resolveBootstrapTarget(input) {
5725
5509
  }
5726
5510
 
5727
5511
  // src/components/AvatarArchitectApp.tsx
5728
- import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5729
- function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl, onFetchServerProjects, onServerSave, onServerLoad, onServerDelete }) {
5730
- useEffect10(() => {
5512
+ import { Fragment as Fragment10, jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
5513
+ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onSelectMedia, buildInfo, initialHfToken, hfNamespace, allowDevNamespace, requireWorkspaceSelection = false, serverBaseUrl }) {
5514
+ useEffect9(() => {
5731
5515
  const id = "flow-styles";
5732
5516
  if (!document.getElementById(id)) {
5733
5517
  const style = document.createElement("style");
@@ -5745,10 +5529,8 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5745
5529
  return null;
5746
5530
  }
5747
5531
  });
5748
- const [projectLoaded, setProjectLoaded] = useState25(false);
5749
5532
  const [hfToken, setHfToken] = useState25(initialHfToken || "");
5750
5533
  const [hfTokenInput, setHfTokenInput] = useState25(initialHfToken || "");
5751
- const [isLoadingFromHF, setIsLoadingFromHF] = useState25(false);
5752
5534
  const [hfNamespaceLocal, setHfNamespaceLocal] = useState25(() => {
5753
5535
  try {
5754
5536
  return normalizeSelectableNamespace(localStorage.getItem("aa-hf-namespace"));
@@ -5758,7 +5540,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5758
5540
  });
5759
5541
  const [hfNamespaceFromServer, setHfNamespaceFromServer] = useState25(null);
5760
5542
  const [serverProbeFailed, setServerProbeFailed] = useState25(false);
5761
- useEffect10(() => {
5543
+ useEffect9(() => {
5762
5544
  if (hfNamespace !== void 0 || !serverBaseUrl) return;
5763
5545
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
5764
5546
  if (typeof d.hfNamespace === "string" && d.hfNamespace) setHfNamespaceFromServer(d.hfNamespace);
@@ -5776,7 +5558,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5776
5558
  isStartDialogOpen: isDialogTarget && !targetConfirmed
5777
5559
  });
5778
5560
  const effectiveNamespace = bootstrapTarget.namespace ?? "";
5779
- const kontextRef = useRef11(null);
5561
+ const kontextRef = useRef10(null);
5780
5562
  const szenenPort = useMemo6(() => hfToken && effectiveNamespace ? createFlowSzenenViewPort({
5781
5563
  generateText: onGeneratePrompt,
5782
5564
  token: hfToken,
@@ -5803,7 +5585,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5803
5585
  const [bootstrapLog, setBootstrapLog] = useState25([]);
5804
5586
  const [isBootstrapping, setIsBootstrapping] = useState25(false);
5805
5587
  const [hfMissingImages, setHfMissingImages] = useState25([]);
5806
- const syncTopSlot = /* @__PURE__ */ jsxs27(Fragment11, { children: [
5588
+ const syncTopSlot = /* @__PURE__ */ jsxs27(Fragment10, { children: [
5807
5589
  localOnlyCount > 0 && /* @__PURE__ */ jsxs27("div", { style: { background: "rgba(234,179,8,0.15)", border: "1px solid rgba(234,179,8,0.3)", padding: "4px 10px", fontSize: 11, color: "#fbbf24", borderRadius: 4, marginBottom: 4 }, children: [
5808
5590
  "\u26A0 ",
5809
5591
  localOnlyCount,
@@ -5852,7 +5634,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5852
5634
  bootstrapLog.length > 0 && /* @__PURE__ */ jsx29("div", { style: { marginTop: 6, fontFamily: "monospace", fontSize: 10, color: "#78716c", lineHeight: 1.6 }, children: bootstrapLog.map((l, i) => /* @__PURE__ */ jsx29("div", { children: l }, i)) })
5853
5635
  ] })
5854
5636
  ] });
5855
- const wsInputRef = useRef11(null);
5856
5637
  const startApp = (choice) => {
5857
5638
  try {
5858
5639
  localStorage.setItem("aa-layout", choice);
@@ -5866,11 +5647,11 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5866
5647
  const [edges, setEdges] = useState25([]);
5867
5648
  const [history, setHistory] = useState25([]);
5868
5649
  const [galleryItems, setGalleryItems] = useState25([]);
5869
- const galleryItemsRef = useRef11([]);
5870
- useEffect10(() => {
5650
+ const galleryItemsRef = useRef10([]);
5651
+ useEffect9(() => {
5871
5652
  galleryItemsRef.current = galleryItems;
5872
5653
  }, [galleryItems]);
5873
- const hfImageNotFoundRef = useRef11(/* @__PURE__ */ new Map());
5654
+ const hfImageNotFoundRef = useRef10(/* @__PURE__ */ new Map());
5874
5655
  const [galleryVisibleCount, setGalleryVisibleCount] = useState25(20);
5875
5656
  const [historyVisibleCount, setHistoryVisibleCount] = useState25(20);
5876
5657
  const loadThumbnailsForEntries = useCallback4(async (entries) => {
@@ -5892,7 +5673,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5892
5673
  }
5893
5674
  }
5894
5675
  }, [hfToken, effectiveNamespace]);
5895
- useEffect10(() => {
5676
+ useEffect9(() => {
5896
5677
  if (!hfState) return;
5897
5678
  if (hfState.tags?.by_category) setWorkspaceTags(hfState.tags);
5898
5679
  const hfIds = new Set(hfState.metadata.map((m) => m.id));
@@ -5919,14 +5700,14 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5919
5700
  return [...localOnly, ...merged].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
5920
5701
  });
5921
5702
  }, [hfState]);
5922
- useEffect10(() => {
5703
+ useEffect9(() => {
5923
5704
  if (!hfState) return;
5924
5705
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
5925
5706
  const skeletons = sortedMeta.map((m) => ({ id: m.id, prompt: m.prompt, timestamp: m.timestamp, status: "done", nodeId: m.id, tags: m.tags || [] }));
5926
5707
  const repIds = new Set(groupByPrompt(skeletons).slice(0, galleryVisibleCount).map((g) => g.representative.id));
5927
5708
  loadThumbnailsForEntries(sortedMeta.filter((e) => repIds.has(e.id)));
5928
5709
  }, [hfState, galleryVisibleCount, loadThumbnailsForEntries]);
5929
- useEffect10(() => {
5710
+ useEffect9(() => {
5930
5711
  if (!hfState) return;
5931
5712
  const sortedMeta = [...hfState.metadata].sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
5932
5713
  loadThumbnailsForEntries(sortedMeta.slice(0, historyVisibleCount));
@@ -5987,11 +5768,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
5987
5768
  }
5988
5769
  });
5989
5770
  const [isPromptCollapsed, setIsPromptCollapsed] = useState25(false);
5990
- const [projectActionState, setProjectActionState] = useState25("idle");
5991
- const syncServerDataRef = useRef11(null);
5992
5771
  const [workspaceTags, setWorkspaceTags] = useState25(null);
5993
- const [serverProjects, setServerProjects] = useState25([]);
5994
- const [isLoadingFromServer, setIsLoadingFromServer] = useState25(false);
5995
5772
  const [highContrast, setHighContrast] = useState25(() => {
5996
5773
  try {
5997
5774
  return localStorage.getItem("aa-contrast") === "high";
@@ -6006,9 +5783,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6006
5783
  const [isFullscreen, setIsFullscreen] = useState25(false);
6007
5784
  const [zoomScale, setZoomScale] = useState25(1);
6008
5785
  const [zoomOffset, setZoomOffset] = useState25({ x: 0, y: 0 });
6009
- const lastPinchDist = useRef11(null);
6010
- const lastTapTime = useRef11(0);
6011
- const dragStart = useRef11(null);
5786
+ const lastPinchDist = useRef10(null);
5787
+ const lastTapTime = useRef10(0);
5788
+ const dragStart = useRef10(null);
6012
5789
  const openFullscreen = () => {
6013
5790
  setIsFullscreen(true);
6014
5791
  setZoomScale(1);
@@ -6186,7 +5963,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6186
5963
  const group = groups.find((gr) => gr.prompt === currentResult.prompt);
6187
5964
  return group ? group.items : [];
6188
5965
  }, [galleryItems, currentResult?.prompt]);
6189
- useEffect10(() => {
5966
+ useEffect9(() => {
6190
5967
  if (!currentResult?.prompt || !hfToken || !effectiveNamespace || !hfState) return;
6191
5968
  const siblings = galleryItemsRef.current.filter(
6192
5969
  (g) => g.prompt === currentResult.prompt && !g.base64 && !hfImageNotFoundRef.current.has(g.id)
@@ -6439,51 +6216,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6439
6216
  console.error("Download Error", e);
6440
6217
  }
6441
6218
  };
6442
- const handleProjectExport = async () => {
6443
- setProjectActionState("working-full");
6444
- try {
6445
- const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
6446
- await onDownload(base64, "application/zip", `avatar_project_${getFormattedTimestamp()}.zip`);
6447
- setProjectActionState("done");
6448
- setTimeout(() => setProjectActionState("idle"), 3e3);
6449
- } catch {
6450
- setProjectActionState("error");
6451
- }
6452
- };
6453
- const handleProjectImport = async (input) => {
6454
- const file = input instanceof FileList ? input[0] : input;
6455
- if (!file) return;
6456
- setProjectActionState("working-full");
6457
- try {
6458
- const data = await importProjectFromZip(file);
6459
- if (data.nodes) setNodes(data.nodes);
6460
- if (data.edges) setEdges(data.edges);
6461
- if (data.history) {
6462
- setHistory(data.history);
6463
- if (data.history.length > 0) setCurrentResult(data.history[0]);
6464
- }
6465
- if (data.galleryItems) setGalleryItems(data.galleryItems);
6466
- if (data.settings) {
6467
- setAspectRatio(data.settings.aspectRatio || "1:1");
6468
- setSelectedModel(data.settings.selectedModel || "\u{1F34C} Nano Banana Pro");
6469
- setSeed(data.settings.seed || 0);
6470
- setSeedMode(data.settings.seedMode || "random");
6471
- }
6472
- if (data.workspaceTags?.by_category) setWorkspaceTags(data.workspaceTags);
6473
- if (data.recentLabItemIds?.length) {
6474
- const allGens = [...data.history || [], ...data.galleryItems || []];
6475
- const labItems = groupGenerationsToLabItems(allGens);
6476
- const recent = data.recentLabItemIds.map((id) => labItems.find((item) => item.id === id)).filter(Boolean);
6477
- setRecentLabItems(recent);
6478
- }
6479
- setProjectActionState("done");
6480
- setProjectLoaded(true);
6481
- setTimeout(() => setProjectActionState("idle"), 2e3);
6482
- } catch {
6483
- setProjectActionState("error");
6484
- setTimeout(() => setProjectActionState("idle"), 4e3);
6485
- }
6486
- };
6487
6219
  const handleWorkspaceImport = (file) => {
6488
6220
  const reader = new FileReader();
6489
6221
  reader.onload = (ev) => {
@@ -6505,37 +6237,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6505
6237
  };
6506
6238
  reader.readAsText(file);
6507
6239
  };
6508
- const fetchServerProjects = async () => {
6509
- if (!onFetchServerProjects) return;
6510
- try {
6511
- setServerProjects(await onFetchServerProjects());
6512
- } catch {
6513
- }
6514
- };
6515
- const handleServerSave = async (name) => {
6516
- if (!onServerSave) return;
6517
- setProjectActionState("working-full");
6518
- try {
6519
- const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
6520
- await onServerSave(base64, name || `avatar_project_${getFormattedTimestamp()}`);
6521
- await fetchServerProjects();
6522
- setProjectActionState("done");
6523
- setTimeout(() => setProjectActionState("idle"), 2e3);
6524
- } catch {
6525
- setProjectActionState("error");
6526
- setTimeout(() => setProjectActionState("idle"), 4e3);
6527
- }
6528
- };
6529
- const handleServerLoad = async (id) => {
6530
- if (!onServerLoad) return;
6531
- const file = await onServerLoad(id);
6532
- await handleProjectImport(file);
6533
- };
6534
- const handleServerDelete = async (id) => {
6535
- if (!onServerDelete) return;
6536
- await onServerDelete(id);
6537
- await fetchServerProjects();
6538
- };
6539
6240
  const handleHfInitialSync = async (onProgress) => {
6540
6241
  if (!hfToken || !effectiveNamespace) return;
6541
6242
  const existingIds = new Set((hfState?.metadata || []).map((m) => m.id));
@@ -6563,45 +6264,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6563
6264
  onProgress(done, total);
6564
6265
  }
6565
6266
  };
6566
- const handleComputeSyncDiff = async () => {
6567
- if (!onFetchServerProjects || !onServerLoad) throw new Error("Server nicht konfiguriert");
6568
- const projects = await onFetchServerProjects();
6569
- if (projects.length === 0) return { localOnly: [...history], serverOnly: [] };
6570
- const file = await onServerLoad(projects[0].id);
6571
- const serverData = await importProjectFromZip(file);
6572
- syncServerDataRef.current = serverData;
6573
- const localOnly = history.filter((h) => !(serverData.history || []).find((s) => s.id === h.id));
6574
- const serverOnly = (serverData.history || []).filter((s) => !history.find((h) => h.id === s.id));
6575
- return { localOnly, serverOnly };
6576
- };
6577
- const handleExecuteSync = async (selectedLocalIds) => {
6578
- const serverData = syncServerDataRef.current;
6579
- if (!serverData || !onServerSave) return;
6580
- setProjectActionState("working-full");
6581
- try {
6582
- const selectedLocal = history.filter((h) => selectedLocalIds.includes(h.id));
6583
- const mergedHistory = [...serverData.history || [], ...selectedLocal];
6584
- const { base64 } = await exportProjectToZip(
6585
- serverData.nodes || nodes,
6586
- serverData.edges || edges,
6587
- mergedHistory,
6588
- serverData.galleryItems || galleryItems,
6589
- serverData.settings || { aspectRatio, selectedModel, seed, seedMode },
6590
- serverData.workspaceTags || workspaceTags,
6591
- serverData.recentLabItemIds || recentLabItems.map((i) => i.id)
6592
- );
6593
- await onServerSave(base64, `sync_${getFormattedTimestamp()}`);
6594
- await fetchServerProjects();
6595
- setProjectActionState("done");
6596
- setTimeout(() => setProjectActionState("idle"), 2e3);
6597
- } catch {
6598
- setProjectActionState("error");
6599
- setTimeout(() => setProjectActionState("idle"), 4e3);
6600
- }
6601
- };
6602
- useEffect10(() => {
6603
- if (activeTab === "setup" || activeTab === "sync") fetchServerProjects();
6604
- }, [activeTab]);
6605
6267
  const mergeWorkspaceTags = (local, remote) => {
6606
6268
  if (!remote?.by_category) return local;
6607
6269
  const merged = {};
@@ -6654,7 +6316,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6654
6316
  setZoomScale(1);
6655
6317
  setZoomOffset({ x: 0, y: 0 });
6656
6318
  }, 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__ */ jsx29("span", { className: "material-symbols-outlined text-[20px]", children: "zoom_out_map" }) }),
6657
- history.length > 1 && /* @__PURE__ */ jsxs27(Fragment11, { children: [
6319
+ history.length > 1 && /* @__PURE__ */ jsxs27(Fragment10, { children: [
6658
6320
  /* @__PURE__ */ jsx29("button", { onClick: () => {
6659
6321
  if (currentIndex > 0) setCurrentResult(history[currentIndex - 1]);
6660
6322
  }, 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__ */ jsx29("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
@@ -6692,11 +6354,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6692
6354
  }
6693
6355
  if (showStart) {
6694
6356
  return /* @__PURE__ */ jsxs27("div", { className: "fixed inset-0 bg-[#0e0e0e] flex flex-col items-center justify-center p-6", style: { gap: 28, ...hcStyle }, children: [
6695
- /* @__PURE__ */ jsx29("input", { ref: wsInputRef, type: "file", accept: ".zip", className: "hidden", onChange: (e) => {
6696
- const f = e.target.files?.[0];
6697
- if (f) handleProjectImport(f);
6698
- e.target.value = "";
6699
- } }),
6700
6357
  /* @__PURE__ */ jsxs27("div", { className: "flex flex-col items-center gap-1", children: [
6701
6358
  /* @__PURE__ */ jsx29("span", { className: "material-symbols-outlined text-white/15 text-[44px]", children: "palette" }),
6702
6359
  /* @__PURE__ */ jsx29("span", { className: "text-white/25 text-[10px] font-bold uppercase tracking-[0.25em]", children: "Avatar Architect" }),
@@ -6720,21 +6377,6 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6720
6377
  ]
6721
6378
  }
6722
6379
  ),
6723
- /* @__PURE__ */ jsxs27("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
6724
- /* @__PURE__ */ jsxs27(
6725
- "button",
6726
- {
6727
- onClick: () => wsInputRef.current?.click(),
6728
- 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",
6729
- style: { height: 56, background: projectLoaded ? "#16a34a" : "#0284c7" },
6730
- children: [
6731
- /* @__PURE__ */ jsx29("span", { className: "material-symbols-outlined text-[22px]", children: projectLoaded ? "check_circle" : "folder_zip" }),
6732
- projectLoaded ? "Projekt geladen \u2713" : "Projekt laden (.zip)"
6733
- ]
6734
- }
6735
- ),
6736
- !projectLoaded && /* @__PURE__ */ jsx29("span", { className: "text-white/20 text-[10px] text-center", children: "Baum, Bilder und Einstellungen wiederherstellen" })
6737
- ] }),
6738
6380
  /* @__PURE__ */ jsxs27("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
6739
6381
  !initialHfToken && /* @__PURE__ */ jsxs27("div", { className: "flex gap-2 w-full", children: [
6740
6382
  /* @__PURE__ */ jsx29(
@@ -6785,64 +6427,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
6785
6427
  },
6786
6428
  ns
6787
6429
  ))
6788
- ] }),
6789
- hfToken && /* @__PURE__ */ jsxs27(
6790
- "button",
6791
- {
6792
- disabled: isLoadingFromHF,
6793
- onClick: async () => {
6794
- setIsLoadingFromHF(true);
6795
- try {
6796
- const { hfListProjects: hfListProjects2, hfDownloadProject: hfDownloadProject2 } = await import("./hfStateService-W7KO2FOH.mjs");
6797
- const projects = await hfListProjects2(hfToken);
6798
- if (projects.length > 0) {
6799
- const file = await hfDownloadProject2(projects[0].path, hfToken);
6800
- await handleProjectImport(file);
6801
- }
6802
- } catch (e) {
6803
- alert("HF Fehler: " + e.message);
6804
- } finally {
6805
- setIsLoadingFromHF(false);
6806
- }
6807
- },
6808
- 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",
6809
- style: { height: 56, background: "#f59e0b" },
6810
- children: [
6811
- /* @__PURE__ */ jsx29("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromHF ? " animate-spin" : ""}`, children: isLoadingFromHF ? "sync" : "cloud_download" }),
6812
- isLoadingFromHF ? "Laden\u2026" : "Von HF laden"
6813
- ]
6814
- }
6815
- ),
6816
- hfToken && /* @__PURE__ */ jsx29("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand von Hugging Face laden" })
6817
- ] }),
6818
- onFetchServerProjects && /* @__PURE__ */ jsxs27("div", { className: "flex flex-col items-center gap-2 w-full max-w-[280px]", children: [
6819
- /* @__PURE__ */ jsxs27(
6820
- "button",
6821
- {
6822
- disabled: isLoadingFromServer,
6823
- onClick: async () => {
6824
- setIsLoadingFromServer(true);
6825
- try {
6826
- const projects = await onFetchServerProjects();
6827
- setServerProjects(projects);
6828
- if (projects.length > 0 && onServerLoad) {
6829
- const file = await onServerLoad(projects[0].id);
6830
- await handleProjectImport(file);
6831
- }
6832
- } catch {
6833
- } finally {
6834
- setIsLoadingFromServer(false);
6835
- }
6836
- },
6837
- 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",
6838
- style: { height: 56, background: "#7c3aed" },
6839
- children: [
6840
- /* @__PURE__ */ jsx29("span", { className: `material-symbols-outlined text-[22px]${isLoadingFromServer ? " animate-spin" : ""}`, children: isLoadingFromServer ? "sync" : "cloud_download" }),
6841
- isLoadingFromServer ? "Laden\u2026" : "Vom Server laden"
6842
- ]
6843
- }
6844
- ),
6845
- /* @__PURE__ */ jsx29("span", { className: "text-white/20 text-[10px] text-center", children: "Letzten Stand vom Server wiederherstellen" })
6430
+ ] })
6846
6431
  ] }),
6847
6432
  /* @__PURE__ */ jsxs27("div", { className: "flow-szenen-entry", children: [
6848
6433
  /* @__PURE__ */ jsxs27(
@@ -7029,7 +6614,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7029
6614
  /* @__PURE__ */ jsx29("span", { className: "text-[11px] font-bold uppercase tracking-[0.2em]", children: "Avatar Architect" })
7030
6615
  ] }),
7031
6616
  currentResult?.status === "done" && /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined text-[18px]", children: "fullscreen" }) }),
7032
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs27(Fragment11, { children: [
6617
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs27(Fragment10, { children: [
7033
6618
  /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_left" }) }),
7034
6619
  /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined text-[22px]", children: "chevron_right" }) }),
7035
6620
  /* @__PURE__ */ jsxs27("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: [
@@ -7200,26 +6785,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7200
6785
  }, onTagCreate: handleTagCreate, onTagUpdate: handleTagUpdate, onTagDelete: handleTagDelete, onScanImage: handleScanImage, isScanning: isScanningImage }) }),
7201
6786
  activeTab === "setup" && /* @__PURE__ */ jsx29(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo, tagBase: tagBaseResult }),
7202
6787
  activeTab === "sync" && /* @__PURE__ */ jsx29(
7203
- ProjectSyncTab,
6788
+ HfMediaSyncTab,
7204
6789
  {
7205
6790
  topSlot: syncTopSlot,
7206
- onProjectExport: handleProjectExport,
7207
- onProjectImport: (f) => handleProjectImport(f),
7208
- onWorkspaceImport: handleWorkspaceImport,
7209
- projectActionState,
7210
- serverProjects: onFetchServerProjects ? serverProjects : void 0,
7211
- onServerSave: onServerSave ? handleServerSave : void 0,
7212
- onServerLoad: onServerLoad ? handleServerLoad : void 0,
7213
- onServerDelete: onServerDelete ? handleServerDelete : void 0,
7214
- onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
7215
- onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
7216
- onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0,
7217
- hfToken: hfToken || void 0,
7218
- onHfLoad: (f) => handleProjectImport(f),
7219
- onProjectExportBase64: async () => {
7220
- const { base64 } = await exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id));
7221
- return base64;
7222
- },
7223
6791
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
7224
6792
  }
7225
6793
  ),
@@ -7338,7 +6906,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7338
6906
  /* @__PURE__ */ jsx29("span", { style: { fontSize: 11, fontWeight: "bold", textTransform: "uppercase", letterSpacing: "0.2em" }, children: "Avatar Architect" })
7339
6907
  ] }),
7340
6908
  currentResult?.status === "done" && /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined", style: { fontSize: 18 }, children: "fullscreen" }) }),
7341
- history.length > 1 && currentResult && /* @__PURE__ */ jsxs27(Fragment11, { children: [
6909
+ history.length > 1 && currentResult && /* @__PURE__ */ jsxs27(Fragment10, { children: [
7342
6910
  /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_left" }) }),
7343
6911
  /* @__PURE__ */ jsx29("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__ */ jsx29("span", { className: "material-symbols-outlined", style: { fontSize: 20 }, children: "chevron_right" }) }),
7344
6912
  /* @__PURE__ */ jsxs27("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: [
@@ -7601,26 +7169,9 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7601
7169
  activeTab === "inspect" && /* @__PURE__ */ jsx29(InspectPanel, { currentResult, history, onSelect: setCurrentResult }),
7602
7170
  activeTab === "setup" && /* @__PURE__ */ jsx29(SetupPanel, { onWorkspaceImport: handleWorkspaceImport, buildInfo, tagBase: tagBaseResult }),
7603
7171
  activeTab === "sync" && /* @__PURE__ */ jsx29(
7604
- ProjectSyncTab,
7172
+ HfMediaSyncTab,
7605
7173
  {
7606
7174
  topSlot: syncTopSlot,
7607
- onProjectExport: handleProjectExport,
7608
- onProjectImport: (f) => handleProjectImport(f),
7609
- onWorkspaceImport: handleWorkspaceImport,
7610
- projectActionState,
7611
- serverProjects: onFetchServerProjects ? serverProjects : void 0,
7612
- onServerSave: onServerSave ? handleServerSave : void 0,
7613
- onServerLoad: onServerLoad ? handleServerLoad : void 0,
7614
- onServerDelete: onServerDelete ? handleServerDelete : void 0,
7615
- onRefreshServerProjects: onFetchServerProjects ? fetchServerProjects : void 0,
7616
- onComputeSyncDiff: onFetchServerProjects && onServerLoad && onServerSave ? handleComputeSyncDiff : void 0,
7617
- onExecuteSync: onFetchServerProjects && onServerLoad && onServerSave ? handleExecuteSync : void 0,
7618
- hfToken: hfToken || void 0,
7619
- onHfLoad: (f) => handleProjectImport(f),
7620
- onProjectExportBase64: async () => {
7621
- const { base64 } = await import("./project-O4ORKXY5.mjs").then((m) => m.exportProjectToZip(nodes, edges, history, galleryItems, { aspectRatio, selectedModel, seed, seedMode }, workspaceTags, recentLabItems.map((i) => i.id)));
7622
- return base64;
7623
- },
7624
7175
  onHfInitialSync: hfToken ? handleHfInitialSync : void 0
7625
7176
  }
7626
7177
  ),
@@ -7631,7 +7182,7 @@ function AvatarArchitectApp({ onGenerateImage, onGeneratePrompt, onDownload, onS
7631
7182
  }
7632
7183
 
7633
7184
  // src/components/FaApp.tsx
7634
- import { useState as useState26, useEffect as useEffect11, useMemo as useMemo7 } from "react";
7185
+ import { useState as useState26, useEffect as useEffect10, useMemo as useMemo7 } from "react";
7635
7186
  import { jsx as jsx30 } from "react/jsx-runtime";
7636
7187
  function FaApp({
7637
7188
  onGenerateImage,
@@ -7645,14 +7196,10 @@ function FaApp({
7645
7196
  libToken,
7646
7197
  allowDevNamespace,
7647
7198
  serverBaseUrl,
7648
- onFetchServerProjects,
7649
- onServerSave,
7650
- onServerLoad,
7651
- onServerDelete,
7652
7199
  buildInfo
7653
7200
  }) {
7654
7201
  const [hfNamespace, setHfNamespace] = useState26(void 0);
7655
- useEffect11(() => {
7202
+ useEffect10(() => {
7656
7203
  if (!serverBaseUrl) return;
7657
7204
  fetch(`${serverBaseUrl}/api/status`).then((r) => r.json()).then((d) => {
7658
7205
  if (typeof d.hfNamespace === "string") setHfNamespace(d.hfNamespace);
@@ -7675,10 +7222,6 @@ function FaApp({
7675
7222
  allowDevNamespace: !hfNamespace && allowDevNamespace,
7676
7223
  requireWorkspaceSelection: true,
7677
7224
  serverBaseUrl,
7678
- onFetchServerProjects,
7679
- onServerSave,
7680
- onServerLoad,
7681
- onServerDelete,
7682
7225
  buildInfo
7683
7226
  }
7684
7227
  );
@@ -8252,6 +7795,7 @@ export {
8252
7795
  FaToolsBadge,
8253
7796
  FlowWorkspacePicker,
8254
7797
  GLOBAL_STYLES,
7798
+ HfMediaSyncTab,
8255
7799
  HistoryPanel,
8256
7800
  InspectPanel,
8257
7801
  LIBRARY_CUTOVER_MARKER,
@@ -8268,7 +7812,6 @@ export {
8268
7812
  ListView,
8269
7813
  MediaLibrary,
8270
7814
  PillButton,
8271
- ProjectSyncTab,
8272
7815
  PromptTab,
8273
7816
  SCOPE_MAP,
8274
7817
  SectionLabel,
@@ -8302,7 +7845,6 @@ export {
8302
7845
  createTagsDefault,
8303
7846
  deriveTagBaseWorkspaceTags,
8304
7847
  deriveTagViewModel,
8305
- exportProjectToZip,
8306
7848
  formatTreeToMarkdown,
8307
7849
  frameToGeneration,
8308
7850
  getFormattedTimestamp,
@@ -8311,15 +7853,10 @@ export {
8311
7853
  groupGenerationsToLabItems,
8312
7854
  hfBatchArchive,
8313
7855
  hfBootstrapFromLegacy,
8314
- hfDeleteProject,
8315
- hfDownloadProject,
8316
7856
  hfListDir,
8317
- hfListProjects,
8318
7857
  hfLoadImageAsBase64,
8319
7858
  hfUploadImage,
8320
- hfUploadProjectForm,
8321
7859
  hfUploadSmallFile,
8322
- importProjectFromZip,
8323
7860
  injectXMPMetadata,
8324
7861
  interpretSdkError,
8325
7862
  libraryContract,