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.0.0 — 2026-10-16

La versione fine: **FLUX 3 Klein**, meno sorprese ovunque, e tutto quello che era rimasto trascurato messo a posto. Niente funzioni nuove oltre a Klein: solo cose che già c'erano, più solide.

- **FLUX 3 Klein** accanto a Qwen: in cima a Crea, Modifica ed Estrai c'è il selettore **Modello che disegna** con tre schede, **Qwen-Image 2.1** (40 passi, LoRA),
**Klein** (la versione veloce, distillata: 16 passi) e **Klein Base** (quella normale: 50 passi e CFG). Le schede dicono quali sono già scaricate; se manca qualcosa,
un clic scarica il modello, il suo text encoder (Qwen3-VL 4B) e il VAE. Con Klein le foto di riferimento entrano come immagini di riferimento, il prompt viene
**potenziato** dal Qwen3-VL (come consiglia BFL; si spegne in Opzioni) e la qualità (Bozza → Massima) sceglie i passi della famiglia. Klein non usa i LoRA di Qwen
né il Turbo: l'app li nasconde. Il **motore passa alla versione 7** con **ComfyUI 0.41.2**: al primo avvio si aggiorna da solo.
- **Estrai col Qwen3-VL**: il rilevatore piccolo (Florence) è tolto, a identificare persone e oggetti torna il Qwen3-VL (di nuovo con solo il pulsante).
- **Crea: i LoRA si aggiungono**, non solo si tolgono: il selettore completo è sempre sotto il prompt (con Qwen).
- **Megapixel e misura finale separati ovunque**: puoi lavorare a 4 MP e far uscire la foto a 480p (o all'inverso), in Crea, Modifica, Estrai, Fusion, Laboratorio.
Sotto il selettore si legge sempre «La foto esce a…».
- **Ingranditore NVIDIA RTX**: l'installazione non fallisce più con «ENOENT… rename»: lo zip si estrae in una cartella di lavoro, si prende la cartella giusta e si
sposta con più tentativi (e, se Windows la tiene occupata, si copia). Stesso metodo per ComfyUI.
- **Modelli scaricati quando non servono**: dopo 120 secondi senza lavori (Opzioni → Prestazioni, cambiabile) l'app libera la VRAM; il lavoro dopo li riporta su.
- **Più robusta, in tutto**: una sola finestra dell'app alla volta; un errore in una pagina non lascia la finestra bianca (si vede cosa è successo e si ricarica);
gli errori nascosti diventano avvisi in italiano; file di impostazioni, indice della galleria e album scritti in modo atomico (con copia di riserva dell'album);
la galleria si ricostruisce dai PNG senza perdere i preferiti; il motore non resta orfano se l'app si chiude male; log del motore a rotazione; download che non
si pestano i piedi (e con messaggi chiari su spazio, permessi e 401/403/404); rete con timeout; localStorage rovinato che non blocca più l'avvio; ricette tue
senza foto che creano davvero; esportazione album fermata che dice «Fermato»; Fusion che mostra sempre l'ultimo risultato; file che non sono foto rifiutati subito.
- **Galleria e interfaccia**: miniature generate a gruppi (non blocca), selezione che segue i filtri, plurali giusti, storia della maschera con un tetto di memoria,
Spazio per spostare solo in Modifica, album con più di 8 foto per spread a griglia.

## 0.11.0 — 2026-10-15

Estrai senza partenze a sorpresa e molto più veloce, una cosa alla volta in VRAM, i token al secondo, i megapixel ovunque.
Expand Down
30 changes: 23 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# DaProd Image — leggi questo per primo

App desktop (Electron + React + TypeScript) per **Qwen-Image-2.1** in locale su una NVIDIA da 6-8 GB: crea, modifica
App desktop (Electron + React + TypeScript) per **Qwen-Image-2.1** (e **FLUX 3 Klein**) in locale su una NVIDIA da 6-8 GB: crea, modifica
tutta la foto o solo una zona, espande, rifinisce, LoRA con un clic, galleria. Il motore è **ComfyUI senza la sua
interfaccia**, installato e guidato dall'app.

Expand All @@ -17,11 +17,11 @@ interfaccia**, installato e guidato dall'app.
Per preparare una versione c'è la skill `.claude/skills/rilascio`.

## Il motore (src/main/installa, src/main/motore)
- **Versioni fissate** in `engine/versioni.json`: ComfyUI a un commit preciso (zip da codeload, estratto col `tar.exe`
- **Versioni fissate** in `engine/versioni.json` (ora **ComfyUI 0.41.2**, motore 7): ComfyUI a un commit preciso (zip da codeload, estratto col `tar.exe`
di Windows: il tar di Git non apre gli zip), Python 3.13 e PyTorch cu130 via **uv** (`resources/bin/uv.exe`,
`npm run prendi-uv`). Tutto in `%LOCALAPPDATA%\DaProdImage\engine`; `stato.json` lì dentro dice quali passi sono fatti.
- **ComfyUI-GGUF è copiato in `engine/custom_nodes/ComfyUI-GGUF`** (fork di leejet al commit `373048b`, l'unico che
conosce l'architettura `qwen_image21`) **con una correzione nostra** in `ops.py`: da ComfyUI 0.39 la Linear riceve
conosce l'architettura `qwen_image21`) **con una correzione nostra** in `ops.py` (più `comfy_force_forward = True` per 0.41): da ComfyUI 0.39 la Linear riceve
`input_act`/`residual`, senza la correzione il KSampler si ferma. Vedi `DAPROD.md` lì dentro. Se si alza ComfyUI,
si riprova una generazione vera prima di pubblicare.
- I modelli (catalogo con sha256 in `src/main/installa/catalogo.ts`) stanno in `%LOCALAPPDATA%\DaProdImage\models`
Expand All @@ -48,6 +48,18 @@ interfaccia**, installato e guidato dall'app.
- `lavori.ts` è **la coda**: un lavoro alla volta al motore, fasi dai nodi (`fasi` del grafo), stima dai secondi per
passo misurati (`impostazioni.tempi`), risultato spostato in galleria.

## FLUX 3 Klein (famiglie di modelli)
- `Famiglia = 'qwen' | 'klein' | 'klein-base'` (`shared/tipi.ts`, `FAMIGLIE` = passi per qualità), `Richiesta.famiglia`, `impostazioni.famiglia`.
Il selettore è `componenti/ScegliModello.tsx` (3 schede, "da scaricare", download di modello + `klein-te` + `klein-vae`), la qualità `ScegliQualita.tsx`.
- Catalogo: `klein-int8/bf16`, `klein-base-int8/bf16`, `klein-te` (Qwen3-VL 4B), `klein-vae` dal repo `black-forest-labs/flux-3-image-klein-comfy`
(`repo` + `cerca` regex, risolti a runtime da `installa/risolvi.ts`: **i nomi vengono dai template ufficiali, vanno confermati con i file veri**).
`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.
- `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`).

## I grafi (src/main/motore/grafi.ts)
- Pipeline ufficiale: `UnetLoaderGGUF` → LoRA (`LoraLoaderModelOnly`) → `QwenImage21Cache`; `CLIPLoader` tipo `qwen_image`
(Qwen3-VL 8B); `TextEncodeQwenImage21`; KSampler **euler/simple, CFG 1, 40 passi** (quelli "standard").
Expand Down Expand Up @@ -95,7 +107,7 @@ interfaccia**, installato e guidato dall'app.
qualunque pagina la mandi (il `conParoleLora` del renderer fa la stessa cosa, non raddoppia). **Esempio** (`fusion.ts`): `pianificaEsempio` fa un foglio con l'esempio (image 1) e i riferimenti numerati e lo dà al Qwen3-VL (`descrivi`, `SISTEMA_PIANO`), che scrive l'istruzione di modifica decidendo da solo cosa sostituire con cosa (niente regole per persone o altro); il piano sta in `pianoEsempio` (con `chiaveEsempio`: foto + nota), si corregge nell'interfaccia, e `richiestaEsempio` lo manda come prompt con l'esempio come `image_1`. **Galleria**: tasto destro
(`MenuContestuale`), `galleria:elimina` con `definitivo`, `galleria:copia` con più id = file negli appunti con PowerShell `Set-Clipboard`. **Laboratorio**:
un `Ingresso` di tipo `camera` mostra il comando Camera (valore JSON, `leggiCamera`) e le ricette aggiungono il passo `VISTA_TUA`.
- **Una cosa alla volta in VRAM** (`lavori.ts`, `fattiSpazio` + `cliente.scaricaModelli`, `impostazioni.unaCosaAllaVolta`): ogni lavoro è `immagine`, `testo` (LLM e rilevatore) o `cpu` (traduttore); quando cambia
- **Una cosa alla volta in VRAM** (`lavori.ts`, `fattiSpazio` + `cliente.scaricaModelli`, `impostazioni.unaCosaAllaVolta`): ogni lavoro è `immagine`, `testo` (LLM) o `cpu` (traduttore); quando cambia
si manda `/free` e si aspetta che `torch_vram_total` scenda (il motore scarica a coda ferma: senza l'attesa lo farebbe dopo il lavoro successivo). **Token/s**: il TextGenerate manda un `progress` per token,
`lavori.ts` ne ricava `Lavoro.tokSec` (media dal primo messaggio) e `BarraStato` lo mostra.
- **Casella** (`componenti/Casella.tsx`): la textarea dei prompt (cresce col testo, cestino); il tasto destro nei campi di testo è nel main (`index.ts`, evento `context-menu`).
Expand Down Expand Up @@ -134,13 +146,17 @@ interfaccia**, installato e guidato dall'app.
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.
- **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**. Di serie la fa il
**rilevatore piccolo** (`modalita: 'trova'` → nodo `DaProdTrova` in `engine/custom_nodes/DaProd-Nodi/trova.py`: Florence-2 base `<OD>`, legge i gettoni `<loc_N>` a mano, poi esce dalla VRAM; modello `trova-florence`
in `models/detect/florence-2-base-ft`, scaricato al primo uso da `lavori.ts`), con `impostazioni.trova` = `'llm'` o se il rilevatore fallisce si ripiega sul Qwen3-VL (`descrivi`, `SISTEMA_ELEMENTI`). Si sceglie con un clic (più clic = il riquadro più grande
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>`:
**persona** = inquadratura (intera/vita/volto) + posa (T/A/in piedi/come/omino: manichino `<image1>`, ritaglio `<image2>`); **oggetto** = vista (come/davanti/¾/alto/lato), completa le parti coperte.
Proporzioni (`proporzioniEstrai`, auto = dal riquadro per gli oggetti) e `dimensioniEstrai` sono le stesse cifre che l'interfaccia mostra. I risultati stanno in `usaEstrai` (non nelle versioni).
- Le impostazioni di ogni immagine stanno **dentro il PNG** (chunk iTXt `daprod`, `src/main/png.ts`): la galleria si
ricostruisce dai file e un PNG trascinato in Galleria torna con prompt, seed e LoRA.
- LoRA: dall'intestazione safetensors si capisce se sono per Qwen 2.1 (`transformer_blocks.N.attn.to_q`/`img_mlp`)
o per altro (txt_mlp = Qwen 1.x, lora_unet = SD/SDXL, double_blocks = Flux). Link Hugging Face e Civitai.

## Robustezza (1.0.0)
- Zip in `installatore.ts` `estraiZip` (cartella di lavoro, rename con tentativi poi copia); `percorsi.ts` `scriviAtomico` per impostazioni, indice galleria, album (+`.bak`).
- `index.ts`: istanza unica, `errori.log` in DATI, pulizia dei temp vecchi; `processo.ts`: `motore.pid` + `chiudiOrfano`, log a rotazione. `impostazioni.scaricaDopo` = secondi di quiete prima di liberare la VRAM.
- Renderer: `main.tsx` ha il `Salvagente` (ErrorBoundary) e il gestore `unhandledrejection`; `leggiLocale`/`leggiLista` tollerano localStorage rovinato.
- `test/finto.js` deve avere gli stessi campi del vero main (es. `modelli.file` con `klein*`): se mancano, l'avviso di errore compare e si vede.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ a 300 dpi con l'abbondanza.
Un manichino con le ossa che muovi come vuoi (anche **mani e dita**, una per una, e la **testa**) per dare una posa al personaggio: in
Modifica → Estrai e nelle ricette del Laboratorio. Diventa un manichino grigio che il modello usa solo come guida di posa.

## Due modelli: Qwen e FLUX 3 Klein

In cima a Crea, Modifica ed Estrai scegli **chi disegna**: **Qwen-Image 2.1** (con i LoRA e il Turbo), **Klein** (veloce, 16 passi) o **Klein Base**
(la versione normale, 50 passi). I file di Klein si scaricano con un clic dal repository ufficiale di Black Forest Labs.

## Estrai

In Modifica: dai una foto e il modello trova **persone e oggetti**; clicchi quello che vuoi (si illumina) e lo ottieni da solo su sfondo
Expand Down Expand Up @@ -141,7 +146,8 @@ se la scheda è da 6 GB, il profilo **6 GB** in Opzioni. L'app ti avvisa da sola

- Il motore è **ComfyUI** a una versione fissata, senza la sua interfaccia, installato con **uv** in
`%LOCALAPPDATA%\DaProdImage`. Il modello è [Qwen-Image 2.1 Uncensored GGUF](https://huggingface.co/abenzerps/Qwen-Image-2.1-Uncensored-GGUF)
(Q4_K_M per 6-8 GB) con i passi della pipeline ufficiale (40, euler, CFG 1).
(Q4_K_M per 6-8 GB) con i passi della pipeline ufficiale (40, euler, CFG 1). In alternativa **FLUX 3 Klein** (`black-forest-labs/flux-3-image-klein-comfy`), con il suo
encoder Qwen3-VL 4B e il VAE FLUX.2.
- **ComfyUI-GGUF** viaggia dentro l'app: il fork di leejet, l'unico che conosce `qwen_image21`, con una nostra
correzione per ComfyUI 0.39 (`engine/custom_nodes/ComfyUI-GGUF/DAPROD.md`). I **DaProd-Nodi** riempiono la zona
prima di "rimuovi" e accordano i colori dopo il VAE, così l'incollaggio non si vede.
Expand Down
2 changes: 1 addition & 1 deletion electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export default defineConfig({
root: 'src/renderer',
resolve: { alias: { '@shared': resolve('src/shared'), '@': resolve('src/renderer/src') } },
plugins: [react()],
build: { rollupOptions: { input: resolve('src/renderer/index.html') } }
build: { minify: 'esbuild', rollupOptions: { input: resolve('src/renderer/index.html') } }
}
})
5 changes: 5 additions & 0 deletions engine/custom_nodes/ComfyUI-GGUF/DAPROD.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ Copia di [leejet/ComfyUI-GGUF](https://github.com/leejet/ComfyUI-GGUF) al commit
Una sola modifica, in `ops.py` (`GGMLOps.Linear.forward_ggml_cast_weights`): da ComfyUI 0.39 la `Linear`
riceve anche `input_act`, `act_weight`, `act_eps`, `residual`, `residual_scale`; senza questa correzione il
KSampler si ferma con `unexpected keyword argument 'input_act'`.

Da ComfyUI 0.41 quei parametri non arrivano più alla `Linear` (c'è `comfy.ops.linear_input_act()`, che per i pesi GGUF
ricade su `forward()`): la correzione resta compatibile con tutte e due le versioni. In più `GGMLOps.Linear` ha
`comfy_force_forward = True`, così `--fast fp16_accumulation` non porta mai i pesi quantizzati nel percorso fp16.
Provato (CPU, Q8_0 reali) con ComfyUI 0.39 e 0.41.2; il motore ora è alla 0.41.2.
3 changes: 3 additions & 0 deletions engine/custom_nodes/ComfyUI-GGUF/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ class GGMLOps(comfy.ops.manual_cast):
Dequantize weights on the fly before doing the compute
"""
class Linear(GGMLLayer, comfy.ops.manual_cast.Linear):
# DaProd Image: da ComfyUI 0.41 `linear_input_act()` salta il forward del modulo se è acceso `--fast fp16_accumulation` e l'attivazione
# è fp16; con pesi GGUF quel percorso leggerebbe i byte quantizzati. Con questo flag passa sempre da forward().
comfy_force_forward = True
def __init__(self, in_features, out_features, bias=True, device=None, dtype=None):
torch.nn.Module.__init__(self)
# TODO: better workaround for reserved memory spike on windows
Expand Down
5 changes: 1 addition & 4 deletions engine/custom_nodes/DaProd-Nodi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# I nodi di DaProd Image: la modifica di una zona (solo torch), la traduzione del prompt e il rilevatore di persone e oggetti (transformers, già con ComfyUI).
# I nodi di DaProd Image: la modifica di una zona (solo torch), e la traduzione del prompt (transformers, già con ComfyUI).
#
# - DaProdRiempiZona: riempie la zona con i colori che ha attorno (push-pull: medie a scale sempre
# più piccole e poi si risale). Serve per "rimuovi": se il modello vede ancora l'oggetto nella foto
Expand All @@ -10,7 +10,6 @@
import torch.nn.functional as F

from .traduzione import DaProdTraduci
from .trova import DaProdTrova


def _maschera_come(maschera, h, w):
Expand Down Expand Up @@ -116,11 +115,9 @@ def esegui(self, image, reference, mask, strength):
"DaProdRiempiZona": DaProdRiempiZona,
"DaProdAccordaColori": DaProdAccordaColori,
"DaProdTraduci": DaProdTraduci,
"DaProdTrova": DaProdTrova,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DaProdRiempiZona": "DaProd · Riempi la zona",
"DaProdAccordaColori": "DaProd · Accorda i colori",
"DaProdTraduci": "DaProd · Traduci il prompt",
"DaProdTrova": "DaProd · Trova persone e oggetti",
}
Loading
Loading