Initial commit: img-infinite-canvas AI design workbench MVP
Moteva-style AI design workbench replica. Home prompt creates a project; the project page provides an infinite canvas with node drag, zoom/pan, a design chat panel, history replay, image asset upload, and project save/regenerate. - Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage; sky-valley/pi agent runtime adapter. - Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n, shadcn/ui components, auth (OTP/Turnstile/Google/WeChat). - Deploy: Docker Compose and k3s manifests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
export type CanvasViewport = {
|
||||
x: number;
|
||||
y: number;
|
||||
k: number;
|
||||
};
|
||||
|
||||
export type CanvasNodeType = "brief" | "reference" | "image" | "text" | "frame";
|
||||
|
||||
export type CanvasNode = {
|
||||
id: string;
|
||||
type: CanvasNodeType;
|
||||
title: string;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
content: string;
|
||||
tone: string;
|
||||
status: string;
|
||||
parentId?: string;
|
||||
layerRole?: string;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
fontWeight?: string;
|
||||
color?: string;
|
||||
textAlign?: "left" | "center" | "right";
|
||||
lineHeight?: number;
|
||||
letterSpacing?: number;
|
||||
textDecoration?: string;
|
||||
textTransform?: string;
|
||||
opacity?: number;
|
||||
fillColor?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
strokeStyle?: string;
|
||||
flipX?: boolean;
|
||||
flipY?: boolean;
|
||||
rotation?: number;
|
||||
imageAdjustments?: string;
|
||||
mockupSurface?: MockupSurface;
|
||||
mockupModel?: MockupModel;
|
||||
mockupSourceContent?: string;
|
||||
};
|
||||
|
||||
export type CanvasConnection = {
|
||||
id: string;
|
||||
fromNodeId: string;
|
||||
toNodeId: string;
|
||||
};
|
||||
|
||||
export type AgentMessage = {
|
||||
id: string;
|
||||
role: "user" | "assistant" | "system" | "tool" | "error";
|
||||
type?: string;
|
||||
title: string;
|
||||
content: string;
|
||||
threadId?: string;
|
||||
actionId?: string;
|
||||
stepId?: string;
|
||||
toolCallId?: string;
|
||||
name?: string;
|
||||
toolHint?: string;
|
||||
status?: string;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type ProjectSummary = {
|
||||
id: string;
|
||||
userId?: string;
|
||||
title: string;
|
||||
brief: string;
|
||||
status: string;
|
||||
thumbnail: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type Project = ProjectSummary & {
|
||||
version: string;
|
||||
snapshotId: string;
|
||||
canvas: string;
|
||||
lastThreadId: string;
|
||||
viewport: CanvasViewport;
|
||||
nodes: CanvasNode[];
|
||||
connections: CanvasConnection[];
|
||||
messages: AgentMessage[];
|
||||
};
|
||||
|
||||
export type QuickAction = {
|
||||
id: string;
|
||||
label: string;
|
||||
prompt: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
export type InspirationItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
category: string;
|
||||
description: string;
|
||||
accent: string;
|
||||
};
|
||||
|
||||
export type Dashboard = {
|
||||
credits: number;
|
||||
quickActions: QuickAction[];
|
||||
projects: ProjectSummary[];
|
||||
inspiration: InspirationItem[];
|
||||
};
|
||||
|
||||
export type ProjectListResponse = {
|
||||
projects: ProjectSummary[];
|
||||
nextOffset: number;
|
||||
hasMore: boolean;
|
||||
};
|
||||
|
||||
export type GenerateResult = {
|
||||
project: Project;
|
||||
message: AgentMessage;
|
||||
generatedNodes: CanvasNode[];
|
||||
};
|
||||
|
||||
export type NormalizedBox = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type RecognizeObjectResponse = {
|
||||
id: string;
|
||||
label: string;
|
||||
confidence: number;
|
||||
bbox: NormalizedBox;
|
||||
};
|
||||
|
||||
export type GeneratorTaskInputArgs = {
|
||||
aspect_ratio?: string;
|
||||
image?: string[];
|
||||
image_url?: string;
|
||||
prompt?: string;
|
||||
resolution?: string;
|
||||
src_box?: NormalizedBox;
|
||||
dst_box?: NormalizedBox;
|
||||
};
|
||||
|
||||
export type GeneratorTaskArtifact = {
|
||||
type: string;
|
||||
content: string;
|
||||
metadata: {
|
||||
artifact_id?: string;
|
||||
format?: string;
|
||||
height?: number;
|
||||
label?: string;
|
||||
node_id?: string;
|
||||
node_name?: string;
|
||||
size_bytes?: number;
|
||||
width?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type GeneratorTaskResponse = {
|
||||
code: number;
|
||||
message: string;
|
||||
data: {
|
||||
generator_task_id: string;
|
||||
generator_name: string;
|
||||
status: "submitted" | "completed" | "failed" | string;
|
||||
original_input_args: GeneratorTaskInputArgs;
|
||||
queue_info?: {
|
||||
in_queue: boolean;
|
||||
position: number;
|
||||
total_queue_count: number;
|
||||
estimated_end_time: string;
|
||||
remaining_time_seconds: number;
|
||||
};
|
||||
artifacts?: GeneratorTaskArtifact[];
|
||||
};
|
||||
};
|
||||
|
||||
export type DeleteProjectResponse = {
|
||||
id: string;
|
||||
deleted: boolean;
|
||||
};
|
||||
|
||||
export type DeleteProjectsResponse = {
|
||||
deleted: number;
|
||||
};
|
||||
|
||||
export type NodeActionRequest = {
|
||||
action:
|
||||
| "quick-edit"
|
||||
| "upscale"
|
||||
| "remove-background"
|
||||
| "eraser"
|
||||
| "edit-elements"
|
||||
| "edit-text"
|
||||
| "multi-angle"
|
||||
| "move-object"
|
||||
| "mockup"
|
||||
| "expand"
|
||||
| "adjust"
|
||||
| "crop"
|
||||
| "vectorize";
|
||||
prompt?: string;
|
||||
imageModel?: string;
|
||||
imageSize?: string;
|
||||
mask?: string;
|
||||
selection?: string;
|
||||
options?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type MockupPlacement = {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
export type MockupPoint = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type MockupSurface = {
|
||||
targetId: string;
|
||||
polygon: MockupPoint[];
|
||||
mesh?: MockupMesh;
|
||||
};
|
||||
|
||||
export type MockupMesh = {
|
||||
columns: number;
|
||||
rows: number;
|
||||
points: MockupPoint[];
|
||||
};
|
||||
|
||||
export type MockupModelMap = {
|
||||
dataId: string;
|
||||
width: number;
|
||||
height: number;
|
||||
url?: string;
|
||||
data?: number[];
|
||||
};
|
||||
|
||||
export type MockupCameraIntrinsics = {
|
||||
matrix: number[];
|
||||
};
|
||||
|
||||
export type MockupModel = {
|
||||
version: number;
|
||||
depthMap: MockupModelMap;
|
||||
depthScaleMin: number;
|
||||
depthScaleMax: number;
|
||||
shadeMap: MockupModelMap;
|
||||
segmentationMap: MockupModelMap;
|
||||
xOffsetMap: MockupModelMap;
|
||||
yOffsetMap: MockupModelMap;
|
||||
cameraIntrinsics: MockupCameraIntrinsics;
|
||||
colorCorrection: string;
|
||||
foregroundImage: string;
|
||||
};
|
||||
|
||||
export type MockupModelResponse = {
|
||||
code: number;
|
||||
data: MockupModel;
|
||||
msg: string;
|
||||
};
|
||||
|
||||
export type ExtractedTextLayer = {
|
||||
text: string;
|
||||
originalText?: string;
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
fontWeight?: string;
|
||||
color?: string;
|
||||
textAlign?: "left" | "center" | "right";
|
||||
lineHeight?: number;
|
||||
letterSpacing?: number;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
opacity?: number;
|
||||
rotation?: number;
|
||||
confidence?: number;
|
||||
};
|
||||
|
||||
export type ExtractNodeTextResponse = {
|
||||
items: ExtractedTextLayer[];
|
||||
};
|
||||
|
||||
export type TextExtractionSource = "eitdortxt" | "edit-text";
|
||||
|
||||
export type MergeLayersRequest = {
|
||||
nodeIds: string[];
|
||||
title?: string;
|
||||
};
|
||||
|
||||
export type AgentContent =
|
||||
| {
|
||||
type: "text";
|
||||
text: string;
|
||||
textSource?: string;
|
||||
}
|
||||
| {
|
||||
type: "image";
|
||||
imageUrl: string;
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
};
|
||||
|
||||
export type AgentChatPayload = {
|
||||
threadId?: string;
|
||||
threadIdType?: number;
|
||||
mode?: string;
|
||||
imageModel?: string;
|
||||
imageSize?: string;
|
||||
enableWebSearch?: boolean;
|
||||
messages: Array<{
|
||||
role: "user";
|
||||
contents: AgentContent[];
|
||||
}>;
|
||||
toolConfig?: {
|
||||
preferToolCategories?: {
|
||||
SEARCH?: string[];
|
||||
IMAGE?: string[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type AgentThreadResponse = {
|
||||
threadId: string;
|
||||
status: string;
|
||||
threadIdType: number;
|
||||
eventsUrl: string;
|
||||
project: Project;
|
||||
};
|
||||
|
||||
export type AgentThreadSummary = {
|
||||
agentThreadId: string;
|
||||
coverImageUrl: string;
|
||||
text: string;
|
||||
threadIdType: number;
|
||||
updateTimestamp: number;
|
||||
};
|
||||
|
||||
export type AgentThreadListResponse = {
|
||||
projectId: string;
|
||||
threads: AgentThreadSummary[];
|
||||
};
|
||||
Reference in New Issue
Block a user