├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README_zh.md ├── client ├── .gitignore ├── .vscode │ └── extensions.json ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── font │ │ ├── digitW.ttf │ │ └── logo2.otf │ ├── js │ │ └── ACsv.min.js │ └── loading.svg ├── src │ ├── App.js │ ├── AppRoot.vue │ ├── G.ts │ ├── Kit.ts │ ├── Lang.ts │ ├── assets │ │ ├── breakpoints.scss │ │ ├── language_strings.csv │ │ ├── language_support.csv │ │ ├── my-el.scss │ │ └── my.scss │ ├── d │ │ └── ACsv.d.ts │ ├── layouts │ │ ├── FrameBody.vue │ │ └── FrameHead.vue │ ├── main.js │ └── views │ │ ├── LPart.vue │ │ ├── RPartDallE.vue │ │ ├── RPartSD.vue │ │ └── Workspace.vue └── vite.config.js ├── intro ├── screenshot-1.png └── screenshot-2.png ├── server ├── app.py ├── comm.py ├── conf.json ├── downloads │ └── test.webp └── requirements.txt └── wwwroot ├── assets ├── index-47a4560e.js ├── index-4d8dcf98.css ├── vendor-b3440d5e.js └── vendor-f40fe51d.css ├── favicon.ico ├── font ├── digitW.ttf └── logo2.otf ├── index.html ├── js └── ACsv.min.js └── loading.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | __pycache__ 132 | conf.dev.json 133 | conf.prod.json 134 | /server/jobs.json 135 | /server/downloads/job-*.webp 136 | /server/downloads/job-*.jpg 137 | /server/downloads/job-*.png 138 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | COPY . /app 4 | 5 | WORKDIR /app 6 | 7 | RUN pip install -r server/requirements.txt 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 amin2312 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AI Image Generator 2 | ================ 3 | 4 | **[English](./README.md)** | **[中文](./README_zh.md)** 5 | 6 | **AI Image Generator** is a easy-to-operate image generator that provides users with a more convenient and efficient AI image generation solution. 7 | 8 | * It is based on **Dall·E** and **Stable Diffusion** API; 9 | * It supports multi languages; 10 | * It implements complete client(vue) and server(tornado) solution; 11 | 12 | Screenshots & Demo 13 | ---------------- 14 | 15 | ![AI Image Generator Screenshot 1](./intro/screenshot-1.png "Screenshot 1") 16 | ![AI Image Generator Screenshot 2](./intro/screenshot-2.png "Screenshot 2") 17 | 18 | DEMO: [www.ai-img-gen.com/gen-basic/](https://www.ai-img-gen.com/gen-basic/) (only show) 19 | Commercial: [www.ai-img-gen.com/gen-pro/](https://www.ai-img-gen.com/gen-pro/) (provide free trial) 20 | 21 | How to run it 22 | ---------------- 23 | 24 | **Step 1**. Install python >= 3.10 25 | 26 | **Step 2**. Install the project's python dependencies 27 | 28 | ```sh 29 | cd 30 | pip install -r server/requirements.txt 31 | ``` 32 | 33 | **Step 3**. Configure **Dall·E** or **Stable Diffusion** API key 34 | 35 | ```text 36 | Use text editor to open /server/conf.json, 37 | and fill in the "api_key" field by your needs, 38 | remember to save it. 39 | ``` 40 | 41 | **Step 4**. Start the python server 42 | 43 | ```sh 44 | cd 45 | python server/app.py 46 | ``` 47 | 48 | **Step 5**. Open browser, visit **** 49 | 50 | Official site 51 | ---------------- 52 | 53 | * [www.ai-img-gen.com](https://www.ai-img-gen.com/) 54 | 55 | Directory Structure 56 | ---------------- 57 | 58 | ```text 59 | root 60 | ├─ client // Vue client project 61 | ├─ src 62 | ├─ assets 63 | ├─ language_strings.csv // Add new translation using the CSV editor 64 | ├─ language_support.csv // Add new language using the CSV editor 65 | ├─ intro // Related files introduced 66 | ├─ server // Python server project 67 | └─ wwwroot // Server web root directory | Vue build output directory 68 | ``` 69 | 70 | Others 71 | ---------------- 72 | 73 | ***⭐ If you like this project, please add a star ⭐*** 74 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | AI图片生成器(AI Image Generator) 2 | ================ 3 | 4 | **[English](./README.md)** | **[中文](./README_zh.md)** 5 | 6 | **AI图片生成器** 是一款易于操作的图片生成器,为用户提供更加便捷、高效的AI图片生成解决方案。 7 | 8 | * 它是基于 **Dall·E** 和 **Stable Diffusion** API; 9 | * 它支持多种语言; 10 | * 它实现了完整的客户端(vue)和服务器(tornado)的解决方案; 11 | 12 | 截图与演示 13 | ---------------- 14 | 15 | ![AI Image Generator Screenshot 1](./intro/screenshot-1.png "Screenshot 1") 16 | ![AI Image Generator Screenshot 2](./intro/screenshot-2.png "Screenshot 2") 17 | 18 | 演示:[www.ai-img-gen.com/gen-basic/](https://www.ai-img-gen.com/gen-basic/) (仅展示) 19 | 商业版:[www.ai-img-gen.com/gen-pro/](https://www.ai-img-gen.com/gen-pro/) (提供免费试用) 20 | 21 | 如何运行它 22 | ---------------- 23 | 24 | **步骤1**. 安装 python >= 3.10 25 | 26 | **步骤2**. 安装项目的 python 依赖项 27 | 28 | ```sh 29 | cd <项目路径> 30 | pip install -r server/requirements.txt 31 | ``` 32 | 33 | **步骤3**. 配置 **Dall·E** 或 **Stable Diffusion** 的API密钥 34 | 35 | ```text 36 | 使用文本编辑器打开 <项目路径>/server/conf.json, 37 | 根据需要填写"api_key"字段(填写"api_proxy"字段可加入代理以解决部分地区的调用限制), 38 | 记得保存。 39 | ``` 40 | 41 | **步骤4**. 启动 python 服务器 42 | 43 | ```sh 44 | cd <项目路径> 45 | python server/app.py 46 | ``` 47 | 48 | **步骤5**. 打开浏览器, 访问 **** 49 | 50 | 官方网站 51 | ---------------- 52 | 53 | * [www.ai-img-gen.com](https://www.ai-img-gen.com/) 54 | 55 | 目录结构 56 | ---------------- 57 | 58 | ```text 59 | root 60 | ├─ client // Vue 客户端项目 61 | ├─ src 62 | ├─ assets 63 | ├─ language_strings.csv // 使用 CSV 编辑器添加新翻译 64 | ├─ language_support.csv // 使用 CSV 编辑器添加新语言 65 | ├─ intro // 与介绍相关的文件 66 | ├─ server // Python 服务器项目 67 | └─ wwwroot // 服务器 Web 根目录 | Vue 构建输出目录 68 | ``` 69 | 70 | 其他 71 | ---------------- 72 | 73 | ***⭐ 如果您喜欢这个项目,请加一颗星 ⭐*** 74 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | *.suo 22 | *.ntvs* 23 | *.njsproj 24 | *.sln 25 | *.sw? 26 | -------------------------------------------------------------------------------- /client/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | ## Debug command 4 | 5 | ```sh 6 | npm run dev 7 | ``` 8 | 9 | ## Build command 10 | 11 | ```sh 12 | npm run build 13 | ``` 14 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | AI Image Generator - unleash your Creativity 13 | 19 | 22 | 23 | 24 | 25 |
Loding
26 |
27 | 28 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview --port 4173" 8 | }, 9 | "dependencies": { 10 | "@icon-park/vue-next": "^1.4.2", 11 | "element-plus": "^2.2.28", 12 | "mobile-device-detect": "^0.4.3", 13 | "vue": "^3.2.37" 14 | }, 15 | "devDependencies": { 16 | "@vitejs/plugin-vue": "^4.0.0", 17 | "sass": "^1.55.0", 18 | "sass-loader": "^13.1.0", 19 | "unplugin-auto-import": "^0.15.3", 20 | "unplugin-vue-components": "^0.22.9", 21 | "vite": "^4.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/font/digitW.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/client/public/font/digitW.ttf -------------------------------------------------------------------------------- /client/public/font/logo2.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/client/public/font/logo2.otf -------------------------------------------------------------------------------- /client/public/js/ACsv.min.js: -------------------------------------------------------------------------------- 1 | (function(e){"use strict";e["acsv"]=e["acsv"]||{};var g=function(){};g.replace=function(e,t,r){return e.split(t).join(r)};var x=e["acsv"]["Field"]=function(){};var A=e["acsv"]["Table"]=function(){this._selector=null;this._indexSet={};this.body=[];this.head=[];this.content=null};A.Parse=function(e,t,r){if(r==null){r='"'}if(t==null){t=","}var i=A.arrayToRows(A.textToArray(e,t,r));i.content=e;return i};A.textToArray=function(e,t,r){if(r==null){r='"'}if(t==null){t=","}if(e.charCodeAt(0)==65279){e=e.substring(1)}var i=r+r;var l=[];var n=e.length;var s=e;var a=0;while(true){var h=n-a;var o=0;var u=0;var v=[];var f=null;var c=null;while(u=n){break}}return l};A.arrayToRows=function(e){var t=e.shift();var r=e;var i=[];var l=0;var n=t.length;while(lv){o=true}else if(t==1&&u0&&A.JSON_TYPES.indexOf(s)!=-1){if(a!=null){h=JSON.parse(a)}}else{h=a}t.push(h)}return t},fmtObj:function(e){var t={};var r=0;var i=this.head.length;while(r0&&A.JSON_TYPES.indexOf(a)!=-1){if(h!=null){o=JSON.parse(h)}}else{o=h}t[s]=o}return t},toFirstRow:function(){var e=null;if(this._selector!=null&&this._selector.length>0){e=this.fmtRow(this._selector[0])}this._selector=null;return e},toLastRow:function(){var e=null;if(this._selector!=null){var t=this._selector.length;if(t>0){e=this.fmtRow(this._selector[t-1])}}this._selector=null;return e},toRows:function(){if(this._selector==null){return null}var e=[];var t=0;var r=this._selector.length;while(t0){e=this.fmtObj(this._selector[0])}this._selector=null;return e},toLastObj:function(){var e=null;if(this._selector!=null){var t=this._selector.length;if(t>0){e=this.fmtObj(this._selector[t-1])}}this._selector=null;return e},toObjs:function(){if(this._selector==null){return null}var e=[];var t=0;var r=this._selector.length;while(t=0&&sr||t&&u==r){n.push(o);--e;if(e==0){break}}}this._selector=n;return this},selectWhenL:function(e,t,r,i){if(i==null){i=0}var l=this._selector;if(l==null){l=this.body}var n=[];var s=0;var a=l.length;while(si||t&&f==i;var _=fl||r&&f==l;if(c||_){a.push(v);--e;if(e==0){break}}}this._selector=a;return this}};A.JSON_TYPES=["json","strings"]})(typeof exports!="undefined"?exports:typeof window!="undefined"?window:typeof self!="undefined"?self:this); -------------------------------------------------------------------------------- /client/public/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import * as IconPark from '@icon-park/vue-next'; 3 | 4 | import '@icon-park/vue-next/styles/index.css'; 5 | import 'element-plus/dist/index.css'; 6 | import './assets/my-el.scss'; 7 | import "./assets/my.scss"; 8 | 9 | export var MyApp = null; 10 | export var MyAppRoot = null; 11 | export function BuildApp(rootCompoent) 12 | { 13 | MyApp = createApp(rootCompoent); 14 | return MyApp; 15 | } 16 | export function MountApp(ins, id) 17 | { 18 | // import same icons 19 | ins.component('icon-info', IconPark.Info); 20 | ins.component('icon-close-one', IconPark.CloseOne); 21 | ins.component('icon-help', IconPark.Help); 22 | ins.component('icon-delete', IconPark.Delete); 23 | ins.component('icon-copy', IconPark.Copy); 24 | ins.component('icon-refresh', IconPark.Refresh); 25 | ins.component('icon-fill', IconPark.BackgroundColor); 26 | MyAppRoot = ins.mount(id); 27 | } -------------------------------------------------------------------------------- /client/src/AppRoot.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | 32 | 51 | -------------------------------------------------------------------------------- /client/src/G.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import { reactive } from 'vue'; 4 | import { ElLoading } from 'element-plus'; 5 | 6 | const SETTINGS_ITEM_NAME = 'settings'; 7 | const JOBS_ITEM_NAME = 'jobs'; 8 | const IS_DEBUG: boolean = (import.meta as any).env.DEV; 9 | /** 10 | * Conf Declaration. 11 | */ 12 | export const Conf: any = { 13 | "concurrentRequests": 5, // HTTP 1.x: 6, HTTP 2: 100 - Please reserve 1 for other requests 14 | "api": { 15 | "create": "/gen-basic/api/create", 16 | "retrieve": "/gen-basic/api/retrieve" 17 | } 18 | }; 19 | /** 20 | * Global Declaration. 21 | */ 22 | enum Color 23 | { 24 | red, 25 | yellow, 26 | blue 27 | } 28 | 29 | class Global 30 | { 31 | // consts 32 | //API_HOST = `//127.0.0.1:3001`; // for test 33 | API_HOST = ''; 34 | MODELS = ['dall-e', 'stable-diffusion']; 35 | // variables 36 | loading = null; 37 | url: URL = null; 38 | uid = 0; 39 | isDebug = IS_DEBUG; 40 | } 41 | export var G = new Global(); 42 | /** 43 | * Global Bind Declaration. 44 | */ 45 | interface GlobalBind 46 | { 47 | settings: any; 48 | jobs: Array; 49 | 50 | isTabletView: boolean; 51 | isPhoneView: boolean; 52 | isTouchDevice: boolean; 53 | } 54 | export var GB: GlobalBind = reactive({ 55 | settings: { 56 | runTimes: 0, 57 | }, 58 | jobs: [], 59 | 60 | isTabletView: false, 61 | isPhoneView: false, 62 | isTouchDevice: false, 63 | }); 64 | 65 | /** 66 | * Show loading box. 67 | */ 68 | export function ShowLoading(v: boolean, label: string = 'Loading') 69 | { 70 | if (G.loading) 71 | { 72 | G.loading.close(); 73 | G.loading = null; 74 | } 75 | if (v) 76 | { 77 | G.loading = ElLoading.service({ 78 | lock: true, 79 | text: label, 80 | background: 'rgba(0, 0, 0, 0.75)', 81 | }); 82 | } 83 | }; 84 | /** 85 | * Save settings. 86 | */ 87 | export function SaveSettings() 88 | { 89 | try 90 | { 91 | localStorage.setItem(SETTINGS_ITEM_NAME, JSON.stringify(GB.settings)); 92 | } 93 | catch (e) 94 | { 95 | var estr = e.toString(); 96 | if (estr.indexOf('QuotaExceededError') != -1) 97 | alert("Warning: Browser local storage is full, no more data can be saved"); 98 | else 99 | alert(e); 100 | } 101 | } 102 | /** 103 | * Load settings. 104 | */ 105 | export function LoadSettings() 106 | { 107 | var jobj = null; 108 | try 109 | { 110 | jobj = JSON.parse(localStorage.getItem(SETTINGS_ITEM_NAME)); 111 | } 112 | catch { } 113 | return reactive(jobj || {}); 114 | } 115 | /** 116 | * Save jobs. 117 | */ 118 | export function SaveJobs() 119 | { 120 | try 121 | { 122 | localStorage.setItem(JOBS_ITEM_NAME, JSON.stringify(GB.jobs)); 123 | } 124 | catch (e) 125 | { 126 | var estr = e.toString(); 127 | if (estr.indexOf('QuotaExceededError') != -1) 128 | alert("Warning: Browser local storage is full, no more images can be saved"); 129 | else 130 | alert(e); 131 | } 132 | } 133 | /** 134 | * Load jobs. 135 | */ 136 | export function LoadJobs() 137 | { 138 | var jarr = null; 139 | try 140 | { 141 | jarr = JSON.parse(localStorage.getItem(JOBS_ITEM_NAME)); 142 | } 143 | catch { } 144 | return reactive(jarr || []); 145 | } -------------------------------------------------------------------------------- /client/src/Kit.ts: -------------------------------------------------------------------------------- 1 | import Lang from './Lang'; 2 | import { G, GB } from './G'; 3 | /** 4 | * Kit. 5 | */ 6 | export default class Kit 7 | { 8 | static lastError = null; 9 | static checkP3(httpStatusCode, httpReponse) 10 | { 11 | if (httpStatusCode != 200) 12 | { 13 | Kit.lastError = httpStatusCode + ',' + httpReponse; 14 | return new Error(`[ERROR] HTTP:${ Kit.lastError }`); 15 | } 16 | var content = httpReponse; 17 | var index1 = content.indexOf(','); 18 | if (index1 == -1) 19 | { 20 | Kit.lastError = content; 21 | return new Error(`[ERROR] CONTENT:${ Kit.lastError }`); 22 | } 23 | var part1 = null; 24 | var part2 = null; 25 | var part3 = null; 26 | part1 = content.substring(0, index1); 27 | var index2 = content.indexOf(',', index1 + 1); 28 | if (index2 > 0) 29 | { 30 | part2 = content.substring(index1 + 1, index2); 31 | part3 = content.substring(index2 + 1); 32 | part1 = parseInt(part1); 33 | part2 = parseInt(part2); 34 | switch (part2) 35 | { 36 | case 10: 37 | part3 = parseInt(part3); 38 | break; 39 | case 11: 40 | part3 = parseInt(part3) ? true : false; 41 | break; 42 | case 20: 43 | part3 = parseFloat(part3); 44 | break; 45 | case 32: 46 | part3 = JSON.parse(part3); 47 | break; 48 | } 49 | } 50 | return { part1, part2, part3 }; 51 | } 52 | /** 53 | * Get the items by condition. 54 | */ 55 | static getStateItems(src: Array, state, n = -1, key: any = null): Array 56 | { 57 | var items: Array = []; 58 | for (let item of src) 59 | if (item.state == state) 60 | { 61 | items.push(key ? item[key] : item); 62 | n--; 63 | if (n == 0) 64 | { 65 | break; 66 | } 67 | } 68 | return items; 69 | } 70 | /** 71 | * Format input info. 72 | */ 73 | static formatInputInfo(I: any): string 74 | { 75 | var info = ''; 76 | if (I.model == G.MODELS[0]) 77 | { 78 | info = `${ Lang.n.prompt }: ${ I.prompt } 79 | ${ Lang.n.model }: ${ I.model } 80 | ${ Lang.n.version }: ${ I.version } 81 | ${ Lang.n.size }: ${ I.param.size } 82 | ${ Lang.n.quality }: ${ I.param.quality } 83 | ${ Lang.n.style }: ${ I.param.style } 84 | ${ Lang.n.supplier }: ${ I.supplier } 85 | `; 86 | } 87 | else if (I.model == G.MODELS[1]) 88 | { 89 | info = `${ Lang.n.prompt }: ${ I.prompt } 90 | ${ Lang.n.negativePrompt }: ${ I.negativePrompt } 91 | ${ Lang.n.model }: ${ I.model } 92 | ${ Lang.n.version }: ${ I.version } 93 | ${ Lang.n.width }: ${ I.param.width } 94 | ${ Lang.n.height }: ${ I.param.height } 95 | CFG Scale: ${ I.param.cfg_scale } 96 | ${ Lang.n.steps }: ${ I.param.steps } 97 | CLIP Guidance: ${ I.param.clip_guidance_preset } 98 | ${ Lang.n.sampler }: ${ I.param.sampler } 99 | ${ Lang.n.style }: ${ I.param.style_preset } 100 | ${ Lang.n.seed }: ${ I.param.seed } 101 | ${ Lang.n.supplier }: ${ I.supplier } 102 | `; 103 | } 104 | return info; 105 | } 106 | /** 107 | * Fill the blank values of A with B. 108 | */ 109 | static fillInto(b: any, a: any, cover = false, plus = false): void 110 | { 111 | if (b == null || a == null) 112 | { 113 | return; 114 | } 115 | for (let key in b) 116 | { 117 | let val = a[key]; 118 | if (cover || val == null) 119 | { 120 | a[key] = b[key]; 121 | } 122 | else if (plus) 123 | { 124 | a[key] += b[key]; 125 | } 126 | } 127 | } 128 | /** 129 | * Generate a uid. 130 | */ 131 | static genUID(randomLength = 8, prefix: string = '') 132 | { 133 | return prefix + Kit.padStart(Math.random().toString(36).substring(2, 2 + randomLength), '0', randomLength) + '-' + Date.now().toString(36); 134 | } 135 | /** 136 | * Padding start. 137 | */ 138 | static padStart(s: string, chr: string, n: number) 139 | { 140 | const c = n - s.length; 141 | if (c > 0) 142 | { 143 | s = Array(c).fill(chr).join('') + s; 144 | } 145 | return s; 146 | } 147 | /** 148 | * Copy text to clipboard. 149 | */ 150 | static copyTextToClipboard(input, { target = document.body } = {}) 151 | { 152 | const element = document.createElement('textarea'); 153 | const previouslyFocusedElement = document.activeElement; 154 | element.value = input; 155 | // Prevent keyboard from showing on mobile 156 | element.setAttribute('readonly', ''); 157 | element.style.contain = 'strict'; 158 | element.style.position = 'absolute'; 159 | element.style.left = '-9999px'; 160 | element.style.fontSize = '12pt'; // Prevent zooming on iOS 161 | const selection: any = document.getSelection(); 162 | const originalRange = selection.rangeCount > 0 && selection.getRangeAt(0); 163 | target.append(element); 164 | element.select(); 165 | // Explicit selection workaround for iOS 166 | element.selectionStart = 0; 167 | element.selectionEnd = input.length; 168 | let isSuccess = false; 169 | try 170 | { 171 | isSuccess = document.execCommand('copy'); 172 | } 173 | catch { } 174 | element.remove(); 175 | if (originalRange) 176 | { 177 | selection.removeAllRanges(); 178 | selection.addRange(originalRange); 179 | } 180 | // Get the focus back on the previously focused element, if any 181 | if (previouslyFocusedElement) 182 | { 183 | (previouslyFocusedElement as HTMLElement).focus(); 184 | } 185 | return isSuccess; 186 | } 187 | } -------------------------------------------------------------------------------- /client/src/Lang.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /** 3 | * I18N Language. 4 | */ 5 | export default class Lang 6 | { 7 | static _support: acsv.Table; 8 | static _strings: acsv.Table; 9 | static _customValue: string; 10 | static customLabel: string | null; 11 | static alignment: string; 12 | /** 13 | * Init. 14 | */ 15 | static Init(support, strings): void 16 | { 17 | Lang._support = support; 18 | Lang._strings = strings; 19 | Lang._strings.createIndexAt(0); // for id field 20 | Lang._strings.createIndexAt(1); // for name field 21 | } 22 | /** 23 | * Get system definition. 24 | */ 25 | static get systemValue(): string 26 | { 27 | return Lang.getLangFieldBy(navigator.language); 28 | } 29 | /** 30 | * Get custom definition. 31 | */ 32 | static get customValue(): string 33 | { 34 | return Lang._customValue; 35 | } 36 | static set customValue(v: string) 37 | { 38 | var rowLang: string | null = null; 39 | var rowName: string | null = null; 40 | var rows = Lang._support.selectAll().toObjs(); 41 | var row: { 42 | name: string, langField: string, iso_639_3166: string; 43 | }; 44 | for (let i = 0, n = rows.length; i < n; i++) 45 | { 46 | row = rows[i]; 47 | if (row.langField == v) 48 | { 49 | rowLang = v; 50 | rowName = row.name; 51 | break; 52 | } 53 | } 54 | if (rowLang == v) 55 | { 56 | Lang._customValue = v; 57 | Lang.customLabel = rowName; 58 | } 59 | else 60 | { 61 | Lang._customValue = Lang.systemValue; 62 | } 63 | if (v == 'ar') 64 | { 65 | Lang.alignment = 'right'; 66 | } 67 | else 68 | { 69 | Lang.alignment = 'left'; 70 | } 71 | } 72 | /** 73 | * Get lang field by iso-639-3166. 74 | */ 75 | static getLangFieldBy(iso_639_3166: string): string 76 | { 77 | var rows = Lang._support.selectAll().toObjs(); 78 | var row: { 79 | name: string, langField: string, iso_639_3166: string; 80 | }; 81 | for (let i = 0, n = rows.length; i < n; i++) 82 | { 83 | row = rows[i]; 84 | if (row.iso_639_3166.indexOf(iso_639_3166) != -1) 85 | { 86 | return row.langField; 87 | } 88 | } 89 | var iso639 = iso_639_3166.split("-")[0]; 90 | for (let i = 0, n = rows.length; i < n; i++) 91 | { 92 | row = rows[i]; 93 | if (row.iso_639_3166.indexOf(iso639) != -1) 94 | { 95 | return row.langField; 96 | } 97 | } 98 | return rows[0].langField; 99 | } 100 | /** 101 | * Get the language label by id. 102 | */ 103 | static id(value: any, index: number = 0): string 104 | { 105 | var obj = Lang._strings.selectWhenE(1, value, index).toFirstObj(); 106 | if (obj == null) 107 | { 108 | console.log('error language id:' + value); 109 | return null; 110 | } 111 | var string: string = obj[Lang.customValue]; 112 | if (string == null || string == '' || string == '&') 113 | { 114 | string = obj['en']; 115 | } 116 | return string; 117 | } 118 | /** 119 | * Get the language label by name. 120 | */ 121 | static n: any = new Proxy({}, { 122 | get: function (obj, prop) 123 | { 124 | return Lang.id(prop, 1); 125 | } 126 | }); 127 | } -------------------------------------------------------------------------------- /client/src/assets/breakpoints.scss: -------------------------------------------------------------------------------- 1 | $grid-breakpoints: ( 2 | xs: 0, 3 | sm: 576px, 4 | md: 768px, 5 | lg: 992px, 6 | xl: 1200px, 7 | xxl: 1400px 8 | ) !default; 9 | // Breakpoint viewport sizes and media queries. 10 | // 11 | // Breakpoints are defined as a map of (name: minimum width), order from small to large: 12 | // 13 | // (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px) 14 | // 15 | // The map defined in the `$grid-breakpoints` global variable is used as the `$breakpoints` argument by default. 16 | 17 | // Name of the next breakpoint, or null for the last breakpoint. 18 | // 19 | // >> breakpoint-next(sm) 20 | // md 21 | // >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) 22 | // md 23 | // >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl xxl)) 24 | // md 25 | @function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) { 26 | $n: index($breakpoint-names, $name); 27 | @if not $n { 28 | @error "breakpoint `#{$name}` not found in `#{$breakpoints}`"; 29 | } 30 | @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null); 31 | } 32 | 33 | // Minimum breakpoint width. Null for the smallest (first) breakpoint. 34 | // 35 | // >> breakpoint-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) 36 | // 576px 37 | @function breakpoint-min($name, $breakpoints: $grid-breakpoints) { 38 | $min: map-get($breakpoints, $name); 39 | @return if($min != 0, $min, null); 40 | } 41 | 42 | // Maximum breakpoint width. 43 | // The maximum value is reduced by 0.02px to work around the limitations of 44 | // `min-` and `max-` prefixes and viewports with fractional widths. 45 | // See https://www.w3.org/TR/mediaqueries-4/#mq-min-max 46 | // Uses 0.02px rather than 0.01px to work around a current rounding bug in Safari. 47 | // See https://bugs.webkit.org/show_bug.cgi?id=178261 48 | // 49 | // >> breakpoint-max(md, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) 50 | // 767.98px 51 | @function breakpoint-max($name, $breakpoints: $grid-breakpoints) { 52 | $max: map-get($breakpoints, $name); 53 | @return if($max and $max > 0, $max - .02, null); 54 | } 55 | 56 | // Returns a blank string if smallest breakpoint, otherwise returns the name with a dash in front. 57 | // Useful for making responsive utilities. 58 | // 59 | // >> breakpoint-infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) 60 | // "" (Returns a blank string) 61 | // >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px)) 62 | // "-sm" 63 | @function breakpoint-infix($name, $breakpoints: $grid-breakpoints) { 64 | @return if(breakpoint-min($name, $breakpoints) == null, "", "-#{$name}"); 65 | } 66 | 67 | // Media of at least the minimum breakpoint width. No query for the smallest breakpoint. 68 | // Makes the @content apply to the given breakpoint and wider. 69 | @mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) { 70 | $min: breakpoint-min($name, $breakpoints); 71 | @if $min { 72 | @media (min-width: $min) { 73 | @content; 74 | } 75 | } @else { 76 | @content; 77 | } 78 | } 79 | 80 | // Media of at most the maximum breakpoint width. No query for the largest breakpoint. 81 | // Makes the @content apply to the given breakpoint and narrower. 82 | @mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) { 83 | $max: breakpoint-max($name, $breakpoints); 84 | @if $max { 85 | @media (max-width: $max) { 86 | @content; 87 | } 88 | } @else { 89 | @content; 90 | } 91 | } 92 | 93 | // Media that spans multiple breakpoint widths. 94 | // Makes the @content apply between the min and max breakpoints 95 | @mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) { 96 | $min: breakpoint-min($lower, $breakpoints); 97 | $max: breakpoint-max($upper, $breakpoints); 98 | 99 | @if $min != null and $max != null { 100 | @media (min-width: $min) and (max-width: $max) { 101 | @content; 102 | } 103 | } @else if $max == null { 104 | @include media-breakpoint-up($lower, $breakpoints) { 105 | @content; 106 | } 107 | } @else if $min == null { 108 | @include media-breakpoint-down($upper, $breakpoints) { 109 | @content; 110 | } 111 | } 112 | } 113 | 114 | // Media between the breakpoint's minimum and maximum widths. 115 | // No minimum for the smallest breakpoint, and no maximum for the largest one. 116 | // Makes the @content apply only to the given breakpoint, not viewports any wider or narrower. 117 | @mixin media-breakpoint-only($name, $breakpoints: $grid-breakpoints) { 118 | $min: breakpoint-min($name, $breakpoints); 119 | $next: breakpoint-next($name, $breakpoints); 120 | $max: breakpoint-max($next, $breakpoints); 121 | 122 | @if $min != null and $max != null { 123 | @media (min-width: $min) and (max-width: $max) { 124 | @content; 125 | } 126 | } @else if $max == null { 127 | @include media-breakpoint-up($name, $breakpoints) { 128 | @content; 129 | } 130 | } @else if $min == null { 131 | @include media-breakpoint-down($next, $breakpoints) { 132 | @content; 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /client/src/assets/language_strings.csv: -------------------------------------------------------------------------------- 1 | id:int,name:string,en:string,zh:string,zh-Hant:string,es:string,pt:string,fr:string,tr:string,ru:string,ind:string,ja:string 2 | 101,logo,AI Image Generator,AI图片生成,AI繪圖生成,Generador de Imágenes de IA,Gerador de Imagem de IA,Générateur d'Images IA,Yapay Zeka Görüntü Oluşturucusu,ИИ-генератор изображений,Generator Gambar AI,AI画像生成 3 | 102,prompt,prompt,提示词,提示詞,,,,,,, 4 | 103,,Drawing..,绘图中..,繪圖中..,,,,,,, 5 | 104,,Please wait for current request to complete,请等待当前请求完成,請等待目前請求完成,,,,,,, 6 | 105,version,version,版本,版本,,,,,,, 7 | 106,size,size,尺寸,尺寸,,,,,,, 8 | 107,,square,方块,方塊,,,,,,, 9 | 108,,banner,横幅,橫幅,,,,,,, 10 | 109,,poster,海报,海報,,,,,,, 11 | 110,quality,quality,画质,畫質,,,,,,, 12 | 111,,standard ,标准,標準,,,,,,, 13 | 112,,HD,高清,高清,,,,,,, 14 | 113,style,style,风格,風格,,,,,,, 15 | 114,,vivid,生动,生動,,,,,,, 16 | 115,,natural,真实,真實,,,,,,, 17 | 116,supplier,supplier,供应商,供應商,,,,,,, 18 | 117,apiKey,API Key,API密钥,API金鑰,,,,,,, 19 | 118,clear,Clear,清空,清空,,,,,,, 20 | 119,generate,Generate,生成,生成,,,,,,, 21 | 120,,optional,可选,可選,,,,,,, 22 | 121,,Please input your prompt,请输入您的提示词,請輸入您的提示詞,,,,,,, 23 | 122,home,Home,主页,主页,,,,,,, 24 | 123,delete,Delete,删除,刪除,,,,,,, 25 | 124,,Are you sure you want to delete this image?,您确定要删除该图片吗?,您確定要刪除該圖片嗎?,,,,,,, 26 | 125,,Operation successful,操作成功,操作成功,,,,,,, 27 | 126,date,Date,日期,日期,,,,,,, 28 | 127,apiVer,API Ver,API版本,API版本,,,,,,, 29 | 128,endpoint,endpoint,,,,,,,,, 30 | 129,,Please fill in required fields,请填写必填字段,請填寫必填字段,,,,,,, 31 | 130,,required,必填,必填,,,,,,, 32 | 131,imgInfo,Image info,图片信息,圖片訊息,,,,,,, 33 | 132,inputInfo,Generated info,生成信息,生成訊息,,,,,,, 34 | 133,outputInfo,Raw response,原始响应,原始回應,,,,,,, 35 | 134,imgInfo1,Image,图片,圖片,,,,,,, 36 | 135,inputInfo1,Gen,生成,生成,,,,,,, 37 | 136,outputInfo1,Raw,原始,原始,,,,,,, 38 | 137,model,model,模型,模型,,,,,,, 39 | 138,elapsed,Elapsed,耗时,耗時,,,,,,, 40 | 139,width,width,宽度,寬度,,,,,,, 41 | 140,height,height,高度,高度,,,,,,, 42 | 141,sampler,sampler,采样器,採樣器,,,,,,, 43 | 143,seed,seed,种子,種子,,,,,,, 44 | 144,steps,steps,步数,步數,,,,,,, 45 | 145,steps2,Number of diffusion steps to run,要运行的扩散步数,要運行的擴散步數,,,,,,, 46 | 146,negativePrompt,negative prompt,否定提示词,否定提示詞,,,,,,, 47 | 147,,(optional) negative prompt,(可选) 否定提示词,(可選) 否定提示詞,,,,,,, 48 | 148,,"Exceeded max number of tasks, 49 | Please try again later.","超过最大任务数, 50 | 请稍后再试。","超過最大任務數, 51 | 請稍後再試。",,,,,,, 52 | 149,,Delete this task,删除此任务,刪除此任務,,,,,,, 53 | 150,,Refresh this task status,刷新此任务状态,重新整理此任務狀態,,,,,,, 54 | 151,,Are you sure you want to delete this task?,您确定要删除此任务吗?,您確定要刪除此任務嗎?,,,,,,, 55 | 152,,Refreshing..,刷新中..,刷新中..,,,,,,, 56 | 153,,This content violates the usage policy,该内容违反了使用政策,該內容違反了使用政策,,,,,,, 57 | 154,,The current version does not support this generated information,当前版本不支持该生成信息,目前版本不支援該生成訊息,,,,,,, 58 | 155,,Unable to generate content,无法生成内容,無法生成內容,,,,,,, 59 | 156,job,Task,任务,任務,,,,,,, 60 | 157,,"Your request has been added to the task, 61 | Please wait for the drawing to be completed.",您的请求已添加到任务中,请等待绘图完成。,您的請求已添加到任務中,請等待繪圖完成。,,,,,,, 62 | 158,,"Free drawing tasks are full, please try again later.",免费的绘图任务已满,请稍后再试。,免費的繪圖任務已滿,請稍後再試。,"Las tareas de dibujo gratuitas están completas, inténtalo de nuevo más tarde.",As tarefas de desenho gratuitas estão completas. Tente novamente mais tarde.,"Les tâches de dessin gratuites sont terminées, veuillez réessayer plus tard.","Ücretsiz çizim görevleri doldu, lütfen daha sonra tekrar deneyin.","Бесплатные задания по рисованию заполнены, повторите попытку позже.","Tugas menggambar gratis sudah penuh, silakan coba lagi nanti.",無料の描画タスクは満員です、後で再度お試しください。 63 | 159,,"When it is empty, the submission is a free drawing task.",当它为空时,提交的是免费的绘图任务,當它為空時,提交的是免費的繪圖任務,"Cuando está vacío, el envío es una tarea de dibujo gratuita.","Quando estiver vazio, o envio é uma tarefa de desenho livre.","Lorsqu'il est vide, la soumission est une tâche de dessin gratuite.","Boş olduğunda, gönderim ücretsiz bir çizim görevidir.","Если он пуст, это означает, что отправка представляет собой бесплатное задание по рисованию.","Jika kosong, penyerahannya adalah tugas menggambar gratis.",空の場合、提出は無料の描画タスクです。 64 | 160,,Cancel,取消,取消,,,,,,, 65 | 161,,Find it,搜索它,搜索它,Encuéntralo,Encontre,Trouve le,Bul onu,Найди это,Temukan,それを見つける 66 | -------------------------------------------------------------------------------- /client/src/assets/language_support.csv: -------------------------------------------------------------------------------- 1 | name:string,iso_639_3166:strings,langField:string 2 | english,en,en 3 | español,es,es 4 | portugués,pt,pt 5 | français,fr,fr 6 | Türkçe,tr,tr 7 | русский,ru,ru 8 | Indonesia,id,ind 9 | 简体中文,zh,zh 10 | 繁體中文,"zh-Hant,zh-TW,zh-HK,zh-MO",zh-Hant 11 | 日本語,ja,ja 12 | -------------------------------------------------------------------------------- /client/src/assets/my-el.scss: -------------------------------------------------------------------------------- 1 | .el-button+.el-button { 2 | margin-left: 4px; 3 | } 4 | 5 | .el-dropdown { 6 | padding: 0px 4px; 7 | } 8 | 9 | .el-dialog { 10 | --el-dialog-content-font-size: medium !important; 11 | } 12 | 13 | .el-dialog__header { 14 | background-color: #eee; 15 | margin-right: 0px !important; 16 | padding-top: 12px !important; 17 | padding-bottom: 12px !important; 18 | } 19 | 20 | .el-dialog__headerbtn { 21 | top: 2px !important; 22 | width: 48px !important; 23 | height: 48px !important; 24 | } 25 | 26 | .el-dialog__footer { 27 | padding-top: 0px !important; 28 | } 29 | 30 | .el-message { 31 | pointer-events: none; 32 | } 33 | 34 | .el-message-box__headerbtn { 35 | top: 15px !important; 36 | } 37 | 38 | .el-message__content { 39 | font-size: 18px !important; 40 | } 41 | 42 | .el-message-box { 43 | width: auto !important; 44 | max-width: 80vw !important; 45 | } 46 | 47 | .el-message-box__header { 48 | background-color: #ddd; 49 | padding-top: 15px !important; 50 | padding-bottom: 15px !important; 51 | } 52 | 53 | .el-message-box__message { 54 | font-size: 18px; 55 | } 56 | 57 | .i-icon>svg { 58 | vertical-align: baseline !important; 59 | } 60 | 61 | @include media-breakpoint-down(sm) { 62 | .el-message { 63 | width: max-content !important; 64 | text-align: center; 65 | justify-content: center; 66 | padding: 10px 10px !important; 67 | 68 | -webkit-touch-callout: none; 69 | -webkit-user-select: none; 70 | -khtml-user-select: none; 71 | -moz-user-select: none; 72 | -ms-user-select: none; 73 | user-select: none; 74 | } 75 | 76 | .el-message__content { 77 | font-size: 14px !important; 78 | } 79 | } -------------------------------------------------------------------------------- /client/src/assets/my.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --my-min-win-h: 540px; 3 | --my-min-win-w: 960px; 4 | 5 | --my-bar-height-xs: 18px; 6 | --my-bar-height-s: 24px; 7 | --my-bar-height: 36px; 8 | --my-bar-height-ml: 42px; 9 | --my-bar-height-l: 48px; 10 | --my-bar-height-xl: 60px; 11 | --my-bar-height-2m: 72px; 12 | --my-bar-height-2l: 96px; 13 | } 14 | 15 | html, 16 | body { 17 | margin: 0px; 18 | padding: 0px; 19 | width: 100%; 20 | height: 100%; 21 | min-width: var(--my-min-win-w); 22 | min-height: var(--my-min-win-h); 23 | } 24 | 25 | body { 26 | color: #000; 27 | background: #FFF; 28 | font-size: 14px; 29 | touch-action: manipulation; 30 | background-color: var(--el-color-primary-light-9); 31 | } 32 | 33 | button, 34 | pre, 35 | code, 36 | input, 37 | textarea { 38 | font-size: medium; 39 | } 40 | 41 | .my_button_white { 42 | padding: 0px 8px; 43 | color: #FFF; 44 | background-color: transparent; 45 | border: 1px solid transparent; 46 | box-sizing: border-box; 47 | } 48 | 49 | .my_button_white:hover { 50 | cursor: pointer; 51 | border: 1px #FFF solid; 52 | } 53 | 54 | .my_button_white:disabled { 55 | cursor: not-allowed !important; 56 | background-color: transparent !important; 57 | opacity: 0.5; 58 | } 59 | 60 | .my_button_black { 61 | padding: 0px 10px; 62 | color: #000; 63 | background-color: transparent; 64 | border: 1px solid transparent; 65 | box-sizing: border-box; 66 | } 67 | 68 | .my_button_black:hover { 69 | cursor: pointer; 70 | border: 1px #000 solid; 71 | } 72 | 73 | .my_button_black:disabled { 74 | cursor: not-allowed !important; 75 | background-color: transparent !important; 76 | opacity: 0.5; 77 | } 78 | 79 | .op_button { 80 | box-sizing: border-box; 81 | border: 1px solid var(--el-color-primary); 82 | line-height: 30px; 83 | padding-top: 0px; 84 | padding-bottom: 0px; 85 | color: var(--el-color-primary); 86 | background-color: #FFF; 87 | } 88 | 89 | .op_button:hover { 90 | border: 1px solid var(--el-color-danger); 91 | cursor: pointer; 92 | } 93 | 94 | .op_button:disabled { 95 | color: #000 !important; 96 | cursor: not-allowed !important; 97 | background-color: transparent !important; 98 | opacity: 0.5; 99 | } 100 | 101 | .op_button+.op_button { 102 | margin-left: 2px; 103 | } 104 | 105 | .op_button_on { 106 | color: white; 107 | background-color: var(--el-color-danger); 108 | } 109 | 110 | .op_button_hl { 111 | border-color: blue; 112 | color: white !important; 113 | background-color: var(--el-color-primary) !important; 114 | } 115 | 116 | .op_button_hl2 { 117 | border-color: red; 118 | color: white !important; 119 | background-color: var(--el-color-danger) !important; 120 | } 121 | 122 | @font-face { 123 | font-family: 'LogoFont'; 124 | src: url('/font/logo2.otf'); 125 | } 126 | 127 | @font-face { 128 | font-family: 'digitW'; 129 | src: url('/font/digitW.ttf'); 130 | } 131 | 132 | @include media-breakpoint-down(sm) { 133 | :root { 134 | --my-min-win-h: 360px; 135 | --my-min-win-w: 360px; 136 | } 137 | } -------------------------------------------------------------------------------- /client/src/d/ACsv.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace acsv 2 | { 3 | class Field 4 | { 5 | /** 6 | * Full Name. 7 | */ 8 | fullName: string; 9 | /** 10 | * Name. 11 | */ 12 | name: string; 13 | /** 14 | * Type. 15 | */ 16 | type: string; 17 | } 18 | /** 19 | * 1. Copyright (c) 2022 amin2312 20 | * 2. Version 1.0.0 21 | * 3. MIT License 22 | * 23 | * ACsv is a easy, fast and powerful csv parse library. 24 | */ 25 | class Table 26 | { 27 | /** 28 | * The raw content. 29 | */ 30 | content: number; 31 | /** 32 | * Parsed csv table Head. 33 | */ 34 | head: Array; 35 | /** 36 | * Parsed csv table Body. 37 | */ 38 | body: Array>; 39 | /** 40 | * Merge a table. 41 | *
Notice: two tables' structure must be same. 42 | * @param b source table 43 | * @return THIS instance 44 | */ 45 | merge(b: Table): Table; 46 | /** 47 | * Create index for the specified column. 48 | *
This function is only valid for "selectWhenE" and "limit" param is 1. 49 | *
It will improve performance. 50 | * @param colIndex column index 51 | */ 52 | createIndexAt(colIndex: number): void; 53 | /** 54 | * Get column index by specified field name. 55 | * @param name As name mean 56 | * @return column index 57 | */ 58 | getColIndexBy(name: String): number; 59 | /** 60 | * Fetch a row object when the column's value is equal to the id value 61 | * @param values the specified value 62 | * @param colIndex specified column index 63 | * @return selected row object 64 | */ 65 | id(value: any, colIndex?: number): any; 66 | /** 67 | * Sort by selected rows. 68 | * @param colIndex the column index specified for sorting 69 | * @param sortType 0: asc, 1: desc 70 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 71 | */ 72 | sortBy(colIndex: number, sortType: number): Table; 73 | /** 74 | * Get current selector(it includes all selected results). 75 | *
Notice: It be assigned after call "select..." function 76 | * @return current selector 77 | */ 78 | getCurrentSelector(): Array; 79 | /** 80 | * Fetch first selected result to a row and return it. 81 | * @return first selected row data 82 | */ 83 | toFirstRow(): Array; 84 | /** 85 | * Fetch last selected result to a row and return it. 86 | * @return last selected row data 87 | */ 88 | toLastRow(): Array; 89 | /** 90 | * Fetch all selected results to the rows and return it. 91 | * @return a array of row data 92 | */ 93 | toRows(): Array>; 94 | /** 95 | * Fetch first selected result to a object and return it. 96 | * @return first selected row object 97 | */ 98 | toFirstObj(): any; 99 | /** 100 | * Fetch last selected result to a object and return it. 101 | * @return last selected row object 102 | */ 103 | toLastObj(): any; 104 | /** 105 | * Fetch all selected results to the objects and return it. 106 | * @return a array of row object 107 | */ 108 | toObjs(): Array; 109 | /** 110 | * Fetch all selected results to a new table. 111 | * @return a new table instance 112 | */ 113 | toTable(): Table; 114 | /** 115 | * Select all rows. 116 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 117 | */ 118 | selectAll(): Table; 119 | /** 120 | * Select the first row. 121 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 122 | */ 123 | selectFirstRow(): Table; 124 | /** 125 | * Select the last row. 126 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 127 | */ 128 | selectLastRow(): Table 129 | /** 130 | * Selects the specified rows by indices. 131 | * @param rowIndices specified row's indices 132 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 133 | */ 134 | selectAt(rowIndices: Array): Table 135 | /** 136 | * Select the rows when the column's value is equal to any value of array. 137 | * @param limit maximum length of every selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 138 | * @param values the array of values 139 | * @param colIndex specified column index 140 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 141 | */ 142 | selectWhenIn(limit: number, values: Array, colIndex?: number): Table 143 | /** 144 | * Select the rows when the column's value is equal to specified value. 145 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 146 | * @param value the specified value 147 | * @param colIndex specified column index 148 | * @param extraSelector extra selector, use it to save selected result 149 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 150 | */ 151 | selectWhenE(limit: number, value: any, colIndex?: number): Table 152 | /** 153 | * Select the rows when the column's values are equal to specified values. 154 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 155 | * @param value1 first specified value 156 | * @param value2 second specified value 157 | * @param colIndex2 second specified column index 158 | * @param colIndex1 first specified column index 159 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 160 | */ 161 | selectWhenE2(limit: number, value1: any, value2: any, colIndex2?: number, colIndex1?: number): Table 162 | /** 163 | * Select the rows when the column's values are equal to specified values. 164 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 165 | * @param value1 first specified value 166 | * @param value2 second specified value 167 | * @param value3 third specified value 168 | * @param colIndex3 third specified column index 169 | * @param colIndex2 second specified column index 170 | * @param colIndex1 first specified column index 171 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 172 | */ 173 | selectWhenE3(limit: number, value1: any, value2: any, value3: any, colIndex3?: number, colIndex2?: number, colIndex1?: number): Table 174 | /** 175 | * Select the rows when the column's value is greater than specified value. 176 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 177 | * @param withEqu whether include equation 178 | * @param value the specified value 179 | * @param colIndex specified column index 180 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 181 | */ 182 | selectWhenG(limit: number, withEqu: boolean, value: number, colIndex?: number): Table 183 | /** 184 | * Select the rows when the column's value is less than specified values. 185 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 186 | * @param withEqu whether include equation 187 | * @param value the specified value 188 | * @param colIndex specified column index 189 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 190 | */ 191 | selectWhenL(limit: number, withEqu: boolean, value: number, colIndex?: number): Table 192 | /** 193 | * Select the rows when the column's value is greater than specified value and less than specified value. 194 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 195 | * @param GWithEqu whether greater and equal 196 | * @param LWithEqu whether less and equal 197 | * @param GValue the specified greater value 198 | * @param LValue the specified less value 199 | * @param colIndex specified column index 200 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 201 | */ 202 | selectWhenGreaterAndLess(limit: number, GWithEqu: boolean, LWithEqu: boolean, GValue: number, LValue: number, colIndex?: number): Table 203 | /** 204 | * Select the rows when the column's value is less than specified value or greater than specified value. 205 | * @param limit maximum length of selected results (0 is infinite, if you only need 1 result, 1 is recommended, it will improve performance) 206 | * @param LWithEqu whether less and equal 207 | * @param GWithEqu whether greater and equal 208 | * @param LValue the specified less value 209 | * @param GValue the specified greater value 210 | * @param colIndex specified column index 211 | * @return THIS instance (for Method Chaining), can call "to..." or "select..." function in next step. 212 | */ 213 | selectWhenLessOrGreater(limit: number, LWithEqu: boolean, GWithEqu: boolean, LValue: number, GValue: number, colIndex?: number): Table 214 | /** 215 | * Parse csv conent. 216 | * @param content As name mean 217 | * @param filedSeparator filed separator 218 | * @param filedDelimiter filed delimiter 219 | * @return a table instance 220 | */ 221 | static Parse(content: String, filedSeparator?: String, filedDelimiter?: String): Table; 222 | } 223 | } -------------------------------------------------------------------------------- /client/src/layouts/FrameBody.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /client/src/layouts/FrameHead.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 39 | -------------------------------------------------------------------------------- /client/src/main.js: -------------------------------------------------------------------------------- 1 | import Kit from "./Kit"; 2 | import Lang from './Lang'; 3 | import { G, GB, LoadSettings, SaveSettings, LoadJobs } from "./G"; 4 | import AppRoot from './AppRoot.vue'; 5 | import { BuildApp, MountApp } from "./App"; 6 | 7 | import CsvFile1 from './assets/language_support.csv?raw'; 8 | import CsvFile2 from './assets/language_strings.csv?raw'; 9 | 10 | /** 11 | * Main. 12 | */ 13 | function main() 14 | { 15 | // Load i18n file 16 | Lang.Init(acsv.Table.Parse(CsvFile1), acsv.Table.Parse(CsvFile2)); 17 | Lang.customValue = Lang.getLangFieldBy(navigator.language); 18 | 19 | readLocalAppData(); 20 | onWindowResize(); 21 | 22 | // 1.New vue app 23 | var myApp = BuildApp(AppRoot); 24 | // 2.Init vue global 25 | myApp.config.globalProperties.G = G; 26 | myApp.config.globalProperties.GB = GB; 27 | myApp.config.globalProperties.Kit = Kit; 28 | myApp.config.globalProperties.Lang = Lang; 29 | // 3.Mount vue app 30 | MountApp(myApp, '#my-app'); 31 | 32 | // Listen to window events 33 | window.onbeforeunload = (e) => SaveSettings(); 34 | window.addEventListener("resize", onWindowResize); 35 | document.addEventListener("gesturestart", (e) => e.preventDefault()); 36 | document.oncontextmenu = (e) => { if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target.className.indexOf("el-popper") != -1 || e.target.className.indexOf("el-image") != -1) { /* pass */ } else { e.preventDefault(); } }; 37 | } 38 | /** 39 | * Read local app data. 40 | */ 41 | function readLocalAppData() 42 | { 43 | G.url = new URL(window.location.href); 44 | G.uid = localStorage.getItem('uid') || Kit.genUID(); 45 | Kit.fillInto(LoadSettings(), GB.settings, true); 46 | GB.settings.runTimes++; 47 | GB.jobs = LoadJobs(); 48 | localStorage.setItem('uid', G.uid); 49 | } 50 | /** 51 | * [Event] window resize. 52 | */ 53 | function onWindowResize() 54 | { 55 | var widthA = 0; 56 | var widthB = 5; 57 | var m = false; 58 | // min-width 59 | if (m = window.matchMedia('(min-width: 576px)').matches) 60 | { 61 | widthA = 1; 62 | } 63 | if (m = window.matchMedia('(min-width: 768px)').matches) 64 | { 65 | widthA = 2; 66 | } 67 | if (m = window.matchMedia('(min-width: 992px)').matches) 68 | { 69 | widthA = 3; 70 | } 71 | if (m = window.matchMedia('(min-width: 1200px)').matches) 72 | { 73 | widthA = 4; 74 | } 75 | // max-width 76 | if (m = window.matchMedia('(max-width: 1199.98px)').matches) 77 | { 78 | widthB = 4; 79 | } 80 | if (m = window.matchMedia('(max-width: 991.98px)').matches) 81 | { 82 | widthB = 3; 83 | } 84 | if (m = window.matchMedia('(max-width: 767.98px)').matches) 85 | { 86 | widthB = 2; 87 | } 88 | if (m = window.matchMedia('(max-width: 575.98px)').matches) 89 | { 90 | widthB = 1; 91 | } 92 | GB.isTabletView = widthA < 3; 93 | GB.isPhoneView = widthB < 2; 94 | GB.isTouchDevice = navigator.maxTouchPoints > 0; 95 | } 96 | 97 | main(); -------------------------------------------------------------------------------- /client/src/views/LPart.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 180 | 181 | 298 | 303 | -------------------------------------------------------------------------------- /client/src/views/RPartDallE.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 160 | 161 | 233 | -------------------------------------------------------------------------------- /client/src/views/RPartSD.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 178 | 179 | 255 | -------------------------------------------------------------------------------- /client/src/views/Workspace.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 364 | 365 | 481 | 486 | -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url'; 2 | 3 | import { defineConfig, splitVendorChunkPlugin } from 'vite'; 4 | import vue from '@vitejs/plugin-vue'; 5 | import AutoImport from 'unplugin-auto-import/vite'; 6 | import Components from 'unplugin-vue-components/vite'; 7 | import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; 8 | 9 | export default defineConfig({ 10 | base: "./", 11 | define: { 12 | // Client configuration 13 | __conf__: { 14 | } 15 | }, 16 | plugins: [ 17 | vue(), 18 | splitVendorChunkPlugin(), 19 | AutoImport({ resolvers: [ElementPlusResolver()] }), 20 | Components({ resolvers: [ElementPlusResolver()] }), 21 | ], 22 | server: { 23 | host: '0.0.0.0', 24 | port: 5174, 25 | }, 26 | resolve: { 27 | alias: { 28 | '@': fileURLToPath(new URL('./src', import.meta.url)) 29 | } 30 | }, 31 | build: { 32 | target: 'es2015', 33 | cssTarget: 'chrome51', 34 | outDir: '../wwwroot/', 35 | emptyOutDir: true, 36 | }, 37 | css: { 38 | preprocessorOptions: { 39 | scss: { 40 | additionalData: `@import "@/assets/breakpoints.scss";` 41 | }, 42 | } 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /intro/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/intro/screenshot-1.png -------------------------------------------------------------------------------- /intro/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/intro/screenshot-2.png -------------------------------------------------------------------------------- /server/app.py: -------------------------------------------------------------------------------- 1 | # encoding=utf-8 2 | import os 3 | import sys 4 | import json 5 | import time 6 | import uuid 7 | import httpx 8 | import asyncio 9 | import traceback 10 | import tornado.web 11 | import tornado.ioloop 12 | from apscheduler.schedulers.tornado import TornadoScheduler 13 | 14 | from openai import AsyncOpenAI 15 | from openai import AsyncAzureOpenAI 16 | 17 | from comm import * 18 | 19 | MSG_DELIM = '---1@3$5^7*9)---' 20 | 21 | NumConcurrencies = 0 22 | 23 | 24 | async def dumpToUrl(UUID, type, data) -> (bool | str): 25 | if not Conf.dump.on: 26 | return False 27 | saveName = '%s.%s' % (UUID, Conf.dump.save_type) 28 | savePath = Conf.dump.save_path + saveName 29 | if type == 'url': 30 | ok, err = await DumpImageByUrl(data, savePath, Conf.dump.save_type, Conf.dump.save_quality) 31 | if not ok: 32 | Logger.error(err) 33 | return False 34 | elif type == 'base64': 35 | ok, err = DumpImageByBase64(data, savePath, Conf.dump.save_type, Conf.dump.save_quality) 36 | if not ok: 37 | Logger.error(err) 38 | return False 39 | return Conf.dump.public_url.replace('{host}', CurHost).replace('{save_path}', savePath).replace('{save_name}', saveName) 40 | 41 | 42 | class onRetrieve(tornado.web.RequestHandler): 43 | def post(self): 44 | global Jobs 45 | 46 | self.set_header('Content-Control', "no-cache") 47 | self.set_header('Access-Control-Allow-Origin', "*") 48 | 49 | try: 50 | content = self.request.body 51 | ids = json.loads(content) 52 | resp = {"err": None, "items": []} 53 | for uuid in ids: 54 | resp["items"].append( 55 | Jobs.get(uuid, {"uuid": uuid, "state": 404})) 56 | self.write(json.dumps(resp)) 57 | except Exception as e: 58 | resp = { 59 | "err": { 60 | "name": type(e).__name__, 61 | "desc": str(e), 62 | } 63 | } 64 | self.write(json.dumps(resp)) 65 | 66 | 67 | class onCreate(tornado.web.RequestHandler): 68 | async def post(self): 69 | global Jobs, CurHost, NumConcurrencies 70 | CurHost = '%s://%s' % (self.request.protocol, self.request.host) 71 | isFreeTrial = False 72 | 73 | self.set_header('Content-Control', "no-cache") 74 | self.set_header('Access-Control-Allow-Origin', "*") 75 | 76 | # 3.Start work 77 | try: 78 | # 1.Create job 79 | NumConcurrencies += 1 80 | UUID: str = 'job-' + str(uuid.uuid4()) 81 | job = { 82 | "uuid": UUID, 83 | "state": 0, 84 | } 85 | 86 | # 2.If overflow, then remove first element 87 | if len(Jobs) >= Conf.jobs.maxItems: 88 | Jobs.pop(next(iter(Jobs))) 89 | Jobs[UUID] = job # add to jobs 90 | 91 | content: bytes = self.request.body 92 | I = json.loads(content) 93 | 94 | # 1.Get and check parameters 95 | p_model: str = I["model"] 96 | p_version: float = I["version"] 97 | p_prompt: str = I["prompt"] 98 | p_negativePrompt: str = I.get("negativePrompt", None) 99 | p_param: dict = I["param"] 100 | p_supplier: str = I["supplier"] 101 | p_api_key: str = I["api_key"] 102 | 103 | isFreeApiKey = not p_api_key 104 | if isFreeApiKey: 105 | # Check whether the max free concurrencies is exceeded 106 | if NumConcurrencies > Conf.jobs.maxFreeConcurrencies: 107 | raise Exception('exceed_max_concurrencies') 108 | 109 | if p_model == 'dall-e': 110 | p_param_size: any = p_param['size'] # type: ignore 111 | p_param_quality: any = p_param['quality'] # type: ignore 112 | p_param_style: any = p_param['style'] # type: ignore 113 | if p_supplier == 'azure': 114 | p_api_version: str = I['api_version'] 115 | p_endpoint: str = I['endpoint'] 116 | 117 | elif p_model == 'stable-diffusion': 118 | p_text_prompts = [{"text": p_prompt, "weight": 1}] 119 | if p_negativePrompt: 120 | p_text_prompts.append( 121 | {"text": p_negativePrompt, "weight": -1}) 122 | p_param_width: int = p_param['width'] 123 | p_param_height: int = p_param['height'] 124 | p_param_cfg_scale: float = p_param['cfg_scale'] 125 | p_param_clip_guidance_preset: str = p_param['clip_guidance_preset'] 126 | p_param_sampler: str | None = p_param['sampler'] if p_param['sampler'] != 'default' else None 127 | p_param_samples: int = p_param['samples'] 128 | p_param_seed: int = p_param['seed'] 129 | p_param_steps: int = p_param['steps'] 130 | p_param_style_preset: str | None = p_param['style_preset'] if p_param['style_preset'] != 'default' else None 131 | 132 | # 2.Send the job to client for storage first 133 | job["I"] = I 134 | self.write(json.dumps(job) + MSG_DELIM) 135 | self.flush() 136 | # raise Exception('empty_content') 137 | # await asyncio.sleep(3000000) # for test 138 | 139 | # 3.Send the API request to supplier 140 | if p_model == 'dall-e': 141 | httpPorxy = httpx.AsyncClient(proxies=Conf.api_proxy) if Conf.api_proxy else None # support proxy 142 | _TIME_A_ = time.time() 143 | if Conf.test: 144 | O = {'created': 1713337972, 'data': [{'b64_json': None, 'revised_prompt': 'A detailed image depicting an everyday life scene on a sunny afternoon in the United States. The scene includes an individual of Caucasian descent, male, in casual summer wear, walking his Golden Retriever on a suburban sidewalk lined with trees. Also included in the scene is a Hispanic female, a professional painter, working on a cityscape painting on a nearby easel. Both individuals are composed, relaxed, and are enjoying the beautiful day in their own ways. The setting sunlight casts long, dappled shadows on the scene, giving it a serene and tranquil ambiance.', 'url': 'test.jpg'}]} 145 | else: 146 | if p_supplier == 'azure': 147 | client = AsyncAzureOpenAI(api_key=p_api_key or Conf.supplier.azure.api_key, http_client=httpPorxy, api_version=p_api_version or Conf.supplier.azure.api_version, azure_endpoint=p_endpoint or Conf.supplier.azure.endpoint) 148 | else: 149 | client = AsyncOpenAI(api_key=p_api_key or Conf.supplier.openai.api_key, http_client=httpPorxy) 150 | resp = await client.images.generate(model=p_model+'-'+str(p_version), prompt=p_prompt, size=p_param_size, quality=p_param_quality, style=p_param_style, n=1) 151 | O = resp.model_dump() 152 | _TIME_B_ = time.time() 153 | 154 | # dump to url 155 | sampleType = 'url' 156 | sampleData = O['data'][0]['url'] 157 | okOrUrl = await dumpToUrl(UUID, sampleType, sampleData) 158 | if okOrUrl: 159 | sampleType = 'url' 160 | sampleData = okOrUrl 161 | 162 | # update job 163 | job['state'] = 1 # update state 164 | job['O'] = O 165 | job['image'] = { 166 | "created": O['created'], 167 | "elapsed": int(_TIME_B_ - _TIME_A_), 168 | "samples": [ 169 | {"type": sampleType, "data": sampleData} 170 | ], 171 | } 172 | elif p_model == 'stable-diffusion': 173 | client = httpx.AsyncClient(proxies=Conf.api_proxy) 174 | api_host = 'https://api.stability.ai' 175 | engine_id = p_model + '-v' + str(p_version).replace('.', '-') 176 | api_key = p_api_key or Conf.supplier.stability.api_key 177 | async with client: 178 | _TIME_A_ = time.time() 179 | if Conf.test: 180 | O = {"artifacts": [{"base64": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==", "finishReason": "SUCCESS", "seed": 1050625087}]} 181 | else: 182 | resp = await client.post(f"{api_host}/v1/generation/{engine_id}/text-to-image", 183 | headers={"Content-Type": "application/json", "Accept": "application/json", "Authorization": f"Bearer {api_key}"}, 184 | json={"text_prompts": p_text_prompts, "width": p_param_width, "height": p_param_height, "cfg_scale": p_param_cfg_scale, "clip_guidance_preset": p_param_clip_guidance_preset, "sampler": p_param_sampler, "samples": p_param_samples, "seed": p_param_seed, "steps": p_param_steps, "style_preset": p_param_style_preset, }, 185 | timeout=Conf.maxRequestSeconds) 186 | if resp.status_code != 200: 187 | raise Exception(resp.text) 188 | O = resp.json() 189 | if len(O['artifacts']) == 0: # Sometime return empty content 190 | raise Exception('empty_content') 191 | _TIME_B_ = time.time() 192 | 193 | # dump to url 194 | sampleType = 'base64' 195 | sampleData = O['artifacts'][0]['base64'] 196 | okOrUrl = await dumpToUrl(UUID, sampleType, sampleData) 197 | if okOrUrl: 198 | sampleType = 'url' 199 | sampleData = okOrUrl 200 | O['artifacts'][0]['base64'] = '...' 201 | 202 | # update job 203 | job['state'] = 1 # update state 204 | job['O'] = O 205 | job['image'] = { 206 | "created": int(_TIME_B_), 207 | "elapsed": int(_TIME_B_ - _TIME_A_), 208 | "samples": [ 209 | {"type": sampleType, "data": sampleData} 210 | ], 211 | } 212 | else: 213 | raise Exception('Invalid model request') 214 | self.write(json.dumps(job) + MSG_DELIM) 215 | self.finish() 216 | except Exception as e: 217 | job['state'] = -1 # update state 218 | job["err"] = { 219 | "name": type(e).__name__, 220 | "desc": str(e), 221 | } 222 | self.write(json.dumps(job) + MSG_DELIM) 223 | self.finish() 224 | Logger.error(traceback.format_exc()) 225 | finally: 226 | NumConcurrencies -= 1 227 | 228 | 229 | class onIndex(tornado.web.RequestHandler): 230 | def get(self): 231 | self.redirect("/gen-basic/") 232 | 233 | 234 | def buildApp(): 235 | return tornado.web.Application( 236 | [ 237 | (r"/gen-basic/api/create", onCreate), 238 | (r"/gen-basic/api/retrieve", onRetrieve), 239 | (r"/gen-basic/downloads/(.*)", tornado.web.StaticFileHandler, {"path": Conf.dump.save_path}), 240 | (r"/gen-basic/(.*)", tornado.web.StaticFileHandler, {"path": "../wwwroot/", "default_filename": "index.html"}), 241 | (r"/", onIndex), 242 | ], 243 | debug=Conf.debug 244 | ) 245 | 246 | 247 | def onDaily(): 248 | pass 249 | 250 | 251 | if __name__ == "__main__": 252 | # change directory to current path 253 | CurDir = os.path.dirname(os.path.realpath(__file__)) 254 | os.chdir(CurDir) 255 | print("Current Directory", CurDir) 256 | # set conf file 257 | confFileName = sys.argv[1] if len(sys.argv) > 1 else 'conf.json' 258 | 259 | Conf: any = LoadConf(confFileName) # type: ignore 260 | Jobs: any = LoadJson(Conf.jobs.save_path) # type: ignore 261 | Logger = InitLog(Conf.errLog) 262 | sys.stdout = MyPrint(Conf.outLog) 263 | 264 | try: 265 | myApp = buildApp() 266 | myApp.listen(Conf.port) 267 | 268 | print("Server port: %d" % Conf.port) 269 | print("Server is running..") 270 | 271 | # add a scheduler 272 | scheduler = TornadoScheduler() 273 | scheduler.add_job(onDaily, 'interval', seconds=2) 274 | scheduler.start() 275 | 276 | tornado.ioloop.IOLoop.current().start() 277 | except Exception as e: 278 | print(e) 279 | finally: 280 | SaveJson(Conf.jobs.save_path, Jobs) 281 | -------------------------------------------------------------------------------- /server/comm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import json 5 | import httpx 6 | import base64 7 | import logging 8 | from io import BytesIO 9 | from PIL import Image 10 | 11 | 12 | def InitLog(fileName): 13 | logging.basicConfig(filename=fileName, level=logging.WARNING, format='[%(levelname)s] %(asctime)s %(message)s') 14 | return logging.getLogger() 15 | 16 | 17 | class DictClass(dict): 18 | __getattr__ = dict.__getitem__ 19 | 20 | 21 | def ConvertDictToObj(d): 22 | if not isinstance(d, dict): 23 | return d 24 | dc = DictClass() 25 | for k, v in d.items(): 26 | dc[k] = ConvertDictToObj(v) 27 | return dc 28 | 29 | 30 | def LoadConf(fileName): 31 | ins = None 32 | if os.path.exists(fileName): 33 | with open(fileName, 'r', encoding='utf-8') as file: 34 | text = file.read() 35 | text = re.sub(r'\s+//.*?$', '', text, flags=re.MULTILINE) 36 | d = json.loads(text or '{}') 37 | ins = ConvertDictToObj(d) 38 | print('LoadConf', fileName, d) 39 | return ins 40 | 41 | 42 | def SaveConf(fileName, content): 43 | print('SaveConf', content) 44 | with open(fileName, 'w', encoding='utf-8') as file: 45 | jstr = json.dumps(content, indent=None) 46 | file.write(jstr) 47 | 48 | 49 | def LoadJson(fileName, default="{}"): 50 | jobj = json.loads(default) 51 | if os.path.exists(fileName): 52 | with open(fileName, 'r', encoding='utf-8') as file: 53 | text = file.read() 54 | text = re.sub(r'\s+//.*?$', '', text, flags=re.MULTILINE) 55 | jobj = json.loads(text or default) 56 | print('LoadJson', fileName) 57 | return jobj 58 | 59 | 60 | def SaveJson(fileName, jobj): 61 | with open(fileName, 'w', encoding='utf-8') as file: 62 | jstr = json.dumps(jobj, indent=None) 63 | file.write(jstr) 64 | print('SaveJson', fileName) 65 | 66 | 67 | async def DumpImageByUrl(url: str, savePath: str, saveType: str, saveQuality: str): 68 | async with httpx.AsyncClient(verify=False, timeout=None) as client: 69 | try: 70 | response = await client.get(url) 71 | except Exception as e: 72 | return False, "DumpImageByUrl fail-1: %s - %s" % (url, str(e)) 73 | 74 | status_code = response.status_code 75 | content = response.content 76 | if status_code != 200: 77 | return False, ("DumpImageByUrl fail-1: %d - %s" % (status_code, url)) 78 | 79 | try: 80 | bin = BytesIO(content) 81 | img = Image.open(bin) 82 | img.save(savePath, format=saveType, quality=saveQuality) 83 | img.close() 84 | bin.close() 85 | except Exception as e: 86 | return False, "DumpImageByUrl fail-3: %s - %s" % (url, str(e)) 87 | return True, None 88 | 89 | 90 | def DumpImageByBase64(b64: str, savePath: str, saveType: str, saveQuality: str): 91 | try: 92 | data = base64.b64decode(b64) 93 | bin = BytesIO(data) 94 | img = Image.open(bin) 95 | img.save(savePath, format=saveType, quality=saveQuality) 96 | img.close() 97 | bin.close() 98 | except Exception as e: 99 | return False, "DumpImageByBase64 fail-1: %s" % (str(e)) 100 | return True, None 101 | 102 | 103 | class MyPrint: 104 | def __init__(self, filename): 105 | self.stdout = sys.stdout 106 | self.file = open(filename, "w") 107 | 108 | def write(self, message): 109 | self.stdout.write(message) 110 | self.file.write(message) 111 | 112 | def flush(self): # The flush method is required to be compatible with sys.stdout 113 | self.stdout.flush() 114 | self.file.flush() 115 | -------------------------------------------------------------------------------- /server/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": false, 3 | "test": false, 4 | "port": 3001, 5 | "errLog": "stderr.log", 6 | "outLog": "stdout.log", 7 | "api_proxy": "", // Use it to solve API calling restrictions in some regions 8 | "maxRequestSeconds": 120, 9 | "jobs": { 10 | "maxItems": 100, 11 | "maxFreeConcurrencies": 100, 12 | "save_path": "jobs.json" 13 | }, 14 | // It is recommended to enable this parameter because the client's localStroage has an upper limit. 15 | "dump": { 16 | "on": true, 17 | "save_type": "webp", 18 | "save_quality": 80, 19 | "save_path": "downloads/", 20 | "public_url": "{host}/{save_path}" 21 | }, 22 | "supplier": { 23 | "openai": { 24 | "api_key": "" 25 | }, 26 | "azure": { 27 | "api_key": "", 28 | "api_version": "", 29 | "endpoint": "" 30 | }, 31 | "stability": { 32 | "api_key": "" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /server/downloads/test.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/server/downloads/test.webp -------------------------------------------------------------------------------- /server/requirements.txt: -------------------------------------------------------------------------------- 1 | APScheduler==3.10.4 2 | httpx==0.27.0 3 | openai==1.23.2 4 | Pillow==9.2.0 5 | tornado==6.2 6 | -------------------------------------------------------------------------------- /wwwroot/assets/index-47a4560e.js: -------------------------------------------------------------------------------- 1 | import{r as F,E as de,o as h,c as v,a as l,t as m,b as pe,d as L,e as k,f as I,F as z,g as i,w as c,h as b,i as D,j as A,k as te,l as ne,m as me,n as ce,p as x,q as ge,s as R,u as J,v as le,x as H,y as K,z as fe,A as _e,B as he,C as be,D as M,G as j,H as ye,I as ve,J as we,K as Se,L as Le,M as Ve,R as Ie,N as Ee}from"./vendor-b3440d5e.js";(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const n of document.querySelectorAll('link[rel="modulepreload"]'))o(n);new MutationObserver(n=>{for(const s of n)if(s.type==="childList")for(const p of s.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&o(p)}).observe(document,{childList:!0,subtree:!0});function r(n){const s={};return n.integrity&&(s.integrity=n.integrity),n.referrerPolicy&&(s.referrerPolicy=n.referrerPolicy),n.crossOrigin==="use-credentials"?s.credentials="include":n.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function o(n){if(n.ep)return;n.ep=!0;const s=r(n);fetch(n.href,s)}})();const G=class S{static Init(e,r){S._support=e,S._strings=r,S._strings.createIndexAt(0),S._strings.createIndexAt(1)}static get systemValue(){return S.getLangFieldBy(navigator.language)}static get customValue(){return S._customValue}static set customValue(e){var r=null,o=null,n=S._support.selectAll().toObjs(),s;for(let p=0,u=n.length;p0)switch(p=o.substring(n+1,g),u=o.substring(g+1),s=parseInt(s),p=parseInt(p),p){case 10:u=parseInt(u);break;case 11:u=!!parseInt(u);break;case 20:u=parseFloat(u);break;case 32:u=JSON.parse(u);break}return{part1:s,part2:p,part3:u}}static getStateItems(e,r,o=-1,n=null){var s=[];for(let p of e)if(p.state==r&&(s.push(n?p[n]:p),o--,o==0))break;return s}static formatInputInfo(e){var r="";return e.model==y.MODELS[0]?r=`${d.n.prompt}: ${e.prompt} 2 | ${d.n.model}: ${e.model} 3 | ${d.n.version}: ${e.version} 4 | ${d.n.size}: ${e.param.size} 5 | ${d.n.quality}: ${e.param.quality} 6 | ${d.n.style}: ${e.param.style} 7 | ${d.n.supplier}: ${e.supplier} 8 | `:e.model==y.MODELS[1]&&(r=`${d.n.prompt}: ${e.prompt} 9 | ${d.n.negativePrompt}: ${e.negativePrompt} 10 | ${d.n.model}: ${e.model} 11 | ${d.n.version}: ${e.version} 12 | ${d.n.width}: ${e.param.width} 13 | ${d.n.height}: ${e.param.height} 14 | CFG Scale: ${e.param.cfg_scale} 15 | ${d.n.steps}: ${e.param.steps} 16 | CLIP Guidance: ${e.param.clip_guidance_preset} 17 | ${d.n.sampler}: ${e.param.sampler} 18 | ${d.n.style}: ${e.param.style_preset} 19 | ${d.n.seed}: ${e.param.seed} 20 | ${d.n.supplier}: ${e.supplier} 21 | `),r}static fillInto(e,r,o=!1,n=!1){if(!(e==null||r==null))for(let s in e){let p=r[s];o||p==null?r[s]=e[s]:n&&(r[s]+=e[s])}}static genUID(e=8,r=""){return r+$.padStart(Math.random().toString(36).substring(2,2+e),"0",e)+"-"+Date.now().toString(36)}static padStart(e,r,o){const n=o-e.length;return n>0&&(e=Array(n).fill(r).join("")+e),e}static copyTextToClipboard(e,{target:r=document.body}={}){const o=document.createElement("textarea"),n=document.activeElement;o.value=e,o.setAttribute("readonly",""),o.style.contain="strict",o.style.position="absolute",o.style.left="-9999px",o.style.fontSize="12pt";const s=document.getSelection(),p=s.rangeCount>0&&s.getRangeAt(0);r.append(o),o.select(),o.selectionStart=0,o.selectionEnd=e.length;let u=!1;try{u=document.execCommand("copy")}catch(g){}return o.remove(),p&&(s.removeAllRanges(),s.addRange(p)),n&&n.focus(),u}};ie.lastError=null;let E=ie;const C=(t,e)=>{const r=t.__vccOpts||t;for(const[o,n]of e)r[o]=n;return r},$e={name:"FrameHead"},ze={id:"my-frame-head"},Te={href:"https://www.ai-img-gen.com/"};function Ae(t,e,r,o,n,s){return h(),v("div",ze,[l("div",null,[l("a",Te,m(t.Lang.n.logo),1)])])}const xe=C($e,[["render",Ae],["__scopeId","data-v-c5286088"]]);const Re={name:"FrameBody"},Je={id:"my-frame-body"};function Me(t,e,r,o,n,s){return h(),v("div",Je,[pe(t.$slots,"default",{},void 0,!0)])}const je=C(Re,[["render",Me],["__scopeId","data-v-173c018b"]]),Ne={name:"LPart",data(){return{curJob:null}},computed:{curOutputInfo(){return JSON.stringify(this.curJob.O,null,2)},curInputInfo(){return E.formatInputInfo(this.curJob.I)},curImageInfo(){var t=`uuid: ${this.curJob.uuid} 22 | ${d.n.date}: ${new Date(this.curJob.image.created*1e3).toLocaleString()} 23 | ${d.n.elapsed}: ${this.curJob.image.elapsed}s 24 | `;return t}},methods:{onFillCurInputInfo(){this.$parent.fillInputBy(this.curJob.I)},onCopyCurPrompt(){E.copyTextToClipboard(this.curJob.I.prompt),L.success(d.id(125))},onCopyCurOutputInfo(){E.copyTextToClipboard(JSON.stringify(this.curJob.O,null,2)),L.success(d.id(125))},setCurJob(t,e=!1){this.curJob=t,e&&setTimeout(()=>{this.$refs.isb.setScrollLeft(2147483647)},1)},getImgSrc(t){var e="";return t.type=="base64"?"data:image/png;base64,"+t.data:(t.type=="url"&&(e=t.data),e)},onDelCurJob(){k.confirm(d.id(124),null).then(()=>{L.success(d.id(125));let t=_.jobs.indexOf(this.curJob);_.jobs.splice(t,1),this.curJob=_.jobs[t-1]||_.jobs[0]||null,N()}).catch(()=>{})},openCurImage(){window.open(this.getImgSrc(this.curJob.image.samples[0]))}}};const U=t=>(R("data-v-c84ea50c"),t=t(),J(),t),Ue={id:"gallery"},Be={class:"image-one"},Ge={class:"op_buttons"},Fe={class:"my_button_white"},He={style:{"overflow-x":"hidden"}},Ke={style:{margin:"0px 0px 8px 0px"}},qe={class:"my_button_white"},We={style:{"overflow-x":"hidden"}},Qe={style:{margin:"0px 0px 8px 0px"}},Xe={class:"my_button_white"},Ye={key:1},Ze={key:2},et={class:"cur-image"},tt=U(()=>l("div",{style:{color:"white",height:"100%",display:"flex","align-items":"center","justify-content":"center"}},"Loading..",-1)),nt=U(()=>l("div",{style:{color:"white",height:"100%",display:"flex","align-items":"center","justify-content":"center"}},"Load Failed",-1)),lt={key:0,class:"image-prompt"},st={class:"image-list"},ot={id:"ImageSet"},it=["onClick"],rt=U(()=>l("div",{style:{color:"white",width:"120px",height:"120px",display:"flex","align-items":"center","justify-content":"center"}},"Loading",-1)),at=U(()=>l("div",{style:{color:"white",width:"120px",height:"120px",display:"flex","align-items":"center","justify-content":"center"}},"Load Failed",-1));function ut(t,e,r,o,n,s){const p=I("icon-info"),u=I("icon-copy"),g=te,w=ne,f=I("icon-fill"),V=I("icon-delete"),O=me,T=ce;return h(),v("div",Ue,[l("div",Be,[l("div",Ge,[n.curJob?(h(),v(z,{key:0},[i(w,{placement:"bottom-start",title:t.Lang.n.outputInfo,width:348,"popper-class":"gallery-info-popover",trigger:"hover",content:s.curOutputInfo},{reference:c(()=>[l("button",Fe,[i(p,{size:"16",style:{"margin-right":"4px"}}),b(m(t.Lang.n.outputInfo1),1)])]),default:c(()=>[l("div",He,[l("pre",Ke,m(s.curOutputInfo),1),i(g,{type:"primary",plain:"",onClick:s.onCopyCurOutputInfo},{default:c(()=>[i(u,{size:"16"})]),_:1},8,["onClick"])])]),_:1},8,["title","content"]),i(w,{placement:"bottom-start",title:t.Lang.n.inputInfo,width:348,"popper-class":"gallery-info-popover",trigger:"hover"},{reference:c(()=>[l("button",qe,[i(p,{size:"16",style:{"margin-right":"4px"}}),b(m(t.Lang.n.inputInfo1),1)])]),default:c(()=>[l("div",We,[l("pre",Qe,m(s.curInputInfo),1),i(g,{type:"primary",plain:"",onClick:s.onFillCurInputInfo},{default:c(()=>[i(f,{size:"16"})]),_:1},8,["onClick"])])]),_:1},8,["title"]),i(w,{placement:"bottom-start",title:t.Lang.n.imgInfo,width:348,"popper-class":"gallery-info-popover",trigger:"hover",content:s.curImageInfo},{reference:c(()=>[l("button",Xe,[i(p,{size:"16",style:{"margin-right":"4px"}}),b(m(t.Lang.n.imgInfo1),1)])]),_:1},8,["title","content"]),l("button",{class:"my_button_white",onClick:e[0]||(e[0]=(...a)=>s.onDelCurJob&&s.onDelCurJob(...a))},[i(V,{size:"16",style:{"margin-right":"4px"}}),b(m(t.Lang.n.delete),1)])],64)):(h(),v("div",Ye)),t.GB.isPhoneView?(h(),v("div",Ze,[i(g,{type:"danger",plain:"",style:{"margin-left":"4px"},size:"small",onClick:e[1]||(e[1]=a=>t.$parent.isAdvanceView=!0)},{default:c(()=>[b(m(t.Lang.n.generate),1)]),_:1})])):D("",!0)]),l("div",et,[n.curJob?(h(),A(O,{key:0,src:s.getImgSrc(n.curJob.image.samples[0]),fit:"contain",onClick:s.openCurImage},{placeholder:c(()=>[tt]),error:c(()=>[nt]),_:1},8,["src","onClick"])):D("",!0)]),n.curJob?(h(),v("div",lt,[i(g,{type:"primary",style:{"margin-right":"4px"},size:"small",text:"",onClick:s.onCopyCurPrompt},{default:c(()=>[i(u,{size:"16"})]),_:1},8,["onClick"]),b(m(n.curJob.I.prompt),1)])):D("",!0)]),l("div",st,[i(T,{always:"",id:"ImageScrollBar",ref:"isb"},{default:c(()=>[l("div",ot,[(h(!0),v(z,null,x(t.Kit.getStateItems(t.GB.jobs,1),a=>(h(),v("div",{key:a.uuid},[l("div",{onClick:P=>s.setCurJob(a),class:ge({"is-selected-item":n.curJob==a})},[i(O,{src:s.getImgSrc(a.image.samples[0]),lazy:"","scroll-container":"#ImageScrollBar .el-scrollbar__wrap",fit:"contain"},{placeholder:c(()=>[rt]),error:c(()=>[at]),_:2},1032,["src"])],10,it)]))),128))])]),_:1},512)])])}const dt=C(Ne,[["render",ut],["__scopeId","data-v-c84ea50c"]]),Q={template:1,isExample:!1,model:y.MODELS[0],prompt:"",version:3,param:{size:"1024x1024",quality:"standard",style:"vivid"},supplier:"openai",api_key:"",api_version:"",endpoint:""},pt={name:"RPartDallE",props:{isDisabledSubmit:Boolean},data(){return{input:JSON.parse(JSON.stringify(Q))}},mounted(){},methods:{onHelpApiKey(){k.alert(d.id(159),null)},onClear(){this.input=JSON.parse(JSON.stringify(Q))},onSubmit(){this.submit()},submit(){if(!this.input.prompt){L.warning(d.id(121));return}if(this.input.supplier=="azure"&&!this.input.endpoint){L.warning(d.id(129)),this.$refs.endpoint.focus();return}this.$parent.submitRequest(this.input)&&(this.input.prompt="")}}};const q=t=>(R("data-v-dd332897"),t=t(),J(),t),mt={id:"R-dall-e"},ct={class:"tab"},gt=q(()=>l("br",null,null,-1)),ft=q(()=>l("br",null,null,-1)),_t=q(()=>l("br",null,null,-1)),ht={style:{display:"flex"}},bt={key:0},yt={key:1},vt={class:"op_buttons"},wt=["disabled"],St=["disabled"];function Lt(t,e,r,o,n,s){const p=le,u=K,g=H,w=I("icon-help");return h(),v("div",mt,[i(p,{modelValue:n.input.prompt,"onUpdate:modelValue":e[0]||(e[0]=f=>n.input.prompt=f),rows:6,type:"textarea",maxlength:4e3,"show-word-limit":"",placeholder:t.Lang.id(121)},null,8,["modelValue","placeholder"]),l("table",ct,[l("tr",null,[l("td",null,m(t.Lang.n.model),1),l("td",null,m(t.G.MODELS[0].toUpperCase())+" "+m(n.input.version),1)]),l("tr",null,[l("td",null,m(t.Lang.n.version),1),l("td",null,[i(g,{modelValue:n.input.version,"onUpdate:modelValue":e[1]||(e[1]=f=>n.input.version=f),fill:"green"},{default:c(()=>[i(u,{size:"small",label:3},{default:c(()=>[b("3")]),_:1})]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.size),1),l("td",null,[i(g,{modelValue:n.input.param.size,"onUpdate:modelValue":e[2]||(e[2]=f=>n.input.param.size=f),class:"param_size",fill:"green"},{default:c(()=>[i(u,{label:"1024x1024",size:"small"},{default:c(()=>[b("1024x1024"),gt,b(m(t.Lang.id(107)),1)]),_:1}),i(u,{label:"1792x1024",size:"small"},{default:c(()=>[b("1792x1024"),ft,b(m(t.Lang.id(108)),1)]),_:1}),i(u,{label:"1024x1792",size:"small"},{default:c(()=>[b("1024x1792"),_t,b(m(t.Lang.id(109)),1)]),_:1})]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.quality),1),l("td",null,[i(g,{modelValue:n.input.param.quality,"onUpdate:modelValue":e[3]||(e[3]=f=>n.input.param.quality=f),fill:"green"},{default:c(()=>[i(u,{label:"standard",size:t.GB.isPhoneView?"small":"default"},{default:c(()=>[b(m(t.Lang.id(111)),1)]),_:1},8,["size"]),i(u,{label:"hd",size:t.GB.isPhoneView?"small":"default"},{default:c(()=>[b(m(t.Lang.id(112)),1)]),_:1},8,["size"])]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.style),1),l("td",null,[i(g,{modelValue:n.input.param.style,"onUpdate:modelValue":e[4]||(e[4]=f=>n.input.param.style=f),fill:"green"},{default:c(()=>[i(u,{label:"vivid",size:t.GB.isPhoneView?"small":"default"},{default:c(()=>[b(m(t.Lang.id(114)),1)]),_:1},8,["size"]),i(u,{label:"natural",size:t.GB.isPhoneView?"small":"default"},{default:c(()=>[b(m(t.Lang.id(115)),1)]),_:1},8,["size"])]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.supplier),1),l("td",null,[i(g,{modelValue:n.input.supplier,"onUpdate:modelValue":e[5]||(e[5]=f=>n.input.supplier=f),fill:"green"},{default:c(()=>[i(u,{label:"openai",size:"small"},{default:c(()=>[b("OpenAI")]),_:1}),i(u,{label:"azure",size:"small"},{default:c(()=>[b("Azure")]),_:1})]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.apiKey),1),l("td",ht,[i(p,{modelValue:n.input.api_key,"onUpdate:modelValue":e[6]||(e[6]=f=>n.input.api_key=f),placeholder:t.Lang.id(120)},null,8,["modelValue","placeholder"]),l("button",{class:"inside_btn",onClick:e[7]||(e[7]=(...f)=>s.onHelpApiKey&&s.onHelpApiKey(...f))},[i(w,{fill:"#000",style:{margin:"0px 4px"}})])])]),n.input.supplier=="azure"?(h(),v("tr",bt,[l("td",null,m(t.Lang.n.apiVer),1),l("td",null,[i(p,{modelValue:n.input.api_version,"onUpdate:modelValue":e[8]||(e[8]=f=>n.input.api_version=f),placeholder:t.Lang.id(120)},null,8,["modelValue","placeholder"])])])):D("",!0),n.input.supplier=="azure"?(h(),v("tr",yt,[l("td",null,m(t.Lang.n.endpoint),1),l("td",null,[i(p,{ref:"endpoint",modelValue:n.input.endpoint,"onUpdate:modelValue":e[9]||(e[9]=f=>n.input.endpoint=f),placeholder:t.Lang.id(130)},null,8,["modelValue","placeholder"])])])):D("",!0)]),l("div",vt,[l("div",null,[l("button",{class:"op_button",onClick:e[10]||(e[10]=f=>s.onClear()),disabled:r.isDisabledSubmit},m(t.Lang.n.clear),9,wt)]),l("div",null,[l("button",{class:"op_button op_button_hl2",onClick:e[11]||(e[11]=f=>s.onSubmit()),disabled:r.isDisabledSubmit},m(t.Lang.n.generate),9,St)])])])}const Vt=C(pt,[["render",Lt],["__scopeId","data-v-dd332897"]]),X={template:1,isExample:!1,model:y.MODELS[1],prompt:"",negativePrompt:"",version:1.6,param:{width:512,height:512,cfg_scale:7,clip_guidance_preset:"NONE",sampler:"default",samples:1,seed:0,steps:30,style_preset:"default"},supplier:"stability",api_key:""},It={name:"RPartSD",props:{isDisabledSubmit:Boolean},data(){return{clip_guidance_enmu:["FAST_BLUE","FAST_GREEN","NONE","SIMPLE","SLOW","SLOWER","SLOWEST"],sampler_enmu:["default","DDIM","DDPM","K_DPMPP_2M","K_DPMPP_2S_ANCESTRAL","K_DPM_2","K_DPM_2_ANCESTRAL","K_EULER","K_EULER_ANCESTRAL","K_HEUN","K_LMS"],style_enmu:["default","3d-model","analog-film","anime","cinematic","comic-book","digital-art","enhance","fantasy-art","isometric","line-art","low-poly","modeling-compound","neon-punk","origami","photographic","pixel-art","tile-texture"],input:JSON.parse(JSON.stringify(X))}},mounted(){},methods:{onHelpApiKey(){k.alert(d.id(159),null)},onClear(){this.input=JSON.parse(JSON.stringify(X))},onSubmit(){this.submit()},submit(){if(!this.input.prompt){L.warning(d.id(121));return}this.$parent.submitRequest(this.input)&&(this.input.prompt="",this.input.negativePrompt="")}}};const re=t=>(R("data-v-5225cccc"),t=t(),J(),t),Et={id:"R-SD"},Ot={class:"tab"},Pt={class:"td2"},kt={class:"td2"},Dt=re(()=>l("td",null,"CFG Scale",-1)),Ct={class:"td2"},$t=["title"],zt={class:"td2"},Tt=re(()=>l("td",null,"CLIP Guidance",-1)),At={style:{display:"flex"}},xt={class:"op_buttons"},Rt=["disabled"],Jt=["disabled"];function Mt(t,e,r,o,n,s){const p=le,u=K,g=H,w=fe,f=_e,V=be,O=he,T=I("icon-help");return h(),v("div",Et,[i(p,{modelValue:n.input.prompt,"onUpdate:modelValue":e[0]||(e[0]=a=>n.input.prompt=a),rows:3,type:"textarea",maxlength:2e3,"show-word-limit":"",placeholder:t.Lang.id(121)},null,8,["modelValue","placeholder"]),i(p,{modelValue:n.input.negativePrompt,"onUpdate:modelValue":e[1]||(e[1]=a=>n.input.negativePrompt=a),style:{"margin-top":"4px"},rows:3,type:"textarea",maxlength:2e3,"show-word-limit":"",placeholder:t.Lang.id(147)},null,8,["modelValue","placeholder"]),l("table",Ot,[l("tr",null,[l("td",null,m(t.Lang.n.model),1),l("td",null,m(t.G.MODELS[1])+" "+m(n.input.version),1)]),l("tr",null,[l("td",null,m(t.Lang.n.version),1),l("td",null,[i(g,{modelValue:n.input.version,"onUpdate:modelValue":e[2]||(e[2]=a=>n.input.version=a),fill:"green"},{default:c(()=>[i(u,{size:"small",label:1.6},{default:c(()=>[b("1.6")]),_:1})]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.width),1),l("td",Pt,[i(w,{modelValue:n.input.param.width,"onUpdate:modelValue":e[3]||(e[3]=a=>n.input.param.width=a),min:320,max:1536,size:"small"},null,8,["modelValue"]),i(f,{modelValue:n.input.param.width,"onUpdate:modelValue":e[4]||(e[4]=a=>n.input.param.width=a),min:320,max:1536,size:"small",controls:!1},null,8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.height),1),l("td",kt,[i(w,{modelValue:n.input.param.height,"onUpdate:modelValue":e[5]||(e[5]=a=>n.input.param.height=a),min:320,max:1536,size:"small"},null,8,["modelValue"]),i(f,{modelValue:n.input.param.height,"onUpdate:modelValue":e[6]||(e[6]=a=>n.input.param.height=a),min:320,max:1536,size:"small",controls:!1},null,8,["modelValue"])])]),l("tr",null,[Dt,l("td",Ct,[i(w,{modelValue:n.input.param.cfg_scale,"onUpdate:modelValue":e[7]||(e[7]=a=>n.input.param.cfg_scale=a),min:0,max:35,size:"small"},null,8,["modelValue"]),i(f,{modelValue:n.input.param.cfg_scale,"onUpdate:modelValue":e[8]||(e[8]=a=>n.input.param.cfg_scale=a),min:0,max:35,size:"small",controls:!1},null,8,["modelValue"])])]),l("tr",{title:t.Lang.n.steps2},[l("td",null,m(t.Lang.n.steps),1),l("td",zt,[i(w,{modelValue:n.input.param.steps,"onUpdate:modelValue":e[9]||(e[9]=a=>n.input.param.steps=a),min:10,max:50,size:"small"},null,8,["modelValue"]),i(f,{modelValue:n.input.param.steps,"onUpdate:modelValue":e[10]||(e[10]=a=>n.input.param.steps=a),min:10,max:50,size:"small",controls:!1},null,8,["modelValue"])])],8,$t),l("tr",null,[Tt,l("td",null,[i(O,{modelValue:n.input.param.clip_guidance_preset,"onUpdate:modelValue":e[11]||(e[11]=a=>n.input.param.clip_guidance_preset=a),size:"small"},{default:c(()=>[(h(!0),v(z,null,x(n.clip_guidance_enmu,a=>(h(),A(V,{key:a,value:a},null,8,["value"]))),128))]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.sampler),1),l("td",null,[i(O,{modelValue:n.input.param.sampler,"onUpdate:modelValue":e[12]||(e[12]=a=>n.input.param.sampler=a),size:"small"},{default:c(()=>[(h(!0),v(z,null,x(n.sampler_enmu,a=>(h(),A(V,{key:a,value:a},null,8,["value"]))),128))]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.style),1),l("td",null,[i(O,{modelValue:n.input.param.style_preset,"onUpdate:modelValue":e[13]||(e[13]=a=>n.input.param.style_preset=a),size:"small"},{default:c(()=>[(h(!0),v(z,null,x(n.style_enmu,a=>(h(),A(V,{key:a,value:a},null,8,["value"]))),128))]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.seed),1),l("td",null,[i(f,{modelValue:n.input.param.seed,"onUpdate:modelValue":e[14]||(e[14]=a=>n.input.param.seed=a),min:0,max:4294967295,size:"small",controls:!1},null,8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.supplier),1),l("td",null,[i(g,{modelValue:n.input.supplier,"onUpdate:modelValue":e[15]||(e[15]=a=>n.input.supplier=a),fill:"green"},{default:c(()=>[i(u,{label:"stability",size:"small"},{default:c(()=>[b("stability")]),_:1})]),_:1},8,["modelValue"])])]),l("tr",null,[l("td",null,m(t.Lang.n.apiKey),1),l("td",At,[i(p,{modelValue:n.input.api_key,"onUpdate:modelValue":e[16]||(e[16]=a=>n.input.api_key=a),placeholder:t.Lang.id(120)},null,8,["modelValue","placeholder"]),l("button",{class:"inside_btn",onClick:e[17]||(e[17]=(...a)=>s.onHelpApiKey&&s.onHelpApiKey(...a))},[i(T,{fill:"#000",style:{margin:"0px 4px"}})])])])]),l("div",xt,[l("div",null,[l("button",{class:"op_button",onClick:e[18]||(e[18]=a=>s.onClear()),disabled:r.isDisabledSubmit},m(t.Lang.n.clear),9,Rt)]),l("div",null,[l("button",{class:"op_button op_button_hl2",onClick:e[19]||(e[19]=a=>s.onSubmit()),disabled:r.isDisabledSubmit},m(t.Lang.n.generate),9,Jt)])])])}const jt=C(It,[["render",Mt],["__scopeId","data-v-5225cccc"]]),Y="---1@3$5^7*9)---",Nt={name:"Workspace",components:{LPart:dt,RPartDallE:Vt,RPartSD:jt},data(){return{model:y.MODELS[0],isDisabledSubmit:!1,isAdvanceView:!1}},mounted(){var t=E.getStateItems(_.jobs,1),e=t.length;e&&this.$refs.lp.setCurJob(t[e-1]);var r=E.getStateItems(_.jobs,0,-1,"uuid");r.length>0&&this.refreshJobs(r);var o=y.url.searchParams.get("model")||null,n=y.url.searchParams.get("p")||null,s=y.url.searchParams.get("np")||null,p=y.url.searchParams.get("do")||null;o&&y.MODELS.indexOf(o)!=-1&&(this.model=o,this.$refs[o].input.prompt=n,s&&this.$refs[o].input.negativePrompt!==void 0&&(this.$refs[o].input.negativePrompt=s)),p=="submit"&&this.$refs[this.model].submit()},computed:{hasWorking(){return E.getStateItems(_.jobs,0,1).length}},methods:{fillInputBy(t){var e=this.$refs[t.model];e.input.template==t.template?(e.input=JSON.parse(JSON.stringify(t)),this.model=t.model,L.success(d.id(125))):L.error(d.id(154))},onRefreshJob(t){L.info(d.id(152)),this.refreshJobs([t.uuid],()=>L.success(d.id(125)))},refreshJobs(t,e=null){var r=`${y.API_HOST}${B.api.retrieve}?uid=${y.uid}`;fetch(r,{method:"POST",body:JSON.stringify(t)}).then(o=>o.json()).then(o=>{if(o.err){console.log(o),L.error("【"+o.err.name+"】"+o.err.desc);return}o.items.forEach(n=>{for(let s=0,p=_.jobs.length;s{console.log(o),L.error(o.toString())})},onDelJob(t,e){k.confirm(d.id(151),null).then(()=>{if(t.state==e){L.success(d.id(125));let r=_.jobs.indexOf(t);_.jobs.splice(r,1),N()}}).catch(()=>{})},submitRequest(t){if(this.isDisabledSubmit)return L.warning(d.id(104)),!1;if(E.getStateItems(_.jobs,0).length>=B.concurrentRequests)return k.alert(d.id(148).replace(` 25 | `,"
"),null,{type:"warning",showClose:!1,dangerouslyUseHTMLString:!0}),!1;var e=t.prompt;this.isDisabledSubmit=!0;var r=0,o=`${y.API_HOST}${B.api.create}?uid=${y.uid}`,n=new XMLHttpRequest;return n.open("POST",o),n.send(JSON.stringify(t)),n.onreadystatechange=()=>{n.readyState===XMLHttpRequest.DONE&&n.status},n.onprogress=()=>{for(;;){var s=n.responseText.indexOf(Y,r);if(s==-1)return;this.isDisabledSubmit=!1;let u=JSON.parse(n.responseText.substring(r,s));r=s+Y.length;var p=!1;for(let g=0,w=_.jobs.length;g${d.id(153)}`,null,{type:"error",showClose:!1,dangerouslyUseHTMLString:!0}):g.indexOf("empty_content")!=-1?k.alert(`${d.n.job}:${u.uuid}
${d.id(155)}`,null,{type:"error",showClose:!1,dangerouslyUseHTMLString:!0}):g.indexOf("exceed_max_concurrencies")!=-1?k.confirm(`${d.id(158)}`,null,{type:"info",cancelButtonText:d.id(160),confirmButtonText:d.id(161),showClose:!1,dangerouslyUseHTMLString:!0}).then(()=>{W(!0);const w={lang:d.customValue,prompt:e,format:"",offset:0,limit:100};fetch("https://www.ai-img-gen.com/api/search",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(w)}).then(f=>f.text()).then(f=>{W(!1);let V=E.checkP3(200,f);if(V instanceof Error){alert(V.toString());return}if(V.part1<0){alert(V.part1+` 26 | `+V.part3);return}const O=`https://www.ai-img-gen.com/lang/${d.customValue}/prompt/${encodeURIComponent(e)}/result/${V.part3.data}`;window.open(O)}).catch(f=>{alert("HTTP CODE: "+f)})}).catch(()=>{}):L.error("【"+u.err.name+"】"+g)}if(u.state==0)_.settings.runTimes>3?L.success({message:`${d.id(157)}
${d.n.job}:${u.uuid}`.replace(` 27 | `,"
"),dangerouslyUseHTMLString:!0}):k.alert(`${d.id(157)}
${d.n.job}:${u.uuid}`.replace(` 28 | `,"
"),null,{type:"success",showClose:!1,dangerouslyUseHTMLString:!0}),setTimeout(()=>{let g=this.$refs.R,w=g.scrollTop,f=g.scrollHeight;f&&w!=f&&(g.scrollTop=f)},1);else if(u.state==1)this.$refs.lp.setCurJob(u,!0),this.isAdvanceView=!1;else if(u.state==-1){for(let g=0,w=_.jobs.length;g(R("data-v-382fb8da"),t=t(),J(),t),Bt={id:"my-workspace"},Gt={class:"L"},Ft=ae(()=>l("div",{class:"L-head"},null,-1)),Ht={class:"L-body"},Kt={class:"R",ref:"R"},qt={class:"R-head"},Wt={class:"R-first-line"},Qt={class:"R-body"},Xt={class:"R-foot"},Yt={key:0,class:"fieldset"},Zt={class:"legend"},en=ae(()=>l("img",{src:Ut},null,-1)),tn={style:{"overflow-x":"hidden"}},nn={style:{margin:"0px 0px 8px 0px"}};function ln(t,e,r,o,n,s){const p=I("LPart"),u=te,g=K,w=H,f=I("RPartDallE"),V=I("RPartSD"),O=I("icon-delete"),T=I("icon-refresh"),a=ne;return h(),v("div",Bt,[l("div",Gt,[Ft,l("div",Ht,[i(p,{ref:"lp"},null,512)])]),M(l("div",Kt,[l("div",qt,[l("div",Wt,[t.GB.isPhoneView?M((h(),A(u,{key:0,type:"danger",plain:"",onClick:e[0]||(e[0]=P=>n.isAdvanceView=!1)},{default:c(()=>[b(m(t.Lang.n.home),1)]),_:1},512)),[[j,t.GB.isPhoneView]]):D("",!0),i(w,{modelValue:n.model,"onUpdate:modelValue":e[1]||(e[1]=P=>n.model=P)},{default:c(()=>[i(g,{label:t.G.MODELS[0]},{default:c(()=>[b("DALL·E")]),_:1},8,["label"]),i(g,{label:t.G.MODELS[1]},{default:c(()=>[b("SD")]),_:1},8,["label"])]),_:1},8,["modelValue"])])]),l("div",Qt,[M(i(f,{ref:t.G.MODELS[0],isDisabledSubmit:n.isDisabledSubmit},null,8,["isDisabledSubmit"]),[[j,n.model==t.G.MODELS[0]]]),M(i(V,{ref:t.G.MODELS[1],isDisabledSubmit:n.isDisabledSubmit},null,8,["isDisabledSubmit"]),[[j,n.model==t.G.MODELS[1]]])]),l("div",Xt,[s.hasWorking?(h(),v("div",Yt,[l("div",Zt,m(t.Lang.id(103)),1),(h(!0),v(z,null,x(t.Kit.getStateItems(t.GB.jobs,0),P=>(h(),v("div",{class:"working-item",key:P.uuid},[en,i(a,{placement:t.GB.isPhoneView?"top-start":"right-start",title:t.Lang.n.inputInfo,width:348,"popper-class":"gallery-info-popover",trigger:"click"},{reference:c(()=>[l("span",null,m(t.Lang.n.job)+":"+m(P.uuid),1)]),default:c(()=>[l("div",tn,[l("pre",nn,m(t.Kit.formatInputInfo(P.I)),1),i(u,{type:"primary",plain:"",onClick:ue=>s.onDelJob(P,0),title:t.Lang.id(149)},{default:c(()=>[i(O,{size:"16"})]),_:2},1032,["onClick","title"]),i(u,{type:"primary",plain:"",onClick:ue=>s.onRefreshJob(P),title:t.Lang.id(150)},{default:c(()=>[i(T,{size:"16"})]),_:2},1032,["onClick","title"])])]),_:2},1032,["placement","title"])]))),128))])):D("",!0)])],512),[[j,!t.GB.isPhoneView||n.isAdvanceView]])])}const sn=C(Nt,[["render",ln],["__scopeId","data-v-382fb8da"]]),on={name:"AppRoot",components:{FrameHead:xe,FrameBody:je,Workspace:sn},mounted(){window.dispatchEvent(new CustomEvent("VueMounted"))}};const rn=t=>(R("data-v-041fc681"),t=t(),J(),t),an={id:"my-body"},un=rn(()=>l("div",{id:"my-model-dialog"},null,-1));function dn(t,e,r,o,n,s){const p=I("FrameHead"),u=I("Workspace"),g=I("FrameBody");return h(),v("div",an,[i(p),i(g,null,{default:c(()=>[i(u)]),_:1}),un])}const pn=C(on,[["render",dn],["__scopeId","data-v-041fc681"]]);var Z=null;function mn(t){return Z=ye(t),Z}function cn(t,e){t.component("icon-info",ve),t.component("icon-close-one",we),t.component("icon-help",Se),t.component("icon-delete",Le),t.component("icon-copy",Ve),t.component("icon-refresh",Ie),t.component("icon-fill",Ee),t.mount(e)}const gn=`name:string,iso_639_3166:strings,langField:string\r 29 | english,en,en\r 30 | español,es,es\r 31 | portugués,pt,pt\r 32 | français,fr,fr\r 33 | Türkçe,tr,tr\r 34 | русский,ru,ru\r 35 | Indonesia,id,ind\r 36 | 简体中文,zh,zh\r 37 | 繁體中文,"zh-Hant,zh-TW,zh-HK,zh-MO",zh-Hant\r 38 | 日本語,ja,ja\r 39 | `,fn=`\uFEFFid:int,name:string,en:string,zh:string,zh-Hant:string,es:string,pt:string,fr:string,tr:string,ru:string,ind:string,ja:string\r 40 | 101,logo,AI Image Generator,AI图片生成,AI繪圖生成,Generador de Imágenes de IA,Gerador de Imagem de IA,Générateur d'Images IA,Yapay Zeka Görüntü Oluşturucusu,ИИ-генератор изображений,Generator Gambar AI,AI画像生成\r 41 | 102,prompt,prompt,提示词,提示詞,,,,,,,\r 42 | 103,,Drawing..,绘图中..,繪圖中..,,,,,,,\r 43 | 104,,Please wait for current request to complete,请等待当前请求完成,請等待目前請求完成,,,,,,,\r 44 | 105,version,version,版本,版本,,,,,,,\r 45 | 106,size,size,尺寸,尺寸,,,,,,,\r 46 | 107,,square,方块,方塊,,,,,,,\r 47 | 108,,banner,横幅,橫幅,,,,,,,\r 48 | 109,,poster,海报,海報,,,,,,,\r 49 | 110,quality,quality,画质,畫質,,,,,,,\r 50 | 111,,standard ,标准,標準,,,,,,,\r 51 | 112,,HD,高清,高清,,,,,,,\r 52 | 113,style,style,风格,風格,,,,,,,\r 53 | 114,,vivid,生动,生動,,,,,,,\r 54 | 115,,natural,真实,真實,,,,,,,\r 55 | 116,supplier,supplier,供应商,供應商,,,,,,,\r 56 | 117,apiKey,API Key,API密钥,API金鑰,,,,,,,\r 57 | 118,clear,Clear,清空,清空,,,,,,,\r 58 | 119,generate,Generate,生成,生成,,,,,,,\r 59 | 120,,optional,可选,可選,,,,,,,\r 60 | 121,,Please input your prompt,请输入您的提示词,請輸入您的提示詞,,,,,,,\r 61 | 122,home,Home,主页,主页,,,,,,,\r 62 | 123,delete,Delete,删除,刪除,,,,,,,\r 63 | 124,,Are you sure you want to delete this image?,您确定要删除该图片吗?,您確定要刪除該圖片嗎?,,,,,,,\r 64 | 125,,Operation successful,操作成功,操作成功,,,,,,,\r 65 | 126,date,Date,日期,日期,,,,,,,\r 66 | 127,apiVer,API Ver,API版本,API版本,,,,,,,\r 67 | 128,endpoint,endpoint,,,,,,,,,\r 68 | 129,,Please fill in required fields,请填写必填字段,請填寫必填字段,,,,,,,\r 69 | 130,,required,必填,必填,,,,,,,\r 70 | 131,imgInfo,Image info,图片信息,圖片訊息,,,,,,,\r 71 | 132,inputInfo,Generated info,生成信息,生成訊息,,,,,,,\r 72 | 133,outputInfo,Raw response,原始响应,原始回應,,,,,,,\r 73 | 134,imgInfo1,Image,图片,圖片,,,,,,,\r 74 | 135,inputInfo1,Gen,生成,生成,,,,,,,\r 75 | 136,outputInfo1,Raw,原始,原始,,,,,,,\r 76 | 137,model,model,模型,模型,,,,,,,\r 77 | 138,elapsed,Elapsed,耗时,耗時,,,,,,,\r 78 | 139,width,width,宽度,寬度,,,,,,,\r 79 | 140,height,height,高度,高度,,,,,,,\r 80 | 141,sampler,sampler,采样器,採樣器,,,,,,,\r 81 | 143,seed,seed,种子,種子,,,,,,,\r 82 | 144,steps,steps,步数,步數,,,,,,,\r 83 | 145,steps2,Number of diffusion steps to run,要运行的扩散步数,要運行的擴散步數,,,,,,,\r 84 | 146,negativePrompt,negative prompt,否定提示词,否定提示詞,,,,,,,\r 85 | 147,,(optional) negative prompt,(可选) 否定提示词,(可選) 否定提示詞,,,,,,,\r 86 | 148,,"Exceeded max number of tasks,\r 87 | Please try again later.","超过最大任务数,\r 88 | 请稍后再试。","超過最大任務數,\r 89 | 請稍後再試。",,,,,,,\r 90 | 149,,Delete this task,删除此任务,刪除此任務,,,,,,,\r 91 | 150,,Refresh this task status,刷新此任务状态,重新整理此任務狀態,,,,,,,\r 92 | 151,,Are you sure you want to delete this task?,您确定要删除此任务吗?,您確定要刪除此任務嗎?,,,,,,,\r 93 | 152,,Refreshing..,刷新中..,刷新中..,,,,,,,\r 94 | 153,,This content violates the usage policy,该内容违反了使用政策,該內容違反了使用政策,,,,,,,\r 95 | 154,,The current version does not support this generated information,当前版本不支持该生成信息,目前版本不支援該生成訊息,,,,,,,\r 96 | 155,,Unable to generate content,无法生成内容,無法生成內容,,,,,,,\r 97 | 156,job,Task,任务,任務,,,,,,,\r 98 | 157,,"Your request has been added to the task,\r 99 | Please wait for the drawing to be completed.",您的请求已添加到任务中,请等待绘图完成。,您的請求已添加到任務中,請等待繪圖完成。,,,,,,,\r 100 | 158,,"Free drawing tasks are full, please try again later.",免费的绘图任务已满,请稍后再试。,免費的繪圖任務已滿,請稍後再試。,"Las tareas de dibujo gratuitas están completas, inténtalo de nuevo más tarde.",As tarefas de desenho gratuitas estão completas. Tente novamente mais tarde.,"Les tâches de dessin gratuites sont terminées, veuillez réessayer plus tard.","Ücretsiz çizim görevleri doldu, lütfen daha sonra tekrar deneyin.","Бесплатные задания по рисованию заполнены, повторите попытку позже.","Tugas menggambar gratis sudah penuh, silakan coba lagi nanti.",無料の描画タスクは満員です、後で再度お試しください。\r 101 | 159,,"When it is empty, the submission is a free drawing task.",当它为空时,提交的是免费的绘图任务,當它為空時,提交的是免費的繪圖任務,"Cuando está vacío, el envío es una tarea de dibujo gratuita.","Quando estiver vazio, o envio é uma tarefa de desenho livre.","Lorsqu'il est vide, la soumission est une tâche de dessin gratuite.","Boş olduğunda, gönderim ücretsiz bir çizim görevidir.","Если он пуст, это означает, что отправка представляет собой бесплатное задание по рисованию.","Jika kosong, penyerahannya adalah tugas menggambar gratis.",空の場合、提出は無料の描画タスクです。\r 102 | 160,,Cancel,取消,取消,,,,,,,\r 103 | 161,,Find it,搜索它,搜索它,Encuéntralo,Encontre,Trouve le,Bul onu,Найди это,Temukan,それを見つける\r 104 | `;function _n(){d.Init(acsv.Table.Parse(gn),acsv.Table.Parse(fn)),d.customValue=d.getLangFieldBy(navigator.language),hn(),ee();var t=mn(pn);t.config.globalProperties.G=y,t.config.globalProperties.GB=_,t.config.globalProperties.Kit=E,t.config.globalProperties.Lang=d,cn(t,"#my-app"),window.onbeforeunload=e=>ke(),window.addEventListener("resize",ee),document.addEventListener("gesturestart",e=>e.preventDefault()),document.oncontextmenu=e=>{e.target instanceof HTMLInputElement||e.target instanceof HTMLTextAreaElement||e.target.className.indexOf("el-popper")!=-1||e.target.className.indexOf("el-image")!=-1||e.preventDefault()}}function hn(){y.url=new URL(window.location.href),y.uid=localStorage.getItem("uid")||E.genUID(),E.fillInto(De(),_.settings,!0),_.settings.runTimes++,_.jobs=Ce(),localStorage.setItem("uid",y.uid)}function ee(){var t=0,e=5;window.matchMedia("(min-width: 576px)").matches&&(t=1),window.matchMedia("(min-width: 768px)").matches&&(t=2),window.matchMedia("(min-width: 992px)").matches&&(t=3),window.matchMedia("(min-width: 1200px)").matches&&(t=4),window.matchMedia("(max-width: 1199.98px)").matches&&(e=4),window.matchMedia("(max-width: 991.98px)").matches&&(e=3),window.matchMedia("(max-width: 767.98px)").matches&&(e=2),window.matchMedia("(max-width: 575.98px)").matches&&(e=1),_.isTabletView=t<3,_.isPhoneView=e<2,_.isTouchDevice=navigator.maxTouchPoints>0}_n(); 105 | -------------------------------------------------------------------------------- /wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/wwwroot/favicon.ico -------------------------------------------------------------------------------- /wwwroot/font/digitW.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/wwwroot/font/digitW.ttf -------------------------------------------------------------------------------- /wwwroot/font/logo2.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amin2312/AI-Image-Generator/d101a63381dae95ba5539501a9a39afe763c4528/wwwroot/font/logo2.otf -------------------------------------------------------------------------------- /wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | AI Image Generator - unleash your Creativity 13 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Loding
30 |
31 | 32 | 33 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /wwwroot/js/ACsv.min.js: -------------------------------------------------------------------------------- 1 | (function(e){"use strict";e["acsv"]=e["acsv"]||{};var g=function(){};g.replace=function(e,t,r){return e.split(t).join(r)};var x=e["acsv"]["Field"]=function(){};var A=e["acsv"]["Table"]=function(){this._selector=null;this._indexSet={};this.body=[];this.head=[];this.content=null};A.Parse=function(e,t,r){if(r==null){r='"'}if(t==null){t=","}var i=A.arrayToRows(A.textToArray(e,t,r));i.content=e;return i};A.textToArray=function(e,t,r){if(r==null){r='"'}if(t==null){t=","}if(e.charCodeAt(0)==65279){e=e.substring(1)}var i=r+r;var l=[];var n=e.length;var s=e;var a=0;while(true){var h=n-a;var o=0;var u=0;var v=[];var f=null;var c=null;while(u=n){break}}return l};A.arrayToRows=function(e){var t=e.shift();var r=e;var i=[];var l=0;var n=t.length;while(lv){o=true}else if(t==1&&u0&&A.JSON_TYPES.indexOf(s)!=-1){if(a!=null){h=JSON.parse(a)}}else{h=a}t.push(h)}return t},fmtObj:function(e){var t={};var r=0;var i=this.head.length;while(r0&&A.JSON_TYPES.indexOf(a)!=-1){if(h!=null){o=JSON.parse(h)}}else{o=h}t[s]=o}return t},toFirstRow:function(){var e=null;if(this._selector!=null&&this._selector.length>0){e=this.fmtRow(this._selector[0])}this._selector=null;return e},toLastRow:function(){var e=null;if(this._selector!=null){var t=this._selector.length;if(t>0){e=this.fmtRow(this._selector[t-1])}}this._selector=null;return e},toRows:function(){if(this._selector==null){return null}var e=[];var t=0;var r=this._selector.length;while(t0){e=this.fmtObj(this._selector[0])}this._selector=null;return e},toLastObj:function(){var e=null;if(this._selector!=null){var t=this._selector.length;if(t>0){e=this.fmtObj(this._selector[t-1])}}this._selector=null;return e},toObjs:function(){if(this._selector==null){return null}var e=[];var t=0;var r=this._selector.length;while(t=0&&sr||t&&u==r){n.push(o);--e;if(e==0){break}}}this._selector=n;return this},selectWhenL:function(e,t,r,i){if(i==null){i=0}var l=this._selector;if(l==null){l=this.body}var n=[];var s=0;var a=l.length;while(si||t&&f==i;var _=fl||r&&f==l;if(c||_){a.push(v);--e;if(e==0){break}}}this._selector=a;return this}};A.JSON_TYPES=["json","strings"]})(typeof exports!="undefined"?exports:typeof window!="undefined"?window:typeof self!="undefined"?self:this); -------------------------------------------------------------------------------- /wwwroot/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------