@rslsp1/fa-app-tools 2.0.85 → 2.0.86

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.
@@ -6468,8 +6468,8 @@ var BestandsBild = ({ port, mediaRef }) => {
6468
6468
  };
6469
6469
  var BatchBild = ({ port, bild }) => {
6470
6470
  const [phase] = usePortQuery(
6471
- () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref) : Promise.resolve({ kind: "ok", value: null }),
6472
- [port, bild.media_ref]
6471
+ () => port.imageLibrary ? port.imageLibrary.resolveImage(bild.media_ref, bild.mime_type ?? null) : Promise.resolve({ kind: "ok", value: null }),
6472
+ [port, bild.media_ref, bild.mime_type]
6473
6473
  );
6474
6474
  return /* @__PURE__ */ jsxs5("li", { "data-testid": `batchbild-${bild.media_ref}`, children: [
6475
6475
  phase.status === "ready" && phase.value && /* @__PURE__ */ jsx5(
@@ -11067,6 +11067,179 @@ function createClientRunPort(options) {
11067
11067
  };
11068
11068
  }
11069
11069
 
11070
+ // src/lib/hfReducer.ts
11071
+ function applyEvent(state, event) {
11072
+ if (event.v.major > 1) return state;
11073
+ switch (event.type) {
11074
+ // SPEZ: image_added (state/spez/hf-state.spez.yaml)
11075
+ case "image_added": {
11076
+ const p = event.payload;
11077
+ if (state.metadata.some((m) => m.id === p.id)) return state;
11078
+ return { ...state, metadata: [...state.metadata, p] };
11079
+ }
11080
+ // SPEZ: task_requested (global/spez/task-queue.spez.yaml)
11081
+ case "task_requested": {
11082
+ const p = event.payload;
11083
+ if (p.expiresAt && p.expiresAt < event.ts) return state;
11084
+ const openTasks = { ...state.openTasks || {} };
11085
+ openTasks[p.taskId] = p;
11086
+ return { ...state, openTasks };
11087
+ }
11088
+ // SPEZ: task_completed (global/spez/task-queue.spez.yaml)
11089
+ case "task_completed": {
11090
+ const p = event.payload;
11091
+ if (!state.openTasks || !state.openTasks[p.taskId]) return state;
11092
+ const openTasks = { ...state.openTasks };
11093
+ delete openTasks[p.taskId];
11094
+ return { ...state, openTasks };
11095
+ }
11096
+ // SPEZ: tag_upserted (state/spez/hf-state.spez.yaml) — Legacy, wird nicht mehr erzeugt
11097
+ case "tag_upserted": {
11098
+ const p = event.payload;
11099
+ const tags = state.tags;
11100
+ const cat = tags.by_category[p.category] || [];
11101
+ const existing = cat.find((t) => t.value === p.value);
11102
+ const updated = { label: p.label, value: p.value, is_user_created: p.is_user_created, is_deleted: p.is_deleted };
11103
+ const newCat = existing ? cat.map((t) => t.value === p.value ? { ...t, ...updated } : t) : [...cat, updated];
11104
+ 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 }];
11105
+ return { ...state, tags: { by_category: { ...tags.by_category, [p.category]: newCat }, all: newAll } };
11106
+ }
11107
+ // SPEZ: thumb_added (state/spez/hf-state.spez.yaml)
11108
+ case "thumb_added": {
11109
+ const p = event.payload;
11110
+ return {
11111
+ ...state,
11112
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, hasThumb: true } : m)
11113
+ };
11114
+ }
11115
+ // SPEZ: metadata_updated (state/spez/hf-state.spez.yaml)
11116
+ case "metadata_updated": {
11117
+ const p = event.payload;
11118
+ return {
11119
+ ...state,
11120
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, ...p.delta } : m)
11121
+ };
11122
+ }
11123
+ // SPEZ: title_set (state/spez/hf-state.spez.yaml)
11124
+ case "title_set": {
11125
+ const p = event.payload;
11126
+ return {
11127
+ ...state,
11128
+ metadata: state.metadata.map((m) => m.id === p.id ? { ...m, titleTs: event.ts } : m)
11129
+ };
11130
+ }
11131
+ default:
11132
+ return state;
11133
+ }
11134
+ }
11135
+ function applyEvents(state, events) {
11136
+ return events.reduce(applyEvent, state);
11137
+ }
11138
+
11139
+ // src/lib/szenen/hfImageStore.ts
11140
+ var LEERER_BESTAND = {
11141
+ metadata: [],
11142
+ tags: { by_category: {}, all: [] },
11143
+ meta: { consolidatedAt: 0, version: 1 }
11144
+ };
11145
+ function endungFuer(mimeType) {
11146
+ return mimeType === "image/png" ? "png" : "jpg";
11147
+ }
11148
+ function bildPfad(id, mimeType) {
11149
+ return `images/${id}.${endungFuer(mimeType)}`;
11150
+ }
11151
+ function typAusBytes(base64, vorgabe = "image/png") {
11152
+ const kopf = base64.slice(0, 16);
11153
+ if (kopf.startsWith("iVBORw0KGgo")) return "image/png";
11154
+ if (kopf.startsWith("/9j/")) return "image/jpeg";
11155
+ if (kopf.startsWith("UklGR")) return "image/webp";
11156
+ if (kopf.startsWith("R0lGOD")) return "image/gif";
11157
+ return vorgabe;
11158
+ }
11159
+ var warten = (ms) => new Promise((r) => setTimeout(r, ms));
11160
+ function createHfImageStore(options) {
11161
+ const { namespace, token } = options;
11162
+ const now = options.now ?? (() => Date.now());
11163
+ const versuche = options.leseVersuche ?? 4;
11164
+ const basisMs = options.leseBasisMs ?? 400;
11165
+ const schlafen = options.schlafen ?? warten;
11166
+ const abgelegt = /* @__PURE__ */ new Map();
11167
+ const bekanntePfade = /* @__PURE__ */ new Set();
11168
+ let gefalteterBestand = null;
11169
+ let laufenderAbgleich = null;
11170
+ async function bestandLesen() {
11171
+ const grundlage = gefalteterBestand ?? await loadHFState(namespace, token) ?? LEERER_BESTAND;
11172
+ const neue = await loadPendingEvents(
11173
+ namespace,
11174
+ token,
11175
+ grundlage.meta.consolidatedAt,
11176
+ bekanntePfade
11177
+ );
11178
+ const danach = neue.filter((e) => e.ts > grundlage.meta.consolidatedAt);
11179
+ gefalteterBestand = danach.length ? applyEvents(grundlage, danach) : grundlage;
11180
+ return gefalteterBestand;
11181
+ }
11182
+ function bestandAbgleichen() {
11183
+ if (!laufenderAbgleich) {
11184
+ laufenderAbgleich = bestandLesen().finally(() => {
11185
+ laufenderAbgleich = null;
11186
+ });
11187
+ }
11188
+ return laufenderAbgleich;
11189
+ }
11190
+ return {
11191
+ bestand: bestandAbgleichen,
11192
+ async putWithId(id, base64, mimeType, titel) {
11193
+ const echterTyp = typAusBytes(base64, mimeType);
11194
+ await hfUploadImage(base64, id, token, echterTyp);
11195
+ await writeHFEvent(namespace, token, "image_added", {
11196
+ id,
11197
+ prompt: titel ?? id,
11198
+ tags: [],
11199
+ timestamp: now(),
11200
+ mimeType: echterTyp
11201
+ }, []);
11202
+ abgelegt.set(id, { data: base64, mimeType: echterTyp });
11203
+ },
11204
+ async resolve(id, mimeType) {
11205
+ const eben = abgelegt.get(id);
11206
+ if (eben) return eben;
11207
+ const zustand = await bestandAbgleichen();
11208
+ const eintrag = zustand.metadata.find((e) => e.id === id);
11209
+ const typ = eintrag?.mimeType || mimeType || null;
11210
+ if (eintrag) {
11211
+ const base64 = await hfLoadImageAsBase64(
11212
+ id,
11213
+ token,
11214
+ void 0,
11215
+ eintrag.filename,
11216
+ void 0,
11217
+ eintrag.mimeType,
11218
+ false
11219
+ );
11220
+ if (base64) return { data: base64, mimeType: eintrag.mimeType || "image/jpeg" };
11221
+ }
11222
+ const pfadTyp = typ ?? "image/png";
11223
+ for (let versuch = 0; versuch < versuche; versuch += 1) {
11224
+ if (versuch > 0) await schlafen(basisMs * 2 ** (versuch - 1));
11225
+ const base64 = await hfLoadImageAsBase64(
11226
+ id,
11227
+ token,
11228
+ void 0,
11229
+ void 0,
11230
+ void 0,
11231
+ pfadTyp,
11232
+ false
11233
+ );
11234
+ if (base64) return { data: base64, mimeType: pfadTyp };
11235
+ }
11236
+ throw new Error(
11237
+ `Das Bild ${id} ist auf HF nicht auffindbar. Geprueft: ${bildPfad(id, pfadTyp)} in ${versuche} Versuchen` + (eintrag ? " (im Bestand verzeichnet, aber nicht ladbar)." : " (nicht im Bestand verzeichnet).")
11238
+ );
11239
+ }
11240
+ };
11241
+ }
11242
+
11070
11243
  // src/lib/szenen/hfImageLibrary.ts
11071
11244
  function fehler2(nachricht, retryable = false) {
11072
11245
  return { kind: "error", message: nachricht, retryable };
@@ -11082,14 +11255,16 @@ function alsBild(eintrag) {
11082
11255
  };
11083
11256
  }
11084
11257
  function createHfImageLibrary(options) {
11085
- const { namespace, token } = options;
11086
11258
  const newImageId = options.newImageId ?? (() => crypto.randomUUID());
11087
- const now = options.now ?? (() => Date.now());
11088
- const frisch = /* @__PURE__ */ new Map();
11259
+ const store = options.store ?? createHfImageStore({
11260
+ namespace: options.namespace,
11261
+ token: options.token,
11262
+ ...options.now ? { now: options.now } : {}
11263
+ });
11089
11264
  return {
11090
11265
  async listImages(query) {
11091
11266
  try {
11092
- const zustand = await loadHFState(namespace, token);
11267
+ const zustand = await store.bestand();
11093
11268
  const eintraege = zustand?.metadata ?? [];
11094
11269
  const gesucht = query?.search?.trim().toLowerCase();
11095
11270
  const gefiltert = gesucht ? eintraege.filter((e) => (e.prompt ?? "").toLowerCase().includes(gesucht) || e.id.toLowerCase().includes(gesucht)) : eintraege;
@@ -11103,54 +11278,18 @@ function createHfImageLibrary(options) {
11103
11278
  );
11104
11279
  }
11105
11280
  },
11106
- async resolveImage(mediaRef) {
11107
- const eben = frisch.get(mediaRef);
11108
- if (eben) return { kind: "ok", value: { data: eben.data, mime_type: eben.mimeType } };
11281
+ async resolveImage(mediaRef, mimeTypeHinweis) {
11109
11282
  try {
11110
- const zustand = await loadHFState(namespace, token);
11111
- const eintrag = (zustand?.metadata ?? []).find((e) => e.id === mediaRef);
11112
- if (eintrag) {
11113
- const base64 = await hfLoadImageAsBase64(
11114
- mediaRef,
11115
- token,
11116
- void 0,
11117
- eintrag.filename,
11118
- void 0,
11119
- eintrag.mimeType,
11120
- false
11121
- );
11122
- if (!base64) {
11123
- return fehler2(`Das Bild ${mediaRef} ist im Bestand verzeichnet, aber nicht ladbar.`, true);
11124
- }
11125
- return { kind: "ok", value: { data: base64, mime_type: eintrag.mimeType || "image/jpeg" } };
11126
- }
11127
- for (const mimeType of ["image/png", "image/jpeg"]) {
11128
- const base64 = await hfLoadImageAsBase64(mediaRef, token, void 0, void 0, void 0, mimeType, false);
11129
- if (base64) return { kind: "ok", value: { data: base64, mime_type: mimeType } };
11130
- }
11131
- return fehler2(
11132
- `Das Bild ${mediaRef} steht nicht im Bestand. Moeglicherweise hat der Abgleich es noch nicht uebertragen.`,
11133
- true
11134
- );
11283
+ const bild = await store.resolve(mediaRef, mimeTypeHinweis);
11284
+ return { kind: "ok", value: { data: bild.data, mime_type: bild.mimeType } };
11135
11285
  } catch (error) {
11136
- return fehler2(
11137
- `Das Bild ${mediaRef} konnte nicht geladen werden: ${error instanceof Error ? error.message : String(error)}`,
11138
- true
11139
- );
11286
+ return fehler2(error instanceof Error ? error.message : String(error), true);
11140
11287
  }
11141
11288
  },
11142
11289
  async putImage(input) {
11143
11290
  const id = newImageId();
11144
11291
  try {
11145
- await hfUploadImage(input.data, id, token, input.mime_type);
11146
- await writeHFEvent(namespace, token, "image_added", {
11147
- id,
11148
- prompt: input.title,
11149
- tags: [],
11150
- timestamp: now(),
11151
- mimeType: input.mime_type
11152
- }, []);
11153
- frisch.set(id, { mimeType: input.mime_type, data: input.data });
11292
+ await store.putWithId(id, input.data, input.mime_type, input.title);
11154
11293
  return {
11155
11294
  kind: "ok",
11156
11295
  value: { mediaRef: id, title: input.title, mimeType: input.mime_type, thumbUrl: null }
@@ -11280,6 +11419,11 @@ function alsBilder(rohe) {
11280
11419
  media_ref: b.media_ref,
11281
11420
  position: b.position,
11282
11421
  ...istText4(b.asset_id) ? { asset_id: b.asset_id } : {},
11422
+ // Der bestätigte MIME-Typ (Review 2026-08-18 §5 P2) muss durch das
11423
+ // Rücklesen hindurch: Genau hier braucht ihn der Lesepfad, um den
11424
+ // Dateipfad `images/<id>.<ext>` zu bilden, statt ihn zu erraten. Nur
11425
+ // gesetzt, wenn er im Ereignis steht — ältere Batches tragen ihn nicht.
11426
+ ...istText4(b.mime_type) ? { mime_type: b.mime_type } : {},
11283
11427
  model: b.model ?? null,
11284
11428
  seed: b.seed ?? null,
11285
11429
  width: b.width ?? null,
@@ -11368,6 +11512,7 @@ async function legeBatchAb(options, input) {
11368
11512
  const { namespace, token, channel } = options;
11369
11513
  const newImageId = options.newImageId ?? ((batchId, position) => `${batchId}-${position}`);
11370
11514
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
11515
+ const store = options.store ?? createHfImageStore({ namespace, token });
11371
11516
  const itemId = itemIdForRun(input.runId);
11372
11517
  let vorhandeneEvents;
11373
11518
  try {
@@ -11386,15 +11531,14 @@ async function legeBatchAb(options, input) {
11386
11531
  for (let offset = 0; offset < input.images.length; offset++) {
11387
11532
  const bild = input.images[offset];
11388
11533
  const id = newImageId(input.generationBatchId, startPosition + offset);
11534
+ const echterTyp = typAusBytes(bild.imageBase64, bild.mimeType);
11389
11535
  try {
11390
- await hfUploadImage(bild.imageBase64, id, token, bild.mimeType);
11391
- await writeHFEvent(namespace, token, "image_added", {
11536
+ await store.putWithId(
11392
11537
  id,
11393
- prompt: input.title ?? `Batch ${input.generationBatchId}`,
11394
- tags: [],
11395
- timestamp: now().getTime(),
11396
- mimeType: bild.mimeType
11397
- }, []);
11538
+ bild.imageBase64,
11539
+ echterTyp,
11540
+ input.title ?? `Batch ${input.generationBatchId}`
11541
+ );
11398
11542
  } catch (error) {
11399
11543
  return fehler3(
11400
11544
  `Bild ${offset + 1}/${input.images.length} des Batches ${input.generationBatchId} konnte nicht im Bildbestand abgelegt werden: ${error instanceof Error ? error.message : String(error)}. Bereits abgelegte Bilder dieses Versuchs (${abgelegt.length}) bleiben im Bestand, geh\xF6ren aber zu keinem Batch, solange das Batch-Ereignis nicht geschrieben ist.`
@@ -11404,6 +11548,7 @@ async function legeBatchAb(options, input) {
11404
11548
  position: startPosition + offset,
11405
11549
  media_ref: id,
11406
11550
  asset_id: id,
11551
+ mime_type: echterTyp,
11407
11552
  model: bild.model ?? null,
11408
11553
  seed: bild.seed ?? null,
11409
11554
  width: bild.width ?? null,
@@ -11447,8 +11592,11 @@ function createHfImageGenerationBatchPersistence(options) {
11447
11592
  images: input.images.map((bild) => ({
11448
11593
  imageBase64: bild.imageBase64,
11449
11594
  // Flow liefert PNG-Base64 (bestehende Konvention, s. AvatarArchitectApp.tsx
11450
- // `data:image/png;base64,${base64}`); der gemeinsame Vertrag führt
11451
- // keinen MIME-Typ je Bild.
11595
+ // `data:image/png;base64,${base64}`), und der gemeinsame Vertrag führt
11596
+ // keinen MIME-Typ je Bild. Das ist deshalb nur die **Vorgabe**:
11597
+ // Entschieden wird nach den echten Bytes (Review §5 P2) — ein künftig
11598
+ // anders kodiertes Bild läge sonst unter einer Endung, unter der es
11599
+ // niemand sucht.
11452
11600
  mimeType: "image/png",
11453
11601
  model: bild.model ?? null,
11454
11602
  seed: bild.seed ?? null,
@@ -11469,6 +11617,8 @@ function createHfImageBatchWrite(options) {
11469
11617
  title: input.title,
11470
11618
  images: input.images.map((bild) => ({
11471
11619
  imageBase64: bild.imageBase64,
11620
+ // Vorgabe, kein Urteil — der Import bringt beliebig kodierte Bilder
11621
+ // mit, und der Typ wird aus den Bytes bestimmt (Review §5 P2).
11472
11622
  mimeType: "image/png",
11473
11623
  model: bild.model ?? null,
11474
11624
  seed: bild.seed ?? null,
@@ -11757,7 +11907,17 @@ function createFlowSzenenViewPort(options) {
11757
11907
  ...options.newRunId ? { newRunId: options.newRunId } : {},
11758
11908
  ...options.now ? { now: options.now } : {}
11759
11909
  });
11760
- const imageLibrary = options.imageLibrary ?? (options.namespace?.trim() && options.token?.trim() ? createHfImageLibrary({ namespace: options.namespace, token: options.token }) : void 0);
11910
+ const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11911
+ const bildSpeicher = hatHfZugangsdaten ? createHfImageStore({
11912
+ namespace: options.namespace,
11913
+ token: options.token,
11914
+ ...options.now ? { now: () => options.now().getTime() } : {}
11915
+ }) : void 0;
11916
+ const imageLibrary = options.imageLibrary ?? (bildSpeicher ? createHfImageLibrary({
11917
+ namespace: options.namespace,
11918
+ token: options.token,
11919
+ store: bildSpeicher
11920
+ }) : void 0);
11761
11921
  const derivation = createClientDerivationPort({
11762
11922
  store: elements,
11763
11923
  derivations: createEventLogDerivationPersistence(channel),
@@ -11767,11 +11927,11 @@ function createFlowSzenenViewPort(options) {
11767
11927
  ...options.now ? { now: options.now } : {}
11768
11928
  });
11769
11929
  const imageBatchHistory = createHfImageBatchHistory({ channel });
11770
- const hatHfZugangsdaten = Boolean(options.namespace?.trim() && options.token?.trim());
11771
- const imageBatchWrite = hatHfZugangsdaten ? createHfImageBatchWrite({
11930
+ const imageBatchWrite = bildSpeicher ? createHfImageBatchWrite({
11772
11931
  namespace: options.namespace,
11773
11932
  token: options.token,
11774
11933
  channel,
11934
+ store: bildSpeicher,
11775
11935
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11776
11936
  ...options.now ? { now: options.now } : {}
11777
11937
  }) : void 0;
@@ -11785,6 +11945,9 @@ function createFlowSzenenViewPort(options) {
11785
11945
  namespace: options.namespace,
11786
11946
  token: options.token,
11787
11947
  channel,
11948
+ // Dieselbe Instanz wie der Bildbestand — sonst wäre das gerade
11949
+ // erzeugte Bild in der Laufansicht wieder unauffindbar (R1).
11950
+ store: bildSpeicher,
11788
11951
  ...options.newBatchImageId ? { newImageId: options.newBatchImageId } : {},
11789
11952
  ...options.now ? { now: options.now } : {}
11790
11953
  }),
@@ -11800,6 +11963,7 @@ function createFlowSzenenViewPort(options) {
11800
11963
  }
11801
11964
 
11802
11965
  export {
11966
+ applyEvents,
11803
11967
  capabilitiesOf,
11804
11968
  unsupportedReason,
11805
11969
  callOptional,
@@ -11914,6 +12078,9 @@ export {
11914
12078
  runPromptDerivation,
11915
12079
  createEventLogRunPersistence,
11916
12080
  createClientRunPort,
12081
+ bildPfad,
12082
+ typAusBytes,
12083
+ createHfImageStore,
11917
12084
  createHfImageLibrary,
11918
12085
  FLOW_MODEL_DISPLAY_NAMES,
11919
12086
  flowImageProviderHasAccess,
@@ -0,0 +1,241 @@
1
+ import { T as TextGenerationPort } from './contracts-DdYOMBl9.js';
2
+
3
+ interface TagOption {
4
+ label: string;
5
+ value: string;
6
+ is_user_created?: boolean;
7
+ is_deleted?: boolean;
8
+ }
9
+ interface WorkspaceTags {
10
+ by_category: Record<string, TagOption[]>;
11
+ all: Array<TagOption & {
12
+ category: string;
13
+ }>;
14
+ }
15
+ interface Generation {
16
+ id: string;
17
+ nodeId: string;
18
+ base64?: string;
19
+ fullBase64?: string;
20
+ mediaId?: string;
21
+ prompt?: string;
22
+ hierarchy?: string;
23
+ seed?: number;
24
+ model?: string;
25
+ timestamp: number;
26
+ status: 'processing' | 'done' | 'error';
27
+ step?: string;
28
+ tags: string[];
29
+ type?: 'generation' | 'import';
30
+ error?: {
31
+ message: string;
32
+ details?: string;
33
+ };
34
+ selectedForExport?: boolean;
35
+ filename?: string;
36
+ hasThumb?: boolean;
37
+ mimeType?: string;
38
+ titleTs?: number;
39
+ }
40
+ interface ProjectSettings {
41
+ aspectRatio: string;
42
+ selectedModel: string;
43
+ seed: number;
44
+ seedMode: 'random' | 'fixed';
45
+ }
46
+ interface ProjectMeta {
47
+ id: string;
48
+ name: string;
49
+ saved_at: string;
50
+ size: number;
51
+ }
52
+ interface SyncDiff {
53
+ localOnly: Generation[];
54
+ serverOnly: Generation[];
55
+ }
56
+ interface LabFrame {
57
+ id: string;
58
+ base64: string;
59
+ mediaId?: string;
60
+ seed?: number;
61
+ model?: string;
62
+ aspectRatio?: string;
63
+ width?: number;
64
+ height?: number;
65
+ source?: 'generated' | 'flow-gallery' | 'zip-import' | 'db-library';
66
+ }
67
+ interface LabItem {
68
+ id: string;
69
+ prompt: string;
70
+ label?: string;
71
+ tags?: string[];
72
+ frames: LabFrame[];
73
+ }
74
+ interface SelectedLabImage {
75
+ item: LabItem;
76
+ frame: LabFrame;
77
+ label: string;
78
+ roleForPrompt?: string;
79
+ roleForImage?: string;
80
+ sendToPromptAI: boolean;
81
+ sendToImageGen: boolean;
82
+ }
83
+ interface LabServices {
84
+ availableItems: LabItem[];
85
+ recentItems: LabItem[];
86
+ onItemUsed: (item: LabItem) => void;
87
+ generateText: (text: string, options?: {
88
+ images?: {
89
+ base64: string;
90
+ mimeType: string;
91
+ }[];
92
+ systemInstruction?: string;
93
+ }) => Promise<string>;
94
+ generateImage: (options: Record<string, any>) => Promise<{
95
+ base64: string;
96
+ mediaId?: string;
97
+ }>;
98
+ saveResult?: (item: LabItem) => Promise<void>;
99
+ workspaceTags?: WorkspaceTags;
100
+ }
101
+ interface HFMetadataEntry {
102
+ id: string;
103
+ prompt?: string;
104
+ seed?: number;
105
+ model?: string;
106
+ tags: string[];
107
+ timestamp: number;
108
+ mimeType: string;
109
+ size?: number;
110
+ filename?: string;
111
+ hasThumb?: boolean;
112
+ titleTs?: number;
113
+ }
114
+
115
+ declare function buildGenerationPrompt(hierarchyText: string, mode?: 'literal' | 'creative'): string;
116
+ declare function buildFallbackPrompt(hierarchyText: string): string;
117
+ declare function cleanAiResponse(text: string): string;
118
+ declare function buildImageGenerationOptions(prompt: string, aspectRatio: string, model: string, referenceMediaId?: string): Record<string, any>;
119
+ interface SelectedTag {
120
+ label: string;
121
+ value: string;
122
+ category: string;
123
+ }
124
+ declare function buildPromptTabPayload(selectedTags: SelectedTag[], instructions: string, rules: string, treeText?: string): string;
125
+ declare function parsePromptResponse(raw: string): {
126
+ prompt: string;
127
+ feedback: string | null;
128
+ };
129
+ interface FlowSdkImage {
130
+ base64: string;
131
+ mimeType: string;
132
+ }
133
+ interface FlowTextGenerationOptions {
134
+ images?: FlowSdkImage[];
135
+ systemInstruction?: string;
136
+ responseFormat?: 'text' | 'json';
137
+ thinkingLevel?: 'low' | 'medium' | 'high';
138
+ }
139
+ interface FlowSdk {
140
+ generate: {
141
+ image: (options: Record<string, any>) => Promise<{
142
+ base64: string;
143
+ mediaId: string;
144
+ }>;
145
+ text: (prompt: string, options?: {
146
+ systemInstruction?: string;
147
+ thinkingLevel?: string;
148
+ images?: FlowSdkImage[];
149
+ }) => Promise<{
150
+ text: string;
151
+ }>;
152
+ };
153
+ download: (args: {
154
+ base64: string;
155
+ mimeType: string;
156
+ filename: string;
157
+ }) => Promise<void>;
158
+ media: {
159
+ selectMultiple: (args: {
160
+ filter: string;
161
+ }) => Promise<any[]>;
162
+ };
163
+ }
164
+ declare function createFlowServices(Flow: FlowSdk): {
165
+ generateImage: (options: Record<string, any>) => Promise<{
166
+ base64: string;
167
+ mediaId: string;
168
+ }>;
169
+ generateText: (text: string, options?: FlowTextGenerationOptions) => Promise<string>;
170
+ };
171
+ declare function interpretSdkError(err: any): Error;
172
+
173
+ type FlowGenerateText = (prompt: string, options?: FlowTextGenerationOptions) => Promise<string>;
174
+ interface CreateFlowTextGenerationPortOptions {
175
+ provider?: string;
176
+ modelId?: string;
177
+ }
178
+ /**
179
+ * Adapts the existing Flow SDK service to the host-neutral Szenen contract.
180
+ * It deliberately does not parse or validate the response: that happens once
181
+ * in `runPromptDerivation`, identically for every host.
182
+ */
183
+ declare function createFlowTextGenerationPort(generateText: FlowGenerateText, options?: CreateFlowTextGenerationPortOptions): TextGenerationPort;
184
+
185
+ interface HFEventVersion {
186
+ major: number;
187
+ minor: number;
188
+ }
189
+ interface HFEvent<P = unknown> {
190
+ v: HFEventVersion;
191
+ type: string;
192
+ ts: number;
193
+ prevTs: number[];
194
+ clientId: string;
195
+ payload: P;
196
+ }
197
+ interface HFStateMeta {
198
+ consolidatedAt: number;
199
+ version: number;
200
+ }
201
+ interface HFStateSnapshot {
202
+ metadata: HFMetadataEntry[];
203
+ tags: WorkspaceTags;
204
+ meta: HFStateMeta;
205
+ openTasks?: Record<string, TaskRequestedPayload>;
206
+ }
207
+ interface HFFileInfo {
208
+ type: 'file' | 'directory';
209
+ path: string;
210
+ size: number;
211
+ }
212
+ interface ImageAddedPayload extends HFMetadataEntry {
213
+ taskId?: string;
214
+ }
215
+ interface TagUpsertedPayload {
216
+ category: string;
217
+ label: string;
218
+ value: string;
219
+ is_user_created?: boolean;
220
+ is_deleted?: boolean;
221
+ }
222
+ interface MetadataUpdatedPayload {
223
+ id: string;
224
+ delta: Partial<HFMetadataEntry>;
225
+ }
226
+ interface TaskRequestedPayload {
227
+ taskId: string;
228
+ taskType: string;
229
+ params: {
230
+ prompt: string;
231
+ tags?: string[];
232
+ book?: string;
233
+ kapitel?: string;
234
+ ankerId?: string;
235
+ promptVersion?: number;
236
+ [key: string]: any;
237
+ };
238
+ expiresAt?: number;
239
+ }
240
+
241
+ export { type CreateFlowTextGenerationPortOptions as C, type FlowGenerateText as F, type Generation as G, type HFEvent as H, type ImageAddedPayload as I, type LabFrame as L, type MetadataUpdatedPayload as M, type ProjectMeta as P, type SelectedLabImage as S, type TagOption as T, type WorkspaceTags as W, type FlowSdk as a, type FlowTextGenerationOptions as b, type HFEventVersion as c, type HFFileInfo as d, type HFMetadataEntry as e, type HFStateMeta as f, type HFStateSnapshot as g, type LabItem as h, type LabServices as i, type ProjectSettings as j, type SelectedTag as k, type SyncDiff as l, type TagUpsertedPayload as m, buildFallbackPrompt as n, buildGenerationPrompt as o, buildImageGenerationOptions as p, buildPromptTabPayload as q, cleanAiResponse as r, createFlowServices as s, createFlowTextGenerationPort as t, interpretSdkError as u, parsePromptResponse as v };