gpt-image-playground
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGPT Image Playground
GPT Image Playground
Skill by ara.so — Daily 2026 Skills collection.
A React 19 + TypeScript + Vite web application for generating and editing images via OpenAI's Images API () or Responses API (). Features a responsive UI with waterfall task cards, IndexedDB local storage, PWA support, bulk selection, history management, and ZIP export/import.
/v1/images/v1/responses由ara.so开发的技能 —— 属于Daily 2026 Skills合集。
这是一款基于React 19 + TypeScript + Vite的Web应用,可通过OpenAI的Images API()或Responses API()生成和编辑图像。具备响应式UI(含瀑布流任务卡片)、IndexedDB本地存储、PWA支持、批量选择、历史记录管理以及ZIP导出/导入功能。
/v1/images/v1/responsesWhat It Does
功能介绍
- Text-to-image: Generate images from prompts via or
images/generationswithresponsestoolimage_generation - Reference image editing: Upload up to 16 reference images, call or multimodal Responses API
images/edits - Smart size selector: Auto-calculates resolutions for 1K/2K/4K at common aspect ratios; custom sizes auto-snapped to 16px multiples, max 3840px, max ratio 3:1, pixel range 655360–8294400
- Parameter control: Quality (low/medium/high), format (PNG/JPEG/WebP), compression, moderation level
- History: IndexedDB storage with SHA-256 image deduplication, bulk ops, favorites, search/filter
- Data portability: ZIP export/import with raw image files +
manifest.json
- 文本转图像:通过或带
images/generations工具的image_generation接口,根据提示词生成图像responses - 参考图编辑:最多上传16张参考图,调用或多模态Responses API
images/edits - 智能尺寸选择器:自动计算1K/2K/4K分辨率的常见宽高比;自定义尺寸自动对齐到16px倍数,最大边长3840px,最大宽高比3:1,像素范围655360–8294400
- 参数控制:支持质量(低/中/高)、格式(PNG/JPEG/WebP)、压缩率、审核级别设置
- 历史记录:基于IndexedDB存储,采用SHA-256图像去重,支持批量操作、收藏、搜索/筛选
- 数据可移植性:ZIP导出/导入功能,包含原始图像文件 +
manifest.json
Tech Stack
技术栈
| Layer | Technology |
|---|---|
| Framework | React 19 + TypeScript |
| Build | Vite |
| Styling | Tailwind CSS 3 |
| State | Zustand |
| Storage | Browser IndexedDB API |
| 层级 | 技术 |
|---|---|
| 框架 | React 19 + TypeScript |
| 构建工具 | Vite |
| 样式方案 | Tailwind CSS 3 |
| 状态管理 | Zustand |
| 存储方案 | Browser IndexedDB API |
Installation & Setup
安装与配置
Prerequisites
前置要求
- Node.js 18+
- npm
- Node.js 18+
- npm
Clone and Run Locally
克隆并本地运行
bash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playground
npm install
npm run devbash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playground
npm install
npm run devundefinedundefinedEnvironment Variables
环境变量
Create in the project root:
.env.localbash
undefined在项目根目录创建文件:
.env.localbash
undefinedOptional: pre-fill default API URL at build time
可选:构建时预填充默认API地址
VITE_DEFAULT_API_URL=https://api.openai.com/v1
undefinedVITE_DEFAULT_API_URL=https://api.openai.com/v1
undefinedBuild for Production
生产环境构建
bash
npm run buildbash
npm run buildOutput in dist/ — deploy to any static file server
输出文件在dist/目录 —— 可部署到任意静态文件服务器
undefinedundefinedDocker
Docker部署
bash
undefinedbash
undefinedSingle container
单容器运行
docker run -d -p 8080:80
-e API_URL=https://api.openai.com/v1
ghcr.io/cooksleep/gpt_image_playground:latest
-e API_URL=https://api.openai.com/v1
ghcr.io/cooksleep/gpt_image_playground:latest
docker run -d -p 8080:80
-e API_URL=https://api.openai.com/v1
ghcr.io/cooksleep/gpt_image_playground:latest
-e API_URL=https://api.openai.com/v1
ghcr.io/cooksleep/gpt_image_playground:latest
Docker Compose
Docker Compose部署
cat > docker-compose.yml << 'EOF'
services:
gpt-image-playground:
image: ghcr.io/cooksleep/gpt_image_playground:latest
environment:
- API_URL=https://api.openai.com/v1
ports:
- "8080:80"
restart: unless-stopped
EOF
docker compose up -d
Update to latest:
```bash
docker compose pull && docker compose up -dcat > docker-compose.yml << 'EOF'
services:
gpt-image-playground:
image: ghcr.io/cooksleep/gpt_image_playground:latest
environment:
- API_URL=https://api.openai.com/v1
ports:
- "8080:80"
restart: unless-stopped
EOF
docker compose up -d
更新至最新版本:
```bash
docker compose pull && docker compose up -dVercel One-Click Deploy
Vercel一键部署
Add environment variable in Settings → Environment Variables:
VITE_DEFAULT_API_URL=https://api.openai.com/v1API Configuration
API配置
In-App Settings
应用内设置
Open settings (top-right gear icon) and configure:
| Field | Images API | Responses API |
|---|---|---|
| Endpoint | | |
| Model example | | |
打开右上角齿轮图标进入设置页面,配置以下内容:
| 字段 | Images API | Responses API |
|---|---|---|
| 接口地址 | | |
| 模型示例 | | |
URL Query Parameters (Deep-link Config)
URL查询参数(深度链接配置)
Pre-fill settings via URL — useful for bookmarks or sharing:
https://gpt-image-playground.cooksleep.dev?apiUrl=https://your-proxy.com&apiKey=sk-xxxx
https://cooksleep.github.io/gpt_image_playground?apiUrl=https://your-proxy.com&apiKey=sk-xxxxParameters:
- — API base URL
apiUrl - — OpenAI API key
apiKey
通过URL预填充设置,适合书签或分享:
https://gpt-image-playground.cooksleep.dev?apiUrl=https://your-proxy.com&apiKey=sk-xxxx
https://cooksleep.github.io/gpt_image_playground?apiUrl=https://your-proxy.com&apiKey=sk-xxxx参数说明:
- —— API基础地址
apiUrl - —— OpenAI API密钥
apiKey
Local Development Proxy (CORS Bypass)
本地开发代理(跨域绕过)
When your API endpoint doesn't allow browser CORS, use the Vite dev proxy:
bash
cp dev-proxy.config.example.json dev-proxy.config.jsonEdit :
dev-proxy.config.jsonjson
{
"enabled": true,
"prefix": "/api-proxy",
"target": "http://127.0.0.1:3000",
"changeOrigin": true,
"secure": false
}Request flow:
Browser → http://localhost:5173/api-proxy/v1/images/generations
→ Vite proxy forwards to →
http://127.0.0.1:3000/v1/images/generationsNote: Proxy only works with . Not included in production builds.
npm run devSet in the app settings to match . The app rewrites matching requests to use the proxy prefix.
API URLtargetIfortargetalready includesAPI URL, the path won't be duplicated — requests become/v1not/api-proxy/responses./api-proxy/v1/responses
当API端点不允许浏览器跨域请求时,使用Vite开发代理:
bash
cp dev-proxy.config.example.json dev-proxy.config.json编辑:
dev-proxy.config.jsonjson
{
"enabled": true,
"prefix": "/api-proxy",
"target": "http://127.0.0.1:3000",
"changeOrigin": true,
"secure": false
}请求流程:
浏览器 → http://localhost:5173/api-proxy/v1/images/generations
→ Vite代理转发至 →
http://127.0.0.1:3000/v1/images/generations注意:代理仅在模式下生效,不包含在生产构建中。
npm run dev在应用设置中设置以匹配,应用会自动将匹配的请求重写为使用代理前缀。
API URLtarget如果或target已包含API URL,路径不会重复 —— 请求将变为/v1而非/api-proxy/responses。/api-proxy/v1/responses
Key Source Code Patterns
核心源码模式
Project Structure
项目结构
src/
├── components/ # React UI components
├── hooks/ # Custom React hooks
├── store/ # Zustand state stores
├── utils/ # Helpers (IndexedDB, image processing, etc.)
├── types/ # TypeScript type definitions
└── main.tsx # App entry pointsrc/
├── components/ # React UI组件
├── hooks/ # 自定义React钩子
├── store/ # Zustand状态存储
├── utils/ # 工具函数(IndexedDB、图像处理等)
├── types/ # TypeScript类型定义
└── main.tsx # 应用入口Using the Images API Directly (TypeScript)
直接调用Images API(TypeScript)
typescript
// Pattern matching how the app calls OpenAI Images API
async function generateImage(params: {
prompt: string;
model: string;
size: string;
quality: "low" | "medium" | "high";
n: number;
outputFormat: "png" | "jpeg" | "webp";
outputCompression?: number;
apiKey: string;
apiUrl: string;
}) {
const response = await fetch(`${params.apiUrl}/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${params.apiKey}`,
},
body: JSON.stringify({
model: params.model,
prompt: params.prompt,
n: params.n,
size: params.size,
quality: params.quality,
output_format: params.outputFormat,
...(params.outputFormat !== "png" && {
output_compression: params.outputCompression,
}),
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message ?? "Generation failed");
}
return response.json();
}typescript
// 匹配应用调用OpenAI Images API的模式
async function generateImage(params: {
prompt: string;
model: string;
size: string;
quality: "low" | "medium" | "high";
n: number;
outputFormat: "png" | "jpeg" | "webp";
outputCompression?: number;
apiKey: string;
apiUrl: string;
}) {
const response = await fetch(`${params.apiUrl}/images/generations`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${params.apiKey}`,
},
body: JSON.stringify({
model: params.model,
prompt: params.prompt,
n: params.n,
size: params.size,
quality: params.quality,
output_format: params.outputFormat,
...(params.outputFormat !== "png" && {
output_compression: params.outputCompression,
}),
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message ?? "Generation failed");
}
return response.json();
}Using the Images Edit API with Reference Images
使用参考图调用Images Edit API
typescript
async function editImage(params: {
prompt: string;
images: File[];
model: string;
size: string;
quality: "low" | "medium" | "high";
n: number;
apiKey: string;
apiUrl: string;
}) {
const formData = new FormData();
formData.append("model", params.model);
formData.append("prompt", params.prompt);
formData.append("n", String(params.n));
formData.append("size", params.size);
formData.append("quality", params.quality);
// Up to 16 reference images
params.images.forEach((file) => {
formData.append("image[]", file);
});
const response = await fetch(`${params.apiUrl}/images/edits`, {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
// Do NOT set Content-Type — browser sets multipart boundary automatically
},
body: formData,
});
return response.json();
}typescript
async function editImage(params: {
prompt: string;
images: File[];
model: string;
size: string;
quality: "low" | "medium" | "high";
n: number;
apiKey: string;
apiUrl: string;
}) {
const formData = new FormData();
formData.append("model", params.model);
formData.append("prompt", params.prompt);
formData.append("n", String(params.n));
formData.append("size", params.size);
formData.append("quality", params.quality);
// 最多16张参考图
params.images.forEach((file) => {
formData.append("image[]", file);
});
const response = await fetch(`${params.apiUrl}/images/edits`, {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
// 请勿设置Content-Type —— 浏览器会自动设置multipart boundary
},
body: formData,
});
return response.json();
}Using Responses API with image_generation Tool
使用带image_generation工具的Responses API
typescript
async function generateViaResponsesAPI(params: {
prompt: string;
model: string;
size: string;
quality: "low" | "medium" | "high";
apiKey: string;
apiUrl: string;
}) {
const response = await fetch(`${params.apiUrl}/responses`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${params.apiKey}`,
},
body: JSON.stringify({
model: params.model,
input: params.prompt,
tools: [
{
type: "image_generation",
size: params.size,
quality: params.quality,
},
],
}),
});
return response.json();
}typescript
async function generateViaResponsesAPI(params: {
prompt: string;
model: string;
size: string;
quality: "low" | "medium" | "high";
apiKey: string;
apiUrl: string;
}) {
const response = await fetch(`${params.apiUrl}/responses`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${params.apiKey}`,
},
body: JSON.stringify({
model: params.model,
input: params.prompt,
tools: [
{
type: "image_generation",
size: params.size,
quality: params.quality,
},
],
}),
});
return response.json();
}Custom Size Validation (matches app logic)
自定义尺寸验证(匹配应用逻辑)
typescript
function sanitizeImageSize(width: number, height: number): {
width: number;
height: number;
} {
// Snap to 16px multiples
let w = Math.round(width / 16) * 16;
let h = Math.round(height / 16) * 16;
// Max edge 3840px
const maxEdge = 3840;
if (w > maxEdge) w = maxEdge;
if (h > maxEdge) h = maxEdge;
// Aspect ratio must not exceed 3:1
if (w / h > 3) h = Math.ceil(w / 3 / 16) * 16;
if (h / w > 3) w = Math.ceil(h / 3 / 16) * 16;
// Total pixels: 655360 to 8294400
const pixels = w * h;
const minPixels = 655360;
const maxPixels = 8294400;
if (pixels < minPixels) {
const scale = Math.sqrt(minPixels / pixels);
w = Math.ceil((w * scale) / 16) * 16;
h = Math.ceil((h * scale) / 16) * 16;
}
if (pixels > maxPixels) {
const scale = Math.sqrt(maxPixels / pixels);
w = Math.floor((w * scale) / 16) * 16;
h = Math.floor((h * scale) / 16) * 16;
}
return { width: w, height: h };
}typescript
function sanitizeImageSize(width: number, height: number): {
width: number;
height: number;
} {
// 对齐到16px倍数
let w = Math.round(width / 16) * 16;
let h = Math.round(height / 16) * 16;
// 最大边长3840px
const maxEdge = 3840;
if (w > maxEdge) w = maxEdge;
if (h > maxEdge) h = maxEdge;
// 宽高比不得超过3:1
if (w / h > 3) h = Math.ceil(w / 3 / 16) * 16;
if (h / w > 3) w = Math.ceil(h / 3 / 16) * 16;
// 总像素:655360至8294400
const pixels = w * h;
const minPixels = 655360;
const maxPixels = 8294400;
if (pixels < minPixels) {
const scale = Math.sqrt(minPixels / pixels);
w = Math.ceil((w * scale) / 16) * 16;
h = Math.ceil((h * scale) / 16) * 16;
}
if (pixels > maxPixels) {
const scale = Math.sqrt(maxPixels / pixels);
w = Math.floor((w * scale) / 16) * 16;
h = Math.floor((h * scale) / 16) * 16;
}
return { width: w, height: h };
}Adding IndexedDB Storage (pattern used by the app)
添加IndexedDB存储(应用使用的模式)
typescript
import { openDB, IDBPDatabase } from "idb"; // App uses native IDB API directly
const DB_NAME = "gpt-image-playground";
const DB_VERSION = 1;
async function initDB(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
// Tasks store
if (!db.objectStoreNames.contains("tasks")) {
const tasks = db.createObjectStore("tasks", { keyPath: "id" });
tasks.createIndex("createdAt", "createdAt");
tasks.createIndex("status", "status");
tasks.createIndex("favorited", "favorited");
}
// Images store with SHA-256 hash deduplication
if (!db.objectStoreNames.contains("images")) {
db.createObjectStore("images", { keyPath: "hash" });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}typescript
import { openDB, IDBPDatabase } from "idb"; // 应用直接使用原生IDB API
const DB_NAME = "gpt-image-playground";
const DB_VERSION = 1;
async function initDB(): Promise<IDBDatabase> {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME, DB_VERSION);
request.onupgradeneeded = (event) => {
const db = (event.target as IDBOpenDBRequest).result;
// 任务存储
if (!db.objectStoreNames.contains("tasks")) {
const tasks = db.createObjectStore("tasks", { keyPath: "id" });
tasks.createIndex("createdAt", "createdAt");
tasks.createIndex("status", "status");
tasks.createIndex("favorited", "favorited");
}
// 带SHA-256哈希去重的图像存储
if (!db.objectStoreNames.contains("images")) {
db.createObjectStore("images", { keyPath: "hash" });
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}Zustand Store Pattern (matching app architecture)
Zustand存储模式(匹配应用架构)
typescript
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface SettingsStore {
apiKey: string;
apiUrl: string;
apiMode: "images" | "responses";
model: string;
setApiKey: (key: string) => void;
setApiUrl: (url: string) => void;
setApiMode: (mode: "images" | "responses") => void;
setModel: (model: string) => void;
}
const useSettingsStore = create<SettingsStore>()(
persist(
(set) => ({
apiKey: "",
apiUrl: "https://api.openai.com/v1",
apiMode: "images",
model: "gpt-image-1",
setApiKey: (apiKey) => set({ apiKey }),
setApiUrl: (apiUrl) => set({ apiUrl }),
setApiMode: (apiMode) => set({ apiMode }),
setModel: (model) => set({ model }),
}),
{ name: "gpt-image-settings" }
)
);typescript
import { create } from "zustand";
import { persist } from "zustand/middleware";
interface SettingsStore {
apiKey: string;
apiUrl: string;
apiMode: "images" | "responses";
model: string;
setApiKey: (key: string) => void;
setApiUrl: (url: string) => void;
setApiMode: (mode: "images" | "responses") => void;
setModel: (model: string) => void;
}
const useSettingsStore = create<SettingsStore>()(
persist(
(set) => ({
apiKey: "",
apiUrl: "https://api.openai.com/v1",
apiMode: "images",
model: "gpt-image-1",
setApiKey: (apiKey) => set({ apiKey }),
setApiUrl: (apiUrl) => set({ apiUrl }),
setApiMode: (apiMode) => set({ apiMode }),
setModel: (model) => set({ model }),
}),
{ name: "gpt-image-settings" }
)
);Common Workflows
常见工作流
1. Fork & Customize for a Specific Use Case
1. Fork并针对特定场景定制
bash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playgroundbash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playgroundSet default API
设置默认API
echo "VITE_DEFAULT_API_URL=https://your-proxy.com/v1" > .env.local
npm install && npm run dev
undefinedecho "VITE_DEFAULT_API_URL=https://your-proxy.com/v1" > .env.local
npm install && npm run dev
undefined2. Add a Custom Preset Size
2. 添加自定义预设尺寸
In the size selector component, add to the presets array:
typescript
const SIZE_PRESETS = [
// existing presets...
{
label: "Banner 16:5",
ratio: { w: 16, h: 5 },
resolutions: {
"1K": { width: 1024, height: 320 },
"2K": { width: 2048, height: 640 },
},
},
];在尺寸选择器组件中,添加到预设数组:
typescript
const SIZE_PRESETS = [
// 现有预设...
{
label: "Banner 16:5",
ratio: { w: 16, h: 5 },
resolutions: {
"1K": { width: 1024, height: 320 },
"2K": { width: 2048, height: 640 },
},
},
];3. Extend Task History with Custom Metadata
3. 扩展任务历史记录的自定义元数据
typescript
interface TaskRecord {
id: string;
createdAt: number;
prompt: string;
status: "pending" | "success" | "failed";
favorited: boolean;
parameters: {
model: string;
size: string;
quality: string;
n: number;
outputFormat: string;
};
// Add custom fields:
tags?: string[];
projectId?: string;
outputImages: string[]; // SHA-256 hashes referencing images store
}typescript
interface TaskRecord {
id: string;
createdAt: number;
prompt: string;
status: "pending" | "success" | "failed";
favorited: boolean;
parameters: {
model: string;
size: string;
quality: string;
n: number;
outputFormat: string;
};
// 添加自定义字段:
tags?: string[];
projectId?: string;
outputImages: string[]; // 引用图像存储的SHA-256哈希
}4. Export/Import Data Programmatically
4. 程序化导出/导入数据
The app exports a ZIP containing:
- Raw image files (not base64)
- with task records and image metadata
manifest.json
typescript
// manifest.json structure
interface ExportManifest {
version: string;
exportedAt: string;
tasks: TaskRecord[];
images: {
hash: string;
filename: string;
mimeType: string;
size: number;
}[];
}应用导出的ZIP包包含:
- 原始图像文件(非base64格式)
- ,包含任务记录和图像元数据
manifest.json
typescript
// manifest.json结构
interface ExportManifest {
version: string;
exportedAt: string;
tasks: TaskRecord[];
images: {
hash: string;
filename: string;
mimeType: string;
size: number;
}[];
}Troubleshooting
故障排查
CORS Errors in Browser
浏览器跨域错误
Cause: API endpoint doesn't allow browser cross-origin requests.
Fix (dev): Enable local proxy in (see above).
dev-proxy.config.jsonFix (production): Use a server-side proxy (Vercel Functions, Cloudflare Workers, or Nginx ).
proxy_pass原因:API端点不允许浏览器跨域请求。
开发环境修复:在中启用本地代理(见上文)。
dev-proxy.config.json生产环境修复:使用服务端代理(Vercel Functions、Cloudflare Workers或Nginx )。
proxy_pass.dev
Domain HTTPS Requirement
.dev.dev
域名HTTPS要求
.devThe deployment requires all resources to be HTTPS. If your API is HTTP-only, use the GitHub Pages version:
gpt-image-playground.cooksleep.devhttps://cooksleep.github.io/gpt_image_playgroundgpt-image-playground.cooksleep.devhttps://cooksleep.github.io/gpt_image_playgroundCustom Size Not Accepted by API
自定义尺寸不被API接受
Ensure size passes validation:
- Width and height are multiples of 16
- Neither dimension exceeds 3840px
- Aspect ratio ≤ 3:1
- Total pixels between 655,360 and 8,294,400
Use the function above to auto-correct.
sanitizeImageSize确保尺寸通过验证:
- 宽高均为16px的倍数
- 任意边长不超过3840px
- 宽高比≤3:1
- 总像素在655,360至8,294,400之间
使用上述函数自动修正。
sanitizeImageSizeResponses API vs Images API
Responses API vs Images API
| Scenario | Use |
|---|---|
| Direct image generation | Images API + |
| Codex CLI-derived APIs | Responses API |
| Text model + image tool | Responses API + text model (e.g. |
| Images API only |
| 场景 | 推荐使用 |
|---|---|
| 直接图像生成 | Images API + |
| Codex CLI衍生API | Responses API |
| 文本模型+图像工具 | Responses API + 文本模型(如 |
多图像输入的 | 仅Images API |
Docker: API URL Not Pre-filled
Docker:API地址未预填充
Ensure you pass the env variable at runtime (not build time):
bash
docker run -e API_URL=https://api.openai.com/v1 ...The container's entrypoint injects into the Nginx-served static files at startup.
API_URL确保在运行时传递环境变量(而非构建时):
bash
docker run -e API_URL=https://api.openai.com/v1 ...容器的入口点会在启动时将注入Nginx提供的静态文件中。
API_URLIndexedDB Orphaned Images
IndexedDB孤立图像
The app automatically cleans up orphaned image blobs on startup. If storage grows unexpectedly, use the in-app export → clear data → reimport workflow, or manually clear IndexedDB via DevTools → Application → IndexedDB.
应用会在启动时自动清理孤立的图像Blob。如果存储占用异常增长,可使用应用内的导出→清除数据→重新导入流程,或通过开发者工具→Application→IndexedDB手动清除。
Live Deployments
在线部署版本
| Version | URL |
|---|---|
| Vercel (HTTPS only) | https://gpt-image-playground.cooksleep.dev |
| GitHub Pages | https://cooksleep.github.io/gpt_image_playground |
| 版本 | 地址 |
|---|---|
| Vercel(仅HTTPS) | https://gpt-image-playground.cooksleep.dev |
| GitHub Pages | https://cooksleep.github.io/gpt_image_playground |