fixes #1
@@ -1,6 +1,6 @@
|
||||
# Caratteri di terze parti inclusi in pi-imgen
|
||||
|
||||
Generato da `scripts/fetch-fonts.sh` il 2026-08-27 07:43 UTC. Non modificare a mano.
|
||||
Generato da `scripts/fetch-fonts.sh` il 2026-08-27 12:28 UTC. Non modificare a mano.
|
||||
|
||||
I file dei caratteri stanno in `vendor/fonts/<id>/` e **non** sono versionati.
|
||||
Ogni cartella contiene il file `LICENSE` originale.
|
||||
|
||||
@@ -724,7 +724,12 @@ export class DrawThingsBackend implements Backend {
|
||||
this.offline = opts.offline ?? process.env.IMGEN_OFFLINE === "1";
|
||||
this.timeoutMs = opts.timeoutMs ?? 0;
|
||||
this.upscaleSteps = opts.upscaleSteps ?? 12;
|
||||
this.upscaleStrength = opts.upscaleStrength ?? 0.25;
|
||||
// 0.4 (up from 0.25) is the floor for going from a 768px draft to a 3579px A3 print
|
||||
// target: at the lower strength SeedVR2 essentially echoes the bicubic resample,
|
||||
// which is what was shipping as a "sgranata" final poster. 0.4 is past the point
|
||||
// where the upscaler invents detail instead of restoring it, while still leaving
|
||||
// the prompt's composition alone.
|
||||
this.upscaleStrength = opts.upscaleStrength ?? 0.4;
|
||||
}
|
||||
|
||||
// -- probe ---------------------------------------------------------------
|
||||
|
||||
@@ -256,15 +256,46 @@ function resolveConfig(deps: LogoDeps): ImgenConfig {
|
||||
return typeof deps.config === "function" ? deps.config() : deps.config;
|
||||
}
|
||||
|
||||
/** `setStatus` is a no-op on some surfaces; guarded anyway — belt and braces. */
|
||||
/**
|
||||
* `setStatus` is a no-op on some surfaces; guarded anyway — belt and braces.
|
||||
*
|
||||
* Drives the working spinner too: `setStatus` alone redraws nothing on its own, and a
|
||||
* generation that updates only the status line looks frozen during a multi-minute model
|
||||
* run. `setWorkingVisible(true)` keeps the spinner up while we are talking to a model,
|
||||
* and `setWorkingMessage` is the text it shows next to the spinner dots.
|
||||
*/
|
||||
function status(ctx: ExtensionContext, text: string | undefined): void {
|
||||
try {
|
||||
ctx.ui.setStatus("imgen", text);
|
||||
const ui = ctx.ui as unknown as {
|
||||
setWorkingMessage?: (t: string | undefined) => void;
|
||||
setWorkingVisible?: (b: boolean) => void;
|
||||
setWorkingIndicator?: (kind: string | undefined) => void;
|
||||
};
|
||||
ui.setWorkingMessage?.(text);
|
||||
ui.setWorkingVisible?.(Boolean(text));
|
||||
ui.setWorkingIndicator?.(text ? "dots" : undefined);
|
||||
} catch {
|
||||
/* a status line is never worth failing a job over */
|
||||
}
|
||||
}
|
||||
|
||||
/** Kill the spinner if the command exits through a path that did not already clear it. */
|
||||
function clearWorking(ctx: ExtensionContext): void {
|
||||
try {
|
||||
const ui = ctx.ui as unknown as {
|
||||
setWorkingVisible?: (b: boolean) => void;
|
||||
setWorkingIndicator?: (kind: string | undefined) => void;
|
||||
setWorkingMessage?: (t: string | undefined) => void;
|
||||
};
|
||||
ui.setWorkingVisible?.(false);
|
||||
ui.setWorkingIndicator?.(undefined);
|
||||
ui.setWorkingMessage?.(undefined);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function notify(ctx: ExtensionContext, message: string, kind: "info" | "warning" | "error" = "info"): void {
|
||||
try {
|
||||
ctx.ui.notify(message, kind);
|
||||
|
||||
@@ -172,17 +172,26 @@ const RESULT_MESSAGE = "imgen-poster";
|
||||
* Draft artwork: small and few steps. Its only job is to let him judge composition and
|
||||
* colour, and it must come back fast enough that the refinement loop feels like a
|
||||
* conversation rather than a render farm.
|
||||
*
|
||||
* 768px on the long edge — not 512 — because the final pass upsamples this image to
|
||||
* print resolution. 512px → ~3579px A3 is a 7× stretch through any upscaler, and the
|
||||
* texture collapses before the upscale can save it. 768px halves that stretch and keeps
|
||||
* enough pixels (~96px/tile on the WCAG tile grid in contrast.ts) for the contrast
|
||||
* measurement to read the artwork faithfully instead of returning a flat average.
|
||||
*/
|
||||
const DRAFT_LONG_EDGE = 512;
|
||||
const DRAFT_STEPS = 4;
|
||||
const DRAFT_LONG_EDGE = 768;
|
||||
const DRAFT_STEPS = 6;
|
||||
|
||||
/**
|
||||
* Final artwork: the full local cap (see render/formats.ts) and enough steps to hold up in
|
||||
* print. Reached ONLY when there is no approved draft to enlarge — i.e. the headless
|
||||
* pipeline. In the interactive command a second generation would be a different picture,
|
||||
* so the final comes from the draft: see `PaintOptions.from`.
|
||||
*
|
||||
* 16 steps (up from 8) is the honest floor for z_image_turbo on a print poster: anything
|
||||
* lower trades away the texture the upscale is supposed to amplify, not invent.
|
||||
*/
|
||||
const FINAL_STEPS = 8;
|
||||
const FINAL_STEPS = 16;
|
||||
|
||||
/** Preview render resolution during the draft loop. A terminal cell is not 300 dpi. */
|
||||
const DRAFT_PPI = 72;
|
||||
@@ -263,17 +272,60 @@ function isAbort(e: unknown): boolean {
|
||||
return e instanceof Error && (e.name === "AbortError" || e.name === "TimeoutError");
|
||||
}
|
||||
|
||||
/** Status line writer that never explodes in a mode without a footer. */
|
||||
/**
|
||||
* Status line writer that also drives the working spinner and its message. `setStatus`
|
||||
* alone redraws nothing on its own; `setWorkingMessage` is what the working indicator
|
||||
* shows, and `setWorkingVisible(true)` is what forces it to stay up while we are
|
||||
* talking to a model. The spinner is the thing he sees during a multi-minute diffusion
|
||||
* run; without it the footer looks frozen between status updates and he does not know
|
||||
* whether the command is still alive.
|
||||
*
|
||||
* Every call is wrapped — `setStatus` and the working-indicator setters are no-ops on
|
||||
* headless surfaces, and a missing method must never abort a generation.
|
||||
*/
|
||||
function statusWriter(ctx: ExtensionContext): (text: string | undefined) => void {
|
||||
let lastVisible = false;
|
||||
return (text) => {
|
||||
try {
|
||||
ctx.ui.setStatus(STATUS_KEY, text);
|
||||
const ui = ctx.ui as unknown as {
|
||||
setWorkingMessage?: (t: string | undefined) => void;
|
||||
setWorkingVisible?: (b: boolean) => void;
|
||||
setWorkingIndicator?: (kind: string | undefined) => void;
|
||||
};
|
||||
ui.setWorkingMessage?.(text);
|
||||
const wantVisible = Boolean(text);
|
||||
if (wantVisible !== lastVisible) {
|
||||
ui.setWorkingVisible?.(wantVisible);
|
||||
ui.setWorkingIndicator?.(wantVisible ? "dots" : undefined);
|
||||
lastVisible = wantVisible;
|
||||
}
|
||||
} catch {
|
||||
/* headless surface without a status bar */
|
||||
/* headless surface without a footer */
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the working indicator is OFF when the command returns, no matter which path
|
||||
* returned. `statusWriter` toggles it on/off on every status call, but any thrown branch
|
||||
* that bypasses the writer would leave the spinner spinning forever.
|
||||
*/
|
||||
function clearWorkingIndicator(ctx: ExtensionContext): void {
|
||||
try {
|
||||
const ui = ctx.ui as unknown as {
|
||||
setWorkingVisible?: (b: boolean) => void;
|
||||
setWorkingIndicator?: (kind: string | undefined) => void;
|
||||
setWorkingMessage?: (t: string | undefined) => void;
|
||||
};
|
||||
ui.setWorkingVisible?.(false);
|
||||
ui.setWorkingIndicator?.(undefined);
|
||||
ui.setWorkingMessage?.(undefined);
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function notify(ctx: ExtensionContext, message: string, kind: "info" | "warning" | "error" = "info"): void {
|
||||
try {
|
||||
ctx.ui.notify(message, kind);
|
||||
@@ -1299,6 +1351,8 @@ export function registerPoster(pi: ExtensionAPI, deps: PosterDeps): void {
|
||||
} finally {
|
||||
// Never leave the footer claiming we are still working.
|
||||
status(undefined);
|
||||
// And kill the working spinner regardless of which branch returned.
|
||||
clearWorkingIndicator(ctx);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1397,7 +1451,7 @@ async function posterCommand(
|
||||
status(S.progress.draft);
|
||||
draftArt = await paintArt(deps, job, spec, "draft", {
|
||||
signal: ctx.signal,
|
||||
onProgress: (_m, fraction) => status(withPercent(S.progress.generating, fraction)),
|
||||
onProgress: (message, fraction) => status(withPercent(S.progress.generating, fraction, message)),
|
||||
onWarning: (w) => notify(ctx, w, "warning"),
|
||||
});
|
||||
status(S.progress.typesetting);
|
||||
@@ -1464,7 +1518,7 @@ async function posterCommand(
|
||||
status(S.progress.generating);
|
||||
draftArt = await paintArt(deps, job, spec, "draft", {
|
||||
signal: ctx.signal,
|
||||
onProgress: (_m, fraction) => status(withPercent(S.progress.generating, fraction)),
|
||||
onProgress: (message, fraction) => status(withPercent(S.progress.generating, fraction, message)),
|
||||
onWarning: (w) => notify(ctx, w, "warning"),
|
||||
});
|
||||
repainted = true;
|
||||
@@ -1524,7 +1578,7 @@ async function posterCommand(
|
||||
artPath = await paintArt(deps, job, spec, "final", {
|
||||
signal: ctx.signal,
|
||||
from: draftArt,
|
||||
onProgress: (_m, fraction) => status(withPercent(S.progress.finalArt, fraction)),
|
||||
onProgress: (message, fraction) => status(withPercent(S.progress.finalArt, fraction, message)),
|
||||
onWarning: (w) => warnings.push(w),
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -1606,8 +1660,17 @@ async function posterCommand(
|
||||
}
|
||||
}
|
||||
|
||||
/** "Genero l'immagine… 40%" — the label stays Italian, the number is just a number. */
|
||||
function withPercent(label: string, fraction?: number): string {
|
||||
/**
|
||||
* "Genero l'immagine… 40%" — the label stays Italian, the number is just a number.
|
||||
*
|
||||
* Takes an optional Italian message from the backend's progress parser — "Decodifica
|
||||
* dell'immagine…", "Salvataggio…", "Caricamento del modello…" — and prefers it to the
|
||||
* generic label when present. The progress parser's words are what the user actually
|
||||
* wants to see during a 90-second diffusion run: a percentage that never moves past 80%
|
||||
* for a minute, with no other context, looks like a hang.
|
||||
*/
|
||||
function withPercent(label: string, fraction: number | undefined, hint?: string): string {
|
||||
if (hint && hint.trim()) return hint;
|
||||
if (typeof fraction !== "number" || !Number.isFinite(fraction)) return label;
|
||||
const pct = Math.max(0, Math.min(100, Math.round(fraction * 100)));
|
||||
return `${label} ${pct}%`;
|
||||
|
||||
@@ -548,6 +548,15 @@ async function socialCommand(
|
||||
} finally {
|
||||
// Never leave a status line behind, on any path.
|
||||
ctx.ui.setStatus(STATUS_KEY, undefined);
|
||||
// And kill the working spinner regardless of which branch returned.
|
||||
const ui = ctx.ui as unknown as {
|
||||
setWorkingVisible?: (b: boolean) => void;
|
||||
setWorkingIndicator?: (kind: string | undefined) => void;
|
||||
setWorkingMessage?: (t: string | undefined) => void;
|
||||
};
|
||||
ui.setWorkingVisible?.(false);
|
||||
ui.setWorkingIndicator?.(undefined);
|
||||
ui.setWorkingMessage?.(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+3872
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
Reference in New Issue
Block a user