Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
# Changelog

## 1.2.1 — 2026-10-23

Tre correzioni dell'interfaccia Orbita e una semplificazione: i modelli si scelgono dalla barra in alto e sono due (Qwen e Klein 3), l'Editor box non si blocca più dopo qualche riquadro, e i LoRA hanno due scaffali, Qwen e Flux, che ora funzionano anche con Klein.

### Modelli

- **La scelta del modello sta nella barra in alto** dell'app (quella bianca, con RAM e VRAM), visibile da qualsiasi pagina: **Qwen**, **Klein 3** (8 passi) e **Klein 3 Base** (50 passi). Spariscono le tessere «Motore» dai pannelli di Crea, Modifica e Box. Se i file di Klein non ci sono, la pillola ha il puntino e il pannellino per scaricarli.
- **FLUX.2 Klein 4B è tolto**: via dal selettore, dal catalogo (modello e text encoder), dai grafi, dal riconoscimento dei LoRA. Le impostazioni salvate con Klein 4B passano da sole a Qwen; una foto fatta con lui si apre come Qwen. I suoi file già scaricati compaiono in Opzioni → File inutilizzati («tolto dal catalogo») e non vengono più scambiati per quelli di Klein 3.

### Editor box

- **Più di tre riquadri, e riquadri dentro i riquadri.** Un riquadro grande si prendeva tutto il mouse, quindi non si poteva più disegnare niente sopra; col terzo o quarto riquadro la tela era già piena. Ora lo strumento **Disegna** (di serie) fa nascere un riquadro nuovo ovunque, anche dentro un altro; il **numero** del riquadro lo sposta, le maniglie lo ridimensionano. Lo strumento **Sposta** afferra il riquadro dal corpo come prima. Un clic su un punto sceglie il riquadro più piccolo che lo contiene. Il tetto passa da 8 a **16**, come nel motore.

### LoRA

- **Due scaffali: Qwen e Flux (Klein 3).** La pagina LoRA ha una scheda per scaffale, ognuna con la sua zona per trascinare i file o incollare un link, la sua pila (ordine e forza) e le sue carte. I file vanno in `loras/qwen` e `loras/flux`; i nomi dei tensori decidono lo scaffale anche se carichi dalla scheda sbagliata (l'app lo dice). I LoRA che non si riconoscono restano nello scaffale della scheda da cui li hai caricati; quelli fatti per altri modelli (SD, SDXL, FLUX.1, FLUX.2) stanno in «Altri», dove si può scegliere a mano per quale modello usarli.
- **Perché i LoRA non funzionavano con Klein:** l'elenco dei LoRA non mandava all'interfaccia per quali modelli erano fatti, e ogni LoRA di Klein risultava «non compatibile» con Klein; inoltre la richiesta con Klein non portava nessun LoRA e le azioni rapide accendevano LoRA di Qwen. Ora la richiesta porta la pila del modello scelto; Klein veloce e Klein Base usano la stessa. Cambiando modello la pila dell'altro scaffale resta lì, ma non parte.
- Le azioni rapide, Camera e gli avvisi «LoRA consigliato» accendono solo LoRA che vanno col modello scelto (sono LoRA di Qwen: con Klein si va col modello da solo). Una rete di sicurezza nel motore toglie dalla richiesta i LoRA dell'altro scaffale.
- Non ancora provato con la GPU: il risultato di un LoRA vero su Klein (il motore li carica come per Qwen, ComfyUI ha la mappatura dei nomi di Klein).

### Prove

- Nuovi: `test/lora-scaffali.mjs` (importazione nei due scaffali con file safetensors finti, famiglie nell'elenco, note vecchie), `test/box-lora-ui.mjs` (barra dei modelli, sei riquadri e uno dentro l'altro, schede dei LoRA, pannellino di download). Aggiornati i test dei modelli, del catalogo, dello spazio, dei LoRA e dei grafi.

## 1.2.0 — 2026-10-23

L'app cambia faccia e il motore impara cose nuove: l'**interfaccia Orbita** con il pannello dei controlli a sinistra, un quarto modello veloce (FLUX.2 Klein 4B), più LoRA insieme, l'editor a box, «senza sfondo» ed Estrai con SAM 2. Il motore passa alla versione 9: al primo avvio l'app lo aggiorna da sola (i modelli già scaricati restano dove sono).
Expand Down
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ interfaccia**, installato e guidato dall'app.
`motore/klein.ts` (`fileKlein`) trova i file (impostazioni → `risolti.json` → scansione cartelle, int8 prima di bf16) o dà un errore in italiano.
- Grafo (`grafi.ts`, `condiziona`/`latenteVuota`/`campiona`): `UNETLoader` + `CLIPLoader` tipo `flux3` + `VAELoader`, `CLIPTextEncode`, foto di riferimento
`VAEEncode` → `ReferenceLatent` (positivo e negativo, in catena), `EmptyFlux2LatentImage`, KSampler euler/simple (distillato cfg 1, base cfg 3,5).
Niente LoRA, Turbo, cache KV; i tag `<imageN>` diventano ordinali. **I grafi Qwen non devono cambiare**: si controlla con un confronto sul grafo vecchio prima di ogni modifica.
Niente Turbo né cache KV (i LoRA invece sì: `applicaLora` vale per tutte le famiglie); i tag `<imageN>` diventano ordinali. **I grafi Qwen non devono cambiare**: si controlla con un confronto sul grafo vecchio prima di ogni modifica.
- **Testo con Klein**: ogni lavoro `descrivi` (migliora prompt, Foto→prompt, Idee, Estrai, Fusion/Esempio, Sorpresa, controllo omino, traduzione LLM) con famiglia Klein scrive col Qwen3-VL 4B (`conEncoderDiTesto` in `lavori.ts`, `encoderKleinFile` in `klein.ts`; `Richiesta.encoderKlein: false` per escluderlo): caricato come `krea2` e con `thinking: true` (con `false` il nodo mette un `<think></think>` vuoto e il 4B risponde «user»/vuoto). Con Qwen resta l'encoder di Qwen.
- `motore/pup.ts` = i prompt upsampler ufficiali; `lavori.ts` `potenzia()` li manda al Qwen3-VL prima del disegno (`impostazioni.potenziaKlein`, cache per prompt).
- Non provato con la GPU qui: qualità, INT8 su 6-8 GB, `ReferenceLatent` in catena (alternativa `ReferenceMultiLatent`).
- **1.2.1: i modelli sono tre pillole** (`qwen-base`, `klein`, `klein-base`: «Qwen», «Klein 3», «Klein 3 Base») e si scelgono **solo dalla barra in alto** (`App.tsx` → `SelettoreModello` in `componenti/ScegliModello.tsx`, da qualsiasi pagina; nei pannelli di Crea/Modifica/Box non c'è un secondo selettore). **FLUX.2 Klein 4B è stato tolto** (niente famiglia `klein4b`, niente voci `klein4b-*` nel catalogo): le impostazioni vecchie passano a Qwen in `modelloDaImpostazioni`, i suoi file in `models/` si vedono come «tolto dal catalogo» (`idle.ts`) e `modelli:file` non li scambia per file di Klein 3 (`eFlux2`).
- **LoRA a due scaffali** (1.2.1): `GruppoLora = 'qwen' | 'flux'` (`shared/loraBase.ts`: `gruppoDiFamiglia`, `gruppoDa`, `vaBeneCon`; Klein veloce e Klein Base hanno gli stessi). File in `loras/qwen` e `loras/flux` (`lora.ts` `scaffale()`: i nomi dei tensori decidono, se non dicono niente vale la scheda da cui si carica, SDXL & co. restano fuori in «Altri» e si assegnano a mano con `InfoLora.gruppo`). **`elencoLora()` deve portare `famiglie` e `gruppo`**: nella 1.2.0 non lo faceva e il renderer dava «non compatibile» a ogni LoRA di Klein. Lo store ha **due pile** (`loraPile`, `loraAttive` = quella del modello scelto; `setLoraPila`/`toggleLoraPer` per scaffale); `richiestaBase()` manda la pila del modello, `loraPer`/`fileLoraCamera` (azioni rapide, Camera) solo LoRA che vanno col modello, e `soloLoraDelModello` in `lavori.ts` toglie quelli dell'altro scaffale. Prove: `test/lora-scaffali.mjs`, `test/box-lora-ui.mjs`.

## I grafi (src/main/motore/grafi.ts)
- Pipeline ufficiale: `UnetLoaderGGUF` → LoRA (`LoraLoaderModelOnly`) → `QwenImage21Cache`; `CLIPLoader` tipo `qwen_image`
Expand Down Expand Up @@ -147,6 +149,7 @@ interfaccia**, installato e guidato dall'app.
il vecchio `testo` del titolo si trasforma in elemento in `vecchioInNuovo()`. `costruisci.ts` raggruppa (densità aerea/normale/fitta) e sceglie
un modello fra i primi senza ripetere; `disegna.ts` disegna su canvas (schermo e stampa: cornice, ombra, z, elementi); `esporta.ts` JPEG/PDF
(`pdf.ts`, senza librerie). Le foto grandi non restano in cache.
- **Editor box** (modo Box dello Studio, `pagine/Box.tsx` + `box.ts`): fino a 16 riquadri (`MAX_BOX`, come `regioniPronte`). Strumento **Disegna** (di serie): il corpo dei riquadri lascia passare il mouse (`.box-cornice.disegna .box-regione { pointer-events: none }`) così si disegna anche dentro uno grande; si sposta dal numero, si ridimensiona dalle maniglie, un clic sceglie il riquadro più piccolo sotto il punto (`riquadroSotto`). Strumento **Sposta**: il corpo si afferra. Non mettere controlli sopra la tela (coprono i punti di partenza del disegno): stanno nel pannello.
- **Estrai** (modo di Modifica, `componenti/Estrai.tsx` + `estrai.ts` + `riquadri.ts`): a foto caricata `trovaElementi` chiede al Qwen3-VL (`descrivi` con la foto intera)
un JSON `{bbox_2d 0–1000, label, kind: person|object}` (`leggiElementi`, tollera fence e JSON troncato, toglie i riquadri doppi). La ricerca parte **solo col pulsante** e la fa il Qwen3-VL (`descrivi`, `SISTEMA_ELEMENTI`, `maxToken 800`; niente rilevatore a parte: Florence è stato provato e tolto). Si sceglie con un clic (più clic = il riquadro più grande
che contiene il punto) o riquadrando a mano; il tipo (persona/oggetto) si corregge. `richiestaEstrai` ritaglia la cosa (+12%) in un temp e fa una **Crea** con quella come `<image1>`:
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,20 @@ Gira su una scheda NVIDIA **da 6-8 GB**, senza cloud e senza abbonamenti.
<img src="risorse/crea.png" width="860" alt="Crea: il preset Foto a sinistra, i tre modelli in alto, la Vespa a Positano">
</div>

## Novità della 1.2.1

- **I modelli si scelgono dalla barra in alto** (sempre visibile, da qualsiasi pagina): **Qwen**, **Klein 3** e **Klein 3 Base**. **FLUX.2 Klein 4B** non c'è più.
- **Editor box**: si possono fare fino a **16 riquadri** e un riquadro anche **dentro un altro** (prima uno grande si prendeva il mouse). Con **Disegna** si trascina ovunque, il numero del riquadro lo sposta e le maniglie lo ridimensionano; con **Sposta** si afferra dal corpo. Un clic su un punto sceglie il riquadro più piccolo che c'è lì sotto.
- **LoRA divisi in due scaffali**, **Qwen** e **Flux (Klein 3)**, ognuno con la sua pila: carichi i file (o un link) nella scheda giusta, e l'app li mette nello scaffale giusto anche se sbagli scheda. I LoRA di Flux ora funzionano con Klein veloce e Klein Base.

## Novità della 1.2.0: l'interfaccia Orbita

- **Un solo Studio** con quattro modi: **Crea**, **Modifica**, **Box** e **Ricette**. I controlli stanno nel **pannello a sinistra**, il pulsante CREA è fisso in fondo e la risoluzione finale si legge sempre.
- **28 stili** con anteprima, in quattro scaffali, e tessere con immagini per generi, luoghi, meteo, stagioni, costumi ed espressioni.
- **Editor a box**: un riquadro per ogni cosa, con il suo prompt (regional prompting).
- **Senza sfondo** (BiRefNet) ed **Estrai con SAM 2**: ritagli dai pixel della tua foto, senza ridisegnarla.
- **LoRA multipli** in fila, con la forza di ciascuno, su Qwen e su Klein.
- **FLUX.2 Klein 4B** (4 passi) come quarto modello: a caldo è circa il 15 % più veloce di Klein veloce a 8 passi (Crea 5,4 s contro 6,4 s sulla RTX 4060).
- ~~FLUX.2 Klein 4B~~ (tolto nella 1.2.1).
- **Modelli uncensored** scaricabili solo se accendi l'interruttore in Opzioni e accetti l'avviso; ogni voce è marcata.

## Installare
Expand Down Expand Up @@ -117,7 +123,7 @@ Nella barra in alto scegli **chi disegna**: **Qwen base** (Qwen-Image 2.1, 40 pa
Il Turbo di Qwen non c'è più: Klein veloce è il modo rapido.

Anche la scrittura dei testi (Migliora prompt, Foto → prompt, Idee, Estrai, Unisci foto, Come l'esempio) usa il **Qwen3-VL 4B** di Klein, con Klein e con Qwen: scrive a 46 token al secondo sulla RTX 4060,
contro 3,3 dell'encoder 8B int8 e 0,55 dell'8B GGUF. «Migliora prompt + Crea» con Qwen passa da 186 a 134 secondi. In Opzioni puoi tornare all'encoder di Qwen. Klein non usa i LoRA: l'app li nasconde.
contro 3,3 dell'encoder 8B int8 e 0,55 dell'8B GGUF. «Migliora prompt + Crea» con Qwen passa da 186 a 134 secondi. In Opzioni puoi tornare all'encoder di Qwen. I LoRA hanno due scaffali, uno per Qwen e uno per Klein: ognuno si vede solo col suo modello.

## Estrai

Expand All @@ -142,8 +148,7 @@ Klein ha i suoi file (versioni int8 o bf16, più l'encoder Qwen3-VL 4B e il VAE
**Riusa**, **Varia**, **Rifinisci 2K**, **Ingrandisci 2×/4×**. Le impostazioni stanno dentro ogni PNG: trascini
un'immagine fatta con DaProd Image e torna con prompt, seed e LoRA.
- **Ogni foto entra come PNG**: JPG, WEBP, AVIF, HEIC, TIFF, i RAW delle fotocamere, girata per il verso giusto.
- **LoRA con un clic** (in Opzioni): trascini i `.safetensors` o incolli un link Hugging Face / Civitai, l'app controlla che
siano davvero per Qwen-Image 2.1, aggiunge le parole chiave al prompt.
- **LoRA con un clic** (pagina LoRA): due schede, **Qwen** e **Flux (Klein 3)**. Trascini i `.safetensors` o incolli un link Hugging Face / Civitai, l'app guarda per quale modello sono fatti, li mette nello scaffale giusto e aggiunge le parole chiave al prompt.
- **La barra in alto** dice sempre quanta RAM e VRAM stai usando e quanto lavora la scheda video.

## Requisiti
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "daprod-image",
"productName": "DaProd Image",
"version": "1.2.0",
"version": "1.2.1",
"description": "DaProd Image: crea e modifica foto con Qwen-Image-2.1 sul tuo PC (CUDA, da 6 GB di VRAM). Turbo a 8 passi, preset per poster e infografiche, zone con anteprima dal vivo, LoRA con un clic, galleria.",
"author": "DaProd",
"license": "MIT",
Expand All @@ -14,7 +14,7 @@
"build": "electron-vite build",
"start": "electron-vite preview",
"typecheck": "tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.web.json",
"test": "node test/modelli.mjs && node test/catalogo.mjs && node test/spazio.mjs && node test/lora.mjs && node test/regioni.mjs && node test/adatta.mjs && node test/grafi_v2.mjs && node test/confronta_grafi.mjs && node test/asset.mjs",
"test": "node test/modelli.mjs && node test/catalogo.mjs && node test/spazio.mjs && node test/lora.mjs && node test/lora-scaffali.mjs && node test/regioni.mjs && node test/adatta.mjs && node test/grafi_v2.mjs && node test/confronta_grafi.mjs && node test/asset.mjs",
"prendi-uv": "node scripts/prendi-uv.mjs",
"dist": "npm run prendi-uv && npm run build && electron-builder --win --x64",
"prova": "node test/prove.mjs",
Expand Down
4 changes: 2 additions & 2 deletions src/main/idle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ export interface FileInutile {
}

// i file che c'erano nelle versioni vecchie e ora non sono più nel catalogo
const TOLTI = ['qwen-image-2.1-UC-Q5_K_M.gguf', 'qwen3vl_8b_bf16.safetensors']
const TOLTI = ['qwen-image-2.1-UC-Q5_K_M.gguf', 'qwen3vl_8b_bf16.safetensors', 'flux-2-klein-4b-fp8.safetensors', 'qwen_3_4b.safetensors']

/** i file dei modelli che non sono in uso: non nelle impostazioni, non nel catalogo (o tolti dal catalogo): si possono buttare */
export function fileInutilizzati(): FileInutile[] {
const imp = impostazioni()
// i file di Klein (scelti a mano o scaricati dall'app) sono in uso quanto quelli di Qwen
const klein = ['klein-int8', 'klein-bf16', 'klein-base-int8', 'klein-base-bf16', 'klein-te', 'klein-vae', 'klein4b-fp8', 'klein4b-te', 'llm-huihui-4b'].map((id) => risoltoSalvato(id)?.file || '')
const klein = ['klein-int8', 'klein-bf16', 'klein-base-int8', 'klein-base-bf16', 'klein-te', 'klein-vae', 'llm-huihui-4b'].map((id) => risoltoSalvato(id)?.file || '')
const inUso = new Set([imp.modello, imp.encoder, imp.vae, imp.upscaler, imp.loraTurbo, imp.kleinModello, imp.kleinBase, imp.kleinEncoder, imp.kleinVae, ...klein].filter(Boolean).map((x) => x.toLowerCase()))
const noti = new Set(statoModelli().filter((v) => !v.link).map((v) => (risoltoSalvato(v.id)?.file || v.file).toLowerCase()).filter(Boolean))
const mmproj = (n: string): boolean => /mmproj|\.mmproj\./i.test(n)
Expand Down
24 changes: 12 additions & 12 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { appendFileSync, copyFileSync, existsSync, readFileSync, readdirSync, rm
import { basename, extname, join, normalize } from 'node:path'
import { pathToFileURL } from 'node:url'
import { randomUUID } from 'node:crypto'
import type { Download, Famiglia, FiltroGalleria, Impostazioni, InfoLora, Richiesta } from '@shared/tipi'
import type { Download, Famiglia, FiltroGalleria, GruppoLora, Impostazioni, InfoLora, Richiesta } from '@shared/tipi'
import { vaBeneCon } from '@shared/loraBase'
import { DATI, ENTRATA, MINIATURE, RADICE, TEMP, assicura } from './percorsi'
import { impostazioni, salvaImpostazioni } from './impostazioni'
Expand Down Expand Up @@ -210,20 +210,20 @@ function registraIpc(): void {
const tutti = [...leggi('diffusion_models', /\.(gguf|safetensors)$/i), ...leggi('unet', /\.(gguf|safetensors)$/i)]
const enc = leggi('text_encoders', /\.(safetensors|gguf)$/i)
const vae = leggi('vae', /\.safetensors$/i)
// i file di Klein sono altri: nelle scelte di Qwen non devono comparire
const isKlein = (n: string): boolean => /klein/i.test(n)
const isKleinTe = (n: string): boolean => /(qwen3|qwen_3).*4b/i.test(n) || isKlein(n)
// i file di Klein sono altri: nelle scelte di Qwen non devono comparire. I file di FLUX.2 Klein 4B/9B (tolto nella 1.2.1) e il
// Qwen3 4B di solo testo che serviva a lui non sono di FLUX 3 Klein: restano fuori da tutte le liste.
const eFlux2 = (n: string): boolean => /klein.*[-_](4b|9b)|[-_](4b|9b).*klein|^qwen_3_4b\./i.test(n)
const isKlein = (n: string): boolean => /klein/i.test(n) && !eFlux2(n)
const isKleinTe = (n: string): boolean => (/(qwen3|qwen_3).*4b/i.test(n) && !eFlux2(n)) || isKlein(n)
const isKleinVae = (n: string): boolean => isKlein(n) || /flux.?[23]/i.test(n)
return {
diffusione: tutti.filter((n) => !isKlein(n)),
encoder: enc.filter((n) => !isKleinTe(n)),
diffusione: tutti.filter((n) => !isKlein(n) && !eFlux2(n)),
encoder: enc.filter((n) => !isKleinTe(n) && !eFlux2(n)),
vae: vae.filter((n) => !isKleinVae(n)),
upscaler: leggi('upscale_models', /\.(pth|safetensors)$/i),
klein: tutti.filter(isKlein),
kleinTe: enc.filter(isKleinTe),
kleinVae: vae.filter(isKleinVae),
klein4b: tutti.filter((n) => /klein.*4b|4b.*klein/i.test(n)),
klein4bTe: enc.filter((n) => /^qwen_?3_4b/i.test(n))
kleinVae: vae.filter(isKleinVae)
}
})
gestisci('modelli:inutilizzati', () => fileInutilizzati())
Expand Down Expand Up @@ -325,14 +325,14 @@ function registraIpc(): void {
// i LoRA che si possono usare con questo modello: compatibili ('si') e da provare ('?'), non quelli di un altro modello
gestisci('lora:compatibili', (famiglia: Famiglia) => elencoLora().filter((l) => vaBeneCon(l, famiglia) !== 'no'))
gestisci('lora:aggiorna', (file: string, m: Partial<InfoLora>) => aggiornaLora(file, m))
gestisci('lora:importa', (percorsi: string[]) => importaLora(percorsi))
gestisci('lora:importa', (percorsi: string[], gruppo?: GruppoLora) => importaLora(percorsi, gruppo))
gestisci('lora:elimina', (file: string) => eliminaLora(file))
gestisci('lora:apriCartella', () => {
const c = join(impostazioni().cartellaModelli, 'loras')
assicura(c)
return shell.openPath(c)
})
gestisci('lora:scarica', async (link: string) => {
gestisci('lora:scarica', async (link: string, gruppo?: GruppoLora) => {
const d = nuovoDownload('LoRA da ' + (() => {
try {
return new URL(link).hostname
Expand All @@ -341,7 +341,7 @@ function registraIpc(): void {
}
})())
try {
const file = await scaricaLora(link, d.avanz, d.ctrl.signal)
const file = await scaricaLora(link, d.avanz, d.ctrl.signal, gruppo)
d.info.stato = 'fatto'
d.info.nome = file
} catch (e) {
Expand Down
Loading
Loading