3eae2beb12
When an image-generator target requests multiple images, duplicate the target placeholder into N generating clones and lay them out beside the target. Incremental results after the first are appended as siblings rather than merged into the target, and placeholder cleanup now clears the target's generating clones on failure. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
img-infinite-canvas API
go-zero backend for the Moteva-style infinite canvas MVP.
Features
- Dashboard data for the Moteva home page
- Project creation from prompt
- Project title rename
- Full canvas project reads
- Moteva-style generation mock for image/design workflows
- Canvas layout save
- Chat history replay
- DDD layout with domain, application, infrastructure, and generated handler layers
- sky-valley/pi Go agent adapter with local desktop agent disabled
- Optional PostgreSQL persistence through pgx/sqlc
- Optional Redis cache, default memory cache
- Asynq job queue for generation, OCR, layer merge, and asset cleanup workers
- Image asset upload URLs for memory, MinIO, AWS S3, Cloudflare R2, and Aliyun OSS
Configuration
Name: img_infinite_canvas-api
Host: 0.0.0.0
Port: 8888
Timeout: 1200000
Storage:
Driver: postgres
DataSource: postgres://canvas:canvas@localhost:15432/canvas?sslmode=disable
Cache:
Driver: redis
Addr: localhost:16379
Password: ""
DB: 0
TTLSeconds: 300
JobQueue:
Driver: asynq
Addr: localhost:16379
Password: ""
DB: 0
Queue: default
Concurrency: 10
MaxRetry: 3
TimeoutSeconds: 900
ShutdownTimeoutSeconds: 30
Agent:
Driver: pi
Text:
Driver: vision
Model: gpt-5.4-mini
TimeoutSeconds: 240
CacheTTLSeconds: 600
Planner:
Driver: vision
Model: gpt-5.5
TimeoutSeconds: 240
Image:
BaseURL: http://154.21.87.3:8080
APIKeyEnv: GPT_IMAGE_API_KEY
InputImageTransport: file
TimeoutSeconds: 600
ObjectStorage:
Driver: minio
Bucket: canvas-assets
Endpoint: localhost:19000
Region: us-east-1
AccessKey: canvas
SecretKey: canvas-secret
PublicBase: http://localhost:19000/canvas-assets
UseSSL: false
ExpiresIn: 900
Agent.Image.InputImageTransport controls how input/reference images are sent to the image model for edit/image-to-image calls: file uploads image bytes with multipart form data, while url sends images: [{"image_url": "..."}] for gateways that can fetch public URLs directly.
Start local Docker dependencies for debugging:
docker compose -f ../deploy/docker-compose.yml up -d postgres redis minio
Use PostgreSQL, Redis, and MinIO inside Docker Compose. The compose file runs the API and Asynq worker as separate processes:
Storage:
Driver: postgres
DataSource: postgres://canvas:canvas@postgres:5432/canvas?sslmode=disable
Cache:
Driver: redis
Addr: redis:6379
TTLSeconds: 300
JobQueue:
Driver: asynq
Addr: redis:6379
Queue: default
TimeoutSeconds: 900
ObjectStorage:
Driver: minio
Bucket: canvas-assets
Endpoint: minio:9000
Region: us-east-1
AccessKey: canvas
SecretKey: canvas-secret
PublicBase: http://localhost:19000/canvas-assets
UseSSL: false
Running
go run . -f etc/config.yaml -mode all
go run . -f etc/config.yaml -mode api
go run . -f etc/config.yaml -mode worker
Testing
go test ./...
go build ./...
Example
curl http://localhost:8888/api/dashboard
curl -X POST http://localhost:8888/api/projects \
-H "Content-Type: application/json" \
-d '{"prompt":"为 still day 品牌服装独立站设计官网视觉方向"}'
curl -X PATCH http://localhost:8888/api/projects/<project-id>/title \
-H "Content-Type: application/json" \
-d '{"title":"夏季活动主视觉"}'
curl -X POST http://localhost:8888/api/canva/project/saveProject \
-H "Content-Type: application/json" \
-d '{"canvas":"SHAKKERDATA://<gzip-base64-canvas-snapshot>","projectCoverList":[],"picCount":0,"projectName":"Untitled","projectId":"<project-id>","version":"1783472149000","sessionId":"browser-session-id","canvasV2Gray":true}'
curl -X POST http://localhost:8888/api/assets/upload-url \
-H "Content-Type: application/json" \
-d '{"fileName":"ref.png","contentType":"image/png"}'
Detailed endpoint docs are in API.md.