Contributing Guide
Relevant source files
The following files were used as context for generating this wiki page:This guide provides a technical overview of the Peinture (AI Image Gen) application for developers looking to contribute. It covers the project's architecture, key features, and development process. The application is a single-page application (SPA) built with React that allows users to generate images from text prompts using various AI models.
Contributions are welcome. The standard process is to fork the repository, create a feature branch, commit your changes, and open a pull request for review.
Sources: README.md:1-4, README.md:100-105
1. Getting Started
To set up a local development environment, you will need Node.js (v18 or higher) and a package manager like npm, yarn, or pnpm.
-
Clone the repository:
git clone https://github.com/Amery2010/peinture.git cd peinture -
Install dependencies:
npm install -
Start the development server:
npm run devThe application will be available at
http://localhost:5173by default, though the port is configured to3000invite.config.ts.
Sources: README.md:41-57, vite.config.ts:7-10
2. Project Structure
The repository is organized into several key files and directories. Understanding their roles is essential for making contributions.
| File / Directory | Description | Source File |
|---|---|---|
App.tsx | The main React component that manages application state, UI layout, and user interactions. | App.tsx |
services/hfService.ts | A service module responsible for all communication with external APIs, including image generation, upscaling, and prompt optimization. | services/hfService.ts |
components/ | Contains reusable React components like HistoryGallery, SettingsModal, CustomSelect, and ImageComparison. | components/HistoryGallery.tsx, components/SettingsModal.tsx, components/CustomSelect.tsx, components/ImageComparison.tsx |
types.ts | Defines core TypeScript interfaces and types used throughout the application, such as GeneratedImage and ModelOption. | types.ts |
index.html | The main HTML entry point for the SPA. It includes CDN links for dependencies and custom inline styles. | index.html |
translations.ts | Contains English and Chinese (中文) translation strings for internationalization (i18n). | translations.ts |
package.json | Lists project dependencies, scripts, and metadata. | package.json |
vite.config.ts | The configuration file for the Vite build tool. | vite.config.ts |
3. Application Architecture
The application follows a component-based architecture with a clear separation between the UI, services, and external APIs.
The following diagram illustrates the high-level architecture and data flow.
3.1. State Management
The primary state management is handled within the App.tsx component using React hooks (useState, useEffect). Key state variables include:
prompt: The user's text input.model: The selected AI model (z-image-turboorqwen-image-fast).history: An array ofGeneratedImageobjects, persisted inlocalStorage.currentImage: The currently selected or newly generated image.isLoading,isOptimizing,isUpscaling: Boolean flags to manage UI state during API calls.
Sources: App.tsx:68-80, App.tsx:128-131
3.2. Data Persistence
User data is stored exclusively in the browser's localStorage, ensuring user privacy as no backend database is required.
- Image History: Generated images are stored under the key
ai_image_gen_history. On application load, history items older than one day are filtered out. - Hugging Face Token: An optional user-provided token is stored under
huggingFaceTokenand used for API authentication. - Language Preference: The selected language ('en' or 'zh') is stored under
app_language.
Sources: App.tsx:91-103, App.tsx:113-115, components/SettingsModal.tsx:18-21
4. Key Feature Implementations
4.1. Image Generation
The image generation process is initiated from App.tsx and handled by hfService.ts, which communicates with Hugging Face Gradio APIs.
The flow is as follows:
- The user clicks the "Generate" button, triggering
handleGenerateinApp.tsx. handleGeneratecallsgenerateImageinhfService.ts, passing the prompt, model, and other parameters.generateImageacts as a router, calling eithergenerateZImageorgenerateQwenImagebased on the selected model.- The selected function makes an initial
POSTrequest to the model's Gradio API endpoint to queue the generation job and receives anevent_id. - A second
GETrequest is made to a polling endpoint with theevent_idto retrieve the result as a Server-Sent Events (SSE) stream. - The
extractCompleteEventDatafunction parses the SSE stream to find thecompleteevent, which contains the final image URL and metadata. - The result is returned to
App.tsx, where the state is updated to display the new image and add it to the history.
Sources: App.tsx:151-172, services/hfService.ts:167-175, services/hfService.ts:104-132, services/hfService.ts:134-165
This sequence diagram visualizes the image generation flow.
Sources: App.tsx:151-172, services/hfService.ts:104-165
4.2. Prompt Optimization
The application can enhance user prompts using an external AI service.
handleOptimizePromptinApp.tsxcallsoptimizePromptinhfService.ts.optimizePromptsends the user's prompt to thePOLLINATIONS_API_URL.- The request body includes a detailed system message instructing the
openai-fastmodel to rewrite the prompt for cinematic and hyper-realistic results, with a strict word count limit. - The refined prompt is extracted from the API response and used to update the
promptstate inApp.tsx.
Sources: App.tsx:243-254, services/hfService.ts:213-255
4.3. Image Upscaling (4x Resolution)
Generated images can be upscaled to 4x their original resolution.
- The
handleUpscalefunction inApp.tsxcalls theupscalerfunction inhfService.ts, providing the URL of the current image. - The
upscalerservice communicates with thetuan2308-upscalerGradio API on Hugging Face, following the same queue-and-poll pattern as image generation. - Upon receiving the upscaled image URL,
App.tsxenters a "comparison mode" by settingisComparingtotrueand storing the new URL intempUpscaledImage. - The
ImageComparisoncomponent is rendered, allowing the user to visually compare the original and upscaled images with a slider. - If the user accepts the change (
handleApplyUpscale), thecurrentImageobject is updated with the new URL andisUpscaled: true, and the history is updated.
Sources: App.tsx:191-206, App.tsx:208-224, services/hfService.ts:177-198, components/ImageComparison.tsx
4.4. Data Structures
The core data structure is GeneratedImage, which holds all information about a single creation.
Source: types.ts:3-13
| Field | Type | Description |
|---|---|---|
id | string | A unique identifier for the image, generated using crypto.randomUUID(). |
url | string | The URL of the generated image file. |
prompt | string | The text prompt used for generation. |
aspectRatio | string | The aspect ratio used (e.g., "1:1", "16:9"). |
timestamp | number | The Unix timestamp (milliseconds) when the image was created. |
model | string | The model used for generation (e.g., "z-image-turbo"). |
seed | number | The seed value used for generation. |
duration | number | The time taken for generation in seconds. |
isBlurred | boolean | A flag to toggle a blur effect, often for NSFW content. |
isUpscaled | boolean | A flag indicating if the image has been upscaled. |
Sources: types.ts:3-13, services/hfService.ts:124
5. Styling
The application uses Tailwind CSS for styling, loaded via a CDN. Customizations and additional styles are defined directly in index.html.
- Tailwind CDN: The project uses the Tailwind CSS CDN with the
formsandcontainer-queriesplugins enabled. - Custom Theme: A
tailwind.configobject is defined in a<script>tag to extend the default theme with custom colors (primary,background-dark) and fonts. - Custom CSS: A
<style>block contains utility classes like.scrollbar-hidefor the history gallery and.generate-button-gradientfor the main action button.
Sources: index.html:15, index.html:17-40, index.html:42-65
6. Conclusion
This guide provides a foundational understanding of the Peinture codebase. When contributing, focus on maintaining the existing architecture: UI logic and state should reside in App.tsx or be extracted into new components in the components/ directory, while all external API interactions should be encapsulated within services/hfService.ts. Ensure any new data structures are clearly defined in types.ts. By following these patterns, you can help maintain the project's clarity and scalability.