Files
sprite-ai/shared/api.ts
T
2026-09-30 07:38:21 +03:00

399 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Типы API между страницами и сервером.
/** Файл в папках ComfyUI (как в ответах ComfyUI и параметрах /view). */
export interface FileRef {
filename: string;
subfolder: string;
type: string; // 'input' | 'output' | 'temp'
}
export interface ApiError {
error: string;
}
// ---------- батчи картинок (ImageBatches + источники + Images) ----------
/** Пользовательский ввод text2img (ImageBatchTextSources) — всё, что нужно, чтобы повторить запуск. */
export interface Text2ImgInput {
shape: string;
colors: string;
/** Шаблон как его ввёл пользователь: {a} — форма, {b} — цвета. Пусто — шаблон по умолчанию. */
template: string;
negative: string;
/** Картинок в батче. */
batch: number;
/** Как введено: пусто — случайный при каждом запуске. */
seed: string;
}
/**
* draft — можно править и запускать; pending — в работе; done / error — завершён.
* Батч с картинками, в работе или из загрузки заблокирован: text-батч можно только продублировать.
*/
export type BatchState = 'draft' | 'pending' | 'done' | 'error';
/** Картинка батча (Images): id и проекты, созданные из неё (из одной картинки — сколько угодно). */
export interface BatchImage extends FileRef {
id: number;
projectIds: number[];
}
/** Проект — работа над одной картинкой из батча. */
export interface Project {
id: number;
created: number;
/** батч, из которого картинка */
batchId: number;
/** откуда исходная картинка: сгенерирована (text — SDXL) или загружена */
imageSource: 'text' | 'upload';
/** исходная картинка */
image: BatchImage;
/** последняя очищенная картинка (история очисток не хранится) */
clean: FileRef | null;
cleanUpdated: number | null;
/** идёт очистка: prompt_id в ComfyUI */
cleanJobId: string | null;
/** ошибка последней очистки */
cleanError?: string;
/** положение идущей очистки в очереди ComfyUI */
live?: LiveStatus;
/**
* 3D-модель без текстур (история не хранится). Генерирует её Hunyuan3D-2 mini или TRELLIS.2;
* TRELLIS.2 заодно заполняет и texture.
*/
model: ModelArtifacts | null;
/** идёт генерация модели: prompt_id в ComfyUI */
modelJobId: string | null;
modelError?: string;
/** положение идущей генерации в очереди ComfyUI */
modelLive?: LiveStatus;
/** текстурированная модель и её текстуры (от TRELLIS.2 или Hunyuan3D-2 Paint) */
texture: TextureArtifacts | null;
/** идёт текстурирование (Paint): prompt_id в ComfyUI */
textureJobId: string | null;
textureError?: string;
textureLive?: LiveStatus;
/** риг SkinTokens: скелет и веса скиннинга на модели (текстурированной, если есть) */
rig: RigArtifacts | null;
/** идёт риггинг: 'skintokens:<мс начала>' (в ComfyUI не попадает — skin-tokens.cpp в WSL) */
rigJobId: string | null;
rigError?: string;
/** анимации, перенесённые на сгенерированный SkinTokens скелет рига (retarget-generated), по порядку создания */
anims: AnimArtifacts[];
animError?: string;
}
/** Анимация из app/data/motions на скелете рига: модель со скелетом рига и движением. */
export interface AnimArtifacts {
glb: FileRef;
/** файлы движений через запятую (пути в app/data/motions) — подпись */
motion: string;
/** файлы движений по порядку анимаций в GLB (у старых записей нет — тогда одна: motion) */
motions?: string[];
/** длительность ретаргета, с */
seconds: number;
updated: number;
}
/** Риг: rig — скелет сгенерирован SkinTokens, skin — только веса под скелет пользователя. */
export interface RigArtifacts {
mode: 'rig' | 'skin';
glb: FileRef;
/** скелет пользователя (GLB), если был */
skeleton: FileRef | null;
/** скелет из app/data/motions (относительный путь), если был — его анимация есть в GLB */
motion?: string;
/** на какой модели: текстурированной или без текстур */
basedOn: 'textured' | 'model';
faces: number | null;
joints: number;
/** время работы SkinTokens, с */
seconds: number;
updated: number;
}
/** Начало идущего риггинга (мс) из rigJobId. */
export const rigStarted = (jobId: string | null) => (jobId?.startsWith('skintokens:') ? Number(jobId.slice(11)) : null);
/** Чем сгенерирована 3D-модель. */
export type ModelGenerator = 'hunyuan' | 'hy3dwrapper' | 'trellis';
export const GENERATOR_NAMES: Record<ModelGenerator, string> = {
hunyuan: 'Hunyuan3D-2 mini', hy3dwrapper: 'Hunyuan3D-2 mini (Hy3D wrapper)', trellis: 'TRELLIS.2',
};
/** Модель без текстур — её можно текстурировать Hunyuan3D-2 Paint (TRELLIS.2 даёт текстуры сразу). */
export const isUntextured = (g: ModelGenerator) => g !== 'trellis';
/** Модель без текстур. */
export interface ModelArtifacts {
generator: ModelGenerator;
glb: FileRef;
/** PNG-превью (рендер без текстур) */
preview: FileRef | null;
/** число треугольников в GLB */
faces: number | null;
/** мс */
updated: number;
}
/** Откуда текстуры: вместе с моделью от TRELLIS.2 или отдельным текстурированием Hunyuan3D-2 Paint. */
export type TextureSource = 'trellis' | 'paint';
/** Текстурированная модель и всё, что к ней относится; каких-то файлов может не быть (у Paint — metallic, roughness). */
export interface TextureArtifacts {
source: TextureSource;
/** текстурированная модель */
glb: FileRef | null;
faces: number | null;
baseColor: FileRef | null;
/** металличность (канал B карты metallicRoughness glTF) в оттенках серого */
metallic: FileRef | null;
/** шероховатость (канал G карты metallicRoughness glTF) в оттенках серого */
roughness: FileRef | null;
/** изображение UV-развёртки */
uv: FileRef | null;
/** виды: у TRELLIS.2 — рендеры модели с 4 сторон, у Paint — 6 видов, нарисованных моделью */
views: FileRef[];
/** мс */
updated: number;
}
/** Генерация 3D-модели проекта. */
export interface ProjectModelRequest {
generator: ModelGenerator;
/** Пусто — случайный. */
seed: number | string;
/** Hunyuan3D-2 mini */
steps: number | string;
octree: number | string;
/** целевое число треугольников после упрощения */
faces: number | string;
/** Hy3D wrapper: перестроить сетку Instant Meshes (Hy3DIMRemesh) до remeshVertices вершин */
remesh: boolean;
remeshVertices: number | string;
/** TRELLIS.2: размер текстуры, px */
textureSize: number | string;
/** TRELLIS.2: металличность и шероховатость (карты metallic, roughness) в модели */
pbr: boolean;
}
/** Текстурирование модели проекта (Hunyuan3D-2 Paint). */
export interface ProjectTextureRequest {
/** Пусто — случайный. */
seed: number | string;
/** размер текстуры, px */
textureSize: number | string;
/** снимать блики и тени с картинки (Hunyuan3D Delight) */
delight: boolean;
}
interface BatchBase {
id: number;
created: number;
updated: number;
/** ImageBatchTextSources.id или ImageBatchUploadSources.id — по sourceType */
sourceId: number;
state: BatchState;
error?: string;
/** Из какого батча продублирован. */
clonedFrom: number | null;
/** Картинки батча (Images.batchId). */
images: BatchImage[];
live?: LiveStatus;
}
export interface TextBatch extends BatchBase {
sourceType: 'text';
source: { id: number; input: Text2ImgInput };
/** prompt_id последнего запуска в ComfyUI. */
jobId: string | null;
/** Фактический seed последнего запуска. */
seedUsed: number | null;
/** Итоговый позитивный промпт последнего запуска. */
prompt: string;
}
export interface UploadBatch extends BatchBase {
sourceType: 'upload';
/** Исходные имена загруженных файлов. */
source: { id: number; files: string[] };
}
export type ImageBatch = TextBatch | UploadBatch;
/** В батче есть картинка, из которой создан проект. */
export function hasProject(b: ImageBatch): boolean {
return b.images.some(i => i.projectIds.length > 0);
}
export function isLocked(b: ImageBatch): boolean {
return b.sourceType === 'upload' || b.state === 'pending' || b.images.length > 0;
}
// ---------- запросы на запуск ----------
/** Очистка исходника проекта. */
export interface CleanupRequest {
size: number | string;
/** Отступ от объекта до края кадра, px с каждой стороны. */
margin: number | string;
grow: number | string;
background: string;
}
// ---------- задачи ComfyUI (таблица jobs; отслеживаются трекером) ----------
export type JobType = 'text2img' | 'cleanup' | 'model' | 'texture';
export type JobState = 'pending' | 'done' | 'error';
/** Состояние незавершённой задачи в очереди ComfyUI (не хранится, считается сервером на лету). */
export type LiveStatus = { state: 'queued'; position: number } | { state: 'running' };
export interface Text2ImgParams {
/** батч (ImageBatches.id), к которому прикрепляются картинки */
batchId: number;
shape: string;
colors: string;
template: string;
negative: string;
batch: number;
seed: number;
/** Итоговый позитивный промпт, как его увидит модель. */
prompt: string;
}
export interface CleanupParams {
/** проект, к которому прикрепляется результат */
projectId: number;
size: number;
margin: number;
grow: number;
background: string;
}
export interface ModelParams {
/** проект, к которому прикрепляется модель */
projectId: number;
generator: ModelGenerator;
seed: number;
faces: number;
/** Hunyuan3D-2 mini */
steps?: number;
octree?: number;
/** Hy3D wrapper: Hy3DIMRemesh, 0 — выключен */
remeshVertices?: number;
/** TRELLIS.2 */
textureSize?: number;
pbr?: boolean;
}
export interface TextureParams {
/** проект, к которому прикрепляется результат */
projectId: number;
seed: number;
textureSize: number;
delight: boolean;
}
interface JobOf<T extends JobType, P> {
/** prompt_id в ComfyUI */
id: string;
type: T;
/** мс с эпохи */
created: number;
state: JobState;
error?: string;
/** картинка-исходник (для cleanup и model) */
source: FileRef | null;
params: P;
/** результаты-картинки (для model — PNG-превью) */
images: FileRef[];
/** GLB-файлы (model, texture) */
models: FileRef[];
live?: LiveStatus;
}
export type Text2ImgJob = JobOf<'text2img', Text2ImgParams>;
export type CleanupJob = JobOf<'cleanup', CleanupParams>;
export type ModelJob = JobOf<'model', ModelParams>;
export type TextureJob = JobOf<'texture', TextureParams>;
export type Job = Text2ImgJob | CleanupJob | ModelJob | TextureJob;
// ---------- значения полей форм (тоже в SQLite) ----------
export interface Text2ImgSettings {
shape: string;
colors: string;
template: string;
negative: string;
batch: string;
seed: string;
promptsOpen: boolean;
}
export interface CleanupSettings {
size: string;
margin: string;
grow: string;
background: string;
}
export interface ModelSettings {
generator: ModelGenerator;
seed: string;
/** Hunyuan3D-2 mini */
steps: string;
octree: string;
faces: string;
/** Hy3D wrapper */
remesh: boolean;
remeshVertices: string;
/** TRELLIS.2 */
trellisFaces: string;
textureSize: string;
trellisPbr: boolean;
}
export interface TextureSettings {
seed: string;
textureSize: string;
delight: boolean;
}
export interface RetargetSettings {
/** выбранные файлы движений в app/data/motions */
motions: string[];
}
export interface RigSettings {
/** скелет: '' — сгенерировать, 'file' — свой GLB, иначе путь GLB в app/data/motions */
skeleton: string;
/** как совместить скелет пользователя с мешем: none | global | articulated */
fit: string;
postprocess: boolean;
}
export type SettingsByPage = {
text2img: Text2ImgSettings; cleanup: CleanupSettings; model: ModelSettings; texture: TextureSettings; rig: RigSettings;
retarget: RetargetSettings;
};
export type SettingsPage = keyof SettingsByPage;
// ---------- очередь ComfyUI ----------
export type JobKind = 'генерация' | 'очистка фона' | '3D-модель' | 'текстура' | 'другое';
export interface QueueItem {
id: string;
kind: JobKind;
}
export interface QueueInfo {
running: QueueItem[];
pending: QueueItem[];
}
// ---------- перенос из localStorage старой версии ----------
export interface ImportRequest {
settings: Partial<{ [P in SettingsPage]: Partial<SettingsByPage[P]> }>;
}
export const DEFAULT_TEMPLATE =
'Game character. Full body visible. Empty arms. {a}, centered on white background. {b}. Game sprite aesthetic.';