gpt-image-playground

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GPT 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 (
/v1/images
) or Responses API (
/v1/responses
). Features a responsive UI with waterfall task cards, IndexedDB local storage, PWA support, bulk selection, history management, and ZIP export/import.

ara.so开发的技能 —— 属于Daily 2026 Skills合集。
这是一款基于React 19 + TypeScript + Vite的Web应用,可通过OpenAI的Images API(
/v1/images
)或Responses API(
/v1/responses
)生成和编辑图像。具备响应式UI(含瀑布流任务卡片)、IndexedDB本地存储、PWA支持、批量选择、历史记录管理以及ZIP导出/导入功能。

What It Does

功能介绍

  • Text-to-image: Generate images from prompts via
    images/generations
    or
    responses
    with
    image_generation
    tool
  • Reference image editing: Upload up to 16 reference images, call
    images/edits
    or multimodal Responses API
  • 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张参考图,调用
    images/edits
    或多模态Responses API
  • 智能尺寸选择器:自动计算1K/2K/4K分辨率的常见宽高比;自定义尺寸自动对齐到16px倍数,最大边长3840px,最大宽高比3:1,像素范围655360–8294400
  • 参数控制:支持质量(低/中/高)、格式(PNG/JPEG/WebP)、压缩率、审核级别设置
  • 历史记录:基于IndexedDB存储,采用SHA-256图像去重,支持批量操作、收藏、搜索/筛选
  • 数据可移植性:ZIP导出/导入功能,包含原始图像文件 +
    manifest.json

Tech Stack

技术栈

LayerTechnology
FrameworkReact 19 + TypeScript
BuildVite
StylingTailwind CSS 3
StateZustand
StorageBrowser 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 dev
bash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playground
npm install
npm run dev
undefined
undefined

Environment Variables

环境变量

Create
.env.local
in the project root:
bash
undefined
在项目根目录创建
.env.local
文件:
bash
undefined

Optional: pre-fill default API URL at build time

可选:构建时预填充默认API地址

VITE_DEFAULT_API_URL=https://api.openai.com/v1
undefined
VITE_DEFAULT_API_URL=https://api.openai.com/v1
undefined

Build for Production

生产环境构建

bash
npm run build
bash
npm run build

Output in dist/ — deploy to any static file server

输出文件在dist/目录 —— 可部署到任意静态文件服务器

undefined
undefined

Docker

Docker部署

bash
undefined
bash
undefined

Single container

单容器运行

docker run -d -p 8080:80
-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

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 -d

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

更新至最新版本:
```bash
docker compose pull && docker compose up -d

Vercel One-Click Deploy

Vercel一键部署

Deploy with Vercel
Add environment variable in Settings → Environment Variables:
VITE_DEFAULT_API_URL=https://api.openai.com/v1

Deploy with Vercel
设置 → 环境变量中添加环境变量:
VITE_DEFAULT_API_URL=https://api.openai.com/v1

API Configuration

API配置

In-App Settings

应用内设置

Open settings (top-right gear icon) and configure:
FieldImages APIResponses API
Endpoint
/v1/images/generations
,
/v1/images/edits
/v1/responses
Model example
gpt-image-1
gpt-4o
,
gpt-5.5
打开右上角齿轮图标进入设置页面,配置以下内容:
字段Images APIResponses API
接口地址
/v1/images/generations
,
/v1/images/edits
/v1/responses
模型示例
gpt-image-1
gpt-4o
,
gpt-5.5

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-xxxx
Parameters:
  • apiUrl
    — API base URL
  • apiKey
    — OpenAI API key

通过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
参数说明:
  • apiUrl
    —— API基础地址
  • apiKey
    —— OpenAI API密钥

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.json
Edit
dev-proxy.config.json
:
json
{
  "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/generations
Note: Proxy only works with
npm run dev
. Not included in production builds.
Set
API URL
in the app settings to match
target
. The app rewrites matching requests to use the proxy prefix.
If
target
or
API URL
already includes
/v1
, the path won't be duplicated — requests become
/api-proxy/responses
not
/api-proxy/v1/responses
.

当API端点不允许浏览器跨域请求时,使用Vite开发代理:
bash
cp dev-proxy.config.example.json dev-proxy.config.json
编辑
dev-proxy.config.json
json
{
  "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 URL
以匹配
target
,应用会自动将匹配的请求重写为使用代理前缀。
如果
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 point
src/
├── 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_playground
bash
git clone https://github.com/CookSleep/gpt_image_playground.git
cd gpt_image_playground

Set default API

设置默认API

echo "VITE_DEFAULT_API_URL=https://your-proxy.com/v1" > .env.local
npm install && npm run dev
undefined
echo "VITE_DEFAULT_API_URL=https://your-proxy.com/v1" > .env.local
npm install && npm run dev
undefined

2. 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)
  • manifest.json
    with task records and image metadata
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
dev-proxy.config.json
(see above).
Fix (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
域名HTTPS要求

The
gpt-image-playground.cooksleep.dev
deployment requires all resources to be HTTPS. If your API is HTTP-only, use the GitHub Pages version:
https://cooksleep.github.io/gpt_image_playground
gpt-image-playground.cooksleep.dev
部署要求所有资源均为HTTPS。如果你的API仅支持HTTP,请使用GitHub Pages版本:
https://cooksleep.github.io/gpt_image_playground

Custom 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
sanitizeImageSize
function above to auto-correct.
确保尺寸通过验证:
  • 宽高均为16px的倍数
  • 任意边长不超过3840px
  • 宽高比≤3:1
  • 总像素在655,360至8,294,400之间
使用上述
sanitizeImageSize
函数自动修正。

Responses API vs Images API

Responses API vs Images API

ScenarioUse
Direct image generationImages API +
gpt-image-1
Codex CLI-derived APIsResponses API
Text model + image toolResponses API + text model (e.g.
gpt-4o
)
images/edits
for multi-image input
Images API only
场景推荐使用
直接图像生成Images API +
gpt-image-1
Codex CLI衍生APIResponses API
文本模型+图像工具Responses API + 文本模型(如
gpt-4o
多图像输入的
images/edits
仅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
API_URL
into the Nginx-served static files at startup.
确保在运行时传递环境变量(而非构建时):
bash
docker run -e API_URL=https://api.openai.com/v1 ...
容器的入口点会在启动时将
API_URL
注入Nginx提供的静态文件中。

IndexedDB 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

在线部署版本