mirror of
https://github.com/Anil-matcha/Open-Generative-AI.git
synced 2026-05-07 01:17:18 +00:00
Merge pull request #77 from Assem-ElQersh/main
feat: add Linux (Ubuntu) desktop build support
This commit is contained in:
commit
57cb24bcbc
8 changed files with 239 additions and 113 deletions
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -22,3 +22,17 @@ dist-ssr
|
|||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Electron build output
|
||||
release/
|
||||
.webpack/
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# Misc
|
||||
*.pem
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
|
|
|||
46
README.md
46
README.md
|
|
@ -23,6 +23,7 @@ One-click installers — no Node.js or terminal required.
|
|||
| macOS Apple Silicon (M1/M2/M3/M4) | [Open Generative AI-1.0.0-arm64.dmg](https://github.com/Anil-matcha/Open-Generative-AI/releases/download/v1.0.0/Open.Generative.AI-1.0.0-arm64.dmg) |
|
||||
| macOS Intel (x64) | [Open Generative AI-1.0.0.dmg](https://github.com/Anil-matcha/Open-Generative-AI/releases/download/v1.0.0/Open.Generative.AI-1.0.0.dmg) |
|
||||
| Windows (x64 + ARM64) | [Open Generative AI Setup 1.0.0.exe](https://github.com/Anil-matcha/Open-Generative-AI/releases/download/v1.0.0/Open.Generative.AI.Setup.1.0.0.exe) |
|
||||
| Linux (Ubuntu x64) | Build locally with `npm run electron:build:linux` |
|
||||
|
||||
All releases: [github.com/Anil-matcha/Open-Generative-AI/releases](https://github.com/Anil-matcha/Open-Generative-AI/releases)
|
||||
|
||||
|
|
@ -56,6 +57,48 @@ Windows SmartScreen may show a warning because the installer is not code-signed:
|
|||
|
||||
The app will install silently to `%LocalAppData%` with a Start Menu shortcut.
|
||||
|
||||
### Ubuntu / Linux Installation
|
||||
|
||||
Linux artifacts are available when building with Electron Builder:
|
||||
|
||||
```bash
|
||||
# Build Linux installers (AppImage + .deb)
|
||||
npm run electron:build:linux
|
||||
```
|
||||
|
||||
Generated files are written to the `release/` folder:
|
||||
- **AppImage** — portable, run directly after making executable:
|
||||
```bash
|
||||
chmod +x "release/Open Generative AI-*.AppImage"
|
||||
./release/Open\ Generative\ AI-*.AppImage
|
||||
```
|
||||
- **.deb** — install on Debian/Ubuntu:
|
||||
```bash
|
||||
sudo apt install ./release/open-generative-ai_*_amd64.deb
|
||||
```
|
||||
|
||||
If AppImage fails to start on older systems, install `libfuse2`:
|
||||
|
||||
```bash
|
||||
sudo apt install libfuse2
|
||||
```
|
||||
|
||||
#### Ubuntu 24.04+ / AppArmor sandbox restriction
|
||||
|
||||
Ubuntu 24.04 and later enable a kernel security policy (`apparmor_restrict_unprivileged_userns`) that blocks Chromium's user-namespace sandbox. If the app fails to start silently or crashes immediately, you have two options:
|
||||
|
||||
**Option A — Recommended: install the `.deb` instead.**
|
||||
The `.deb` package ships an AppArmor profile that grants the required permission automatically on install with no system-wide changes.
|
||||
|
||||
**Option B — Temporary system fix (AppImage users):**
|
||||
```bash
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
```
|
||||
This lasts until next reboot. To make it permanent:
|
||||
```bash
|
||||
echo 'kernel.apparmor_restrict_unprivileged_userns=0' | sudo tee /etc/sysctl.d/99-userns.conf
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Open Generative AI is an open-source AI image, video, cinema, and lip sync studio that brings creative workflows to everyone. Powered by [Muapi.ai](https://muapi.ai), it supports text-to-image, image-to-image, text-to-video, image-to-video, and audio-driven lip sync generation across models like Flux, Nano Banana, Midjourney, Kling, Sora, Veo, Seedream, Infinite Talk, LTX Lipsync, Wan 2.2, and more — all from a sleek, modern interface you can self-host and customize.
|
||||
|
|
@ -251,6 +294,9 @@ npm run electron:build
|
|||
# Windows (NSIS installer — x64 + ARM64)
|
||||
npm run electron:build:win
|
||||
|
||||
# Linux (AppImage + DEB — x64)
|
||||
npm run electron:build:linux
|
||||
|
||||
# Both platforms in one pass
|
||||
npm run electron:build:all
|
||||
```
|
||||
|
|
|
|||
7
build/linux/apparmor.profile
Normal file
7
build/linux/apparmor.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
abi <abi/4.0>,
|
||||
include <tunables/global>
|
||||
|
||||
profile open-generative-ai /opt/Open\ Generative\ AI/open-generative-ai flags=(unconfined) {
|
||||
userns,
|
||||
include if exists <local/open-generative-ai>
|
||||
}
|
||||
|
|
@ -1,33 +1,46 @@
|
|||
import { app, BrowserWindow, shell } from 'electron';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
const { app, BrowserWindow, shell } = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
// Ubuntu 24.04+ sets kernel.apparmor_restrict_unprivileged_userns=1 which
|
||||
// blocks Chromium's user namespace sandbox. The .deb package ships an AppArmor
|
||||
// profile that grants the permission cleanly. When running the AppImage on an
|
||||
// affected system, run once: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
// or pass --no-sandbox on the command line.
|
||||
if (process.platform === 'linux') {
|
||||
app.commandLine.appendSwitch('disable-dev-shm-usage');
|
||||
}
|
||||
|
||||
let mainWindow;
|
||||
|
||||
function createWindow() {
|
||||
const isMac = process.platform === 'darwin';
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1440,
|
||||
height: 900,
|
||||
minWidth: 1024,
|
||||
minHeight: 640,
|
||||
webPreferences: {
|
||||
webSecurity: false, // Allow file:// origin to call external APIs
|
||||
webSecurity: false,
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
titleBarStyle: 'hiddenInset',
|
||||
...(isMac ? { titleBarStyle: 'hiddenInset' } : {}),
|
||||
backgroundColor: '#0d0d0d',
|
||||
show: false,
|
||||
title: 'Open Generative AI',
|
||||
});
|
||||
|
||||
const indexPath = path.join(__dirname, '../dist/index.html');
|
||||
mainWindow.loadFile(indexPath);
|
||||
mainWindow.loadFile(indexPath).catch((err) => {
|
||||
console.error('Failed to load index.html:', err);
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
mainWindow.webContents.on('did-fail-load', (event, code, desc) => {
|
||||
console.error('did-fail-load:', code, desc);
|
||||
});
|
||||
|
||||
// Open external links in the system browser
|
||||
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
||||
shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
|
|
|
|||
163
package-lock.json
generated
163
package-lock.json
generated
|
|
@ -27,7 +27,7 @@
|
|||
"eslint": "^9",
|
||||
"eslint-config-next": "^15.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"vite": "^5.4.0"
|
||||
}
|
||||
},
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -10062,7 +10063,8 @@
|
|||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
|
|
@ -12778,82 +12780,11 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz",
|
||||
"integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.7",
|
||||
"lilconfig": "^3.1.3",
|
||||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.35.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss/node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss/node_modules/jiti": {
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tapable": {
|
||||
"version": "2.3.0",
|
||||
|
|
@ -13791,6 +13722,84 @@
|
|||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"packages/studio/node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"packages/studio/node_modules/fast-glob/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"packages/studio/node_modules/jiti": {
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"packages/studio/node_modules/tailwindcss": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.7",
|
||||
"lilconfig": "^3.1.3",
|
||||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.35.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
40
package.json
40
package.json
|
|
@ -17,7 +17,8 @@
|
|||
"vite:build": "vite build",
|
||||
"electron:build": "vite build && electron-builder --mac",
|
||||
"electron:build:win": "vite build && electron-builder --win",
|
||||
"electron:build:all": "vite build && electron-builder --mac --win"
|
||||
"electron:build:linux": "vite build && electron-builder --linux",
|
||||
"electron:build:all": "vite build && electron-builder --mac --win --linux"
|
||||
},
|
||||
"build": {
|
||||
"appId": "ai.generative.open",
|
||||
|
|
@ -56,27 +57,52 @@
|
|||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"icon": "public/banner.png",
|
||||
"category": "Utility",
|
||||
"maintainer": "Open Generative AI Team",
|
||||
"extraFiles": [
|
||||
{
|
||||
"from": "build/linux/apparmor.profile",
|
||||
"to": "resources/apparmor.profile"
|
||||
}
|
||||
],
|
||||
"target": [
|
||||
{
|
||||
"target": "AppImage",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"target": "deb",
|
||||
"arch": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.0",
|
||||
"next": "^15.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"studio": "*",
|
||||
"axios": "^1.7.0",
|
||||
"react-hot-toast": "^2.4.1"
|
||||
"react-hot-toast": "^2.4.1",
|
||||
"studio": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"autoprefixer": "^10.4.24",
|
||||
"electron": "^33.4.11",
|
||||
"electron-builder": "^25.1.8",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "^15.0.0",
|
||||
"postcss": "^8.5.6",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"vite": "^5.4.0",
|
||||
"@tailwindcss/vite": "^4.1.18"
|
||||
"tailwindcss": "^4.2.2",
|
||||
"vite": "^5.4.0"
|
||||
},
|
||||
"main": "electron/main.js"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,30 +3,30 @@ import { CAMERA_MAP, LENS_MAP, FOCAL_PERSPECTIVE, APERTURE_EFFECT } from '../lib
|
|||
|
||||
const ASSET_URLS = {
|
||||
// CAMERA
|
||||
"Modular 8K Digital": "/assets/cinema/modular_8k_digital.webp",
|
||||
"Full-Frame Cine Digital": "/assets/cinema/full_frame_cine_digital.webp",
|
||||
"Grand Format 70mm Film": "/assets/cinema/grand_format_70mm_film.webp",
|
||||
"Studio Digital S35": "/assets/cinema/studio_digital_s35.webp",
|
||||
"Classic 16mm Film": "/assets/cinema/classic_16mm_film.webp",
|
||||
"Premium Large Format Digital": "/assets/cinema/premium_large_format_digital.webp",
|
||||
"Modular 8K Digital": "./assets/cinema/modular_8k_digital.webp",
|
||||
"Full-Frame Cine Digital": "./assets/cinema/full_frame_cine_digital.webp",
|
||||
"Grand Format 70mm Film": "./assets/cinema/grand_format_70mm_film.webp",
|
||||
"Studio Digital S35": "./assets/cinema/studio_digital_s35.webp",
|
||||
"Classic 16mm Film": "./assets/cinema/classic_16mm_film.webp",
|
||||
"Premium Large Format Digital": "./assets/cinema/premium_large_format_digital.webp",
|
||||
|
||||
// LENS
|
||||
"Creative Tilt Lens": "/assets/cinema/creative_tilt_lens.webp",
|
||||
"Compact Anamorphic": "/assets/cinema/compact_anamorphic.webp",
|
||||
"Extreme Macro": "/assets/cinema/extreme_macro.webp",
|
||||
"70s Cinema Prime": "/assets/cinema/70s_cinema_prime.webp",
|
||||
"Classic Anamorphic": "/assets/cinema/classic_anamorphic.webp",
|
||||
"Premium Modern Prime": "/assets/cinema/premium_modern_prime.webp",
|
||||
"Warm Cinema Prime": "/assets/cinema/warm_cinema_prime.webp",
|
||||
"Swirl Bokeh Portrait": "/assets/cinema/swirl_bokeh_portrait.webp",
|
||||
"Vintage Prime": "/assets/cinema/vintage_prime.webp",
|
||||
"Halation Diffusion": "/assets/cinema/halation_diffusion.webp",
|
||||
"Clinical Sharp Prime": "/assets/cinema/clinical_sharp_prime.webp",
|
||||
"Creative Tilt Lens": "./assets/cinema/creative_tilt_lens.webp",
|
||||
"Compact Anamorphic": "./assets/cinema/compact_anamorphic.webp",
|
||||
"Extreme Macro": "./assets/cinema/extreme_macro.webp",
|
||||
"70s Cinema Prime": "./assets/cinema/70s_cinema_prime.webp",
|
||||
"Classic Anamorphic": "./assets/cinema/classic_anamorphic.webp",
|
||||
"Premium Modern Prime": "./assets/cinema/premium_modern_prime.webp",
|
||||
"Warm Cinema Prime": "./assets/cinema/warm_cinema_prime.webp",
|
||||
"Swirl Bokeh Portrait": "./assets/cinema/swirl_bokeh_portrait.webp",
|
||||
"Vintage Prime": "./assets/cinema/vintage_prime.webp",
|
||||
"Halation Diffusion": "./assets/cinema/halation_diffusion.webp",
|
||||
"Clinical Sharp Prime": "./assets/cinema/clinical_sharp_prime.webp",
|
||||
|
||||
// APERTURE
|
||||
"f/1.4": "/assets/cinema/f_1_4.webp",
|
||||
"f/4": "/assets/cinema/f_4.webp",
|
||||
"f/11": "/assets/cinema/f_11.webp"
|
||||
"f/1.4": "./assets/cinema/f_1_4.webp",
|
||||
"f/4": "./assets/cinema/f_4.webp",
|
||||
"f/11": "./assets/cinema/f_11.webp"
|
||||
};
|
||||
|
||||
export function CameraControls(onChange) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,17 @@ import { AuthModal } from './AuthModal.js';
|
|||
import { createUploadPicker } from './UploadPicker.js';
|
||||
import { savePendingJob, removePendingJob, getPendingJobs } from '../lib/pendingJobs.js';
|
||||
|
||||
function createInlineInstructions(type) {
|
||||
const el = document.createElement('div');
|
||||
el.className = 'w-full text-center text-white/30 text-sm flex flex-col items-center gap-2 py-2';
|
||||
const icon = type === 'image' ? '🖼️' : '🎬';
|
||||
el.innerHTML = `
|
||||
<p>${icon} Enter a prompt above and click <span class="text-primary font-semibold">Generate</span> to create your ${type}.</p>
|
||||
<p class="text-xs text-white/20">Tip: Be descriptive — include style, lighting, mood, and subject for best results.</p>
|
||||
`;
|
||||
return el;
|
||||
}
|
||||
|
||||
export function ImageStudio() {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'w-full h-full flex flex-col items-center justify-center bg-app-bg relative p-4 md:p-6 overflow-y-auto custom-scrollbar overflow-x-hidden';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue