File size: 2,126 Bytes
2151ea1
 
 
de78f87
db6b273
2151ea1
f77d660
8e1c8c5
2151ea1
 
 
 
 
 
de78f87
2151ea1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// Image facade — mirrors tts.js. Picks the active portrait engine (local Z-Image on your
// GPU, or cloud FLUX; in-browser SD-Turbo / Janus get added here later) and exposes one
// generatePortrait(). The persona panel + the Settings image bar import only from here.
import { engineLocal as zimagelocal, engineKleinZeroGpu as klein, engineCloud as flux, engineCloudDev as fluxdev } from '/web/imagenServer.js'
import { engine as bonsai } from '/web/imagenBonsai.js'

const ENGINES = [zimagelocal, klein, bonsai, flux, fluxdev]
// Default: local Z-Image on localhost (your GPU), Klein ZeroGPU in prod. Persisted across
// refreshes; a saved choice wins if it's still available.
const KEY = 'tinyarmy.imageEngine'
let activeId = (() => {
  let saved = ''
  try { saved = localStorage.getItem(KEY) || '' } catch { /* ignore */ }
  const e = ENGINES.find((x) => x.id === saved)
  return e && e.available() ? saved : 'klein-zerogpu'
})()

const eng = () => ENGINES.find((e) => e.id === activeId) || ENGINES.find((e) => e.available()) || ENGINES[0]

export const listImageEngines = () =>
  ENGINES.map((e) => ({ id: e.id, label: e.label, available: e.available(), note: e.note || '' }))
export const getImageEngineId = () => activeId

const _listeners = new Set()
export function onImageEngineChange(fn) { _listeners.add(fn); return () => _listeners.delete(fn) }
export function setImageEngine(id) {
  if (!ENGINES.some((e) => e.id === id) || id === activeId) return
  activeId = id
  try { localStorage.setItem(KEY, id) } catch { /* ignore */ }
  for (const fn of _listeners) { try { fn(id) } catch { /* ignore */ } }
}

export const imageNeedsDownload = () => !!eng().needsDownload
export const imageBackendLabel = () => eng().backendLabel()
export const imageNetworked = () => !!eng().networked

export async function ensureImage(onProgress) { return eng().ensure(onProgress) }

// Generate a portrait → PNG Blob. `prompt` is the appearance description; `seed` keeps it
// reproducible where the engine supports it.
export async function generatePortrait(prompt, { seed } = {}) {
  return eng().generate(prompt, { seed })
}