diff --git a/examples/runware-demo/esbuild/config.mjs b/examples/runware-demo/esbuild/config.mjs new file mode 100644 index 00000000..6ee10d64 --- /dev/null +++ b/examples/runware-demo/esbuild/config.mjs @@ -0,0 +1,56 @@ +import chalk from 'chalk'; +import { readFile } from 'fs/promises'; +import dotenv from 'dotenv'; + +import baseConfig from '../../../esbuild/config.mjs'; +import log from '../../../esbuild/log.mjs'; + +// import packageJson from '../package.json' assert { type: 'json' }; +// Avoid the Experimental Feature warning when using the above. +const packageJson = JSON.parse( + await readFile(new URL('../package.json', import.meta.url)) +); + +// Load .env.local only in local development (not in CI/Vercel) +if (!process.env.CI && !process.env.VERCEL) { + dotenv.config({ path: '.env.local' }); +} + +export default ({ isDevelopment }) => { + log( + `${chalk.yellow('Building version:')} ${chalk.bold(packageJson.version)}` + ); + + // Base configuration that applies to all builds + const baseOptions = { + isDevelopment, + external: [], + pluginVersion: packageJson.version + }; + + // Get the base configuration + const config = baseConfig(baseOptions); + + // Set entry points and output configuration + config.entryPoints = ['./src/index.ts']; + config.outExtension = { '.js': '.mjs' }; + config.outdir = './dist'; + config.outbase = './src'; + config.outfile = undefined; + config.define = { + ...config.define, + 'process.env.CESDK_LICENSE': JSON.stringify( + 'CESDK_LICENSE' in process.env ? process.env.CESDK_LICENSE : '' + ), + 'process.env.RUNWARE_PROXY_URL': JSON.stringify( + 'RUNWARE_PROXY_URL' in process.env ? process.env.RUNWARE_PROXY_URL : '' + ), + 'process.env.ANTHROPIC_PROXY_URL': JSON.stringify( + 'ANTHROPIC_PROXY_URL' in process.env + ? process.env.ANTHROPIC_PROXY_URL + : '' + ) + }; + + return config; +}; diff --git a/examples/runware-demo/esbuild/global.d.ts b/examples/runware-demo/esbuild/global.d.ts new file mode 100644 index 00000000..b2d2a926 --- /dev/null +++ b/examples/runware-demo/esbuild/global.d.ts @@ -0,0 +1,11 @@ +// These constants here are added by the base esbuild config + +declare const PLUGIN_VERSION: string; + +declare namespace NodeJS { + interface ProcessEnv { + CESDK_LICENSE: string; + RUNWARE_PROXY_URL: string; + ANTHROPIC_PROXY_URL: string; + } +} diff --git a/examples/runware-demo/index.html b/examples/runware-demo/index.html new file mode 100644 index 00000000..33ddd997 --- /dev/null +++ b/examples/runware-demo/index.html @@ -0,0 +1,208 @@ + + + + + + CE.SDK with Runware + + + + +
+
+
+ +

Runware Demo

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/examples/runware-demo/package.json b/examples/runware-demo/package.json new file mode 100644 index 00000000..bdf2e0f5 --- /dev/null +++ b/examples/runware-demo/package.json @@ -0,0 +1,66 @@ +{ + "private": true, + "name": "@imgly/plugin-runware-demo-web", + "version": "0.0.0", + "description": "Runware Demo", + "keywords": ["CE.SDK", "plugin", "AI", "Runware"], + "repository": { + "type": "git", + "url": "git+https://github.com/imgly/plugins.git" + }, + "license": "SEE LICENSE IN LICENSE.md", + "author": { + "name": "IMG.LY GmbH", + "email": "support@img.ly", + "url": "https://img.ly" + }, + "bugs": { + "email": "support@img.ly" + }, + "source": "./src/index.ts", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.mjs", + "types": "./dist/index.d.ts" + } + }, + "homepage": "https://img.ly/products/creative-sdk", + "files": ["LICENSE.md", "README.md", "CHANGELOG.md", "dist/", "bin/"], + "scripts": { + "start": "npm run watch", + "clean": "pnpm exec rimraf dist", + "purge": "pnpm exec rimraf node_modules", + "build": "pnpm run clean && pnpm exec node scripts/build.mjs", + "test": "echo No tests", + "dev": "node scripts/watch.mjs", + "dev:wait": "pnpm exec wait-on ./dist/index.mjs ./dist/index.d.ts --window 250 --timeout 60000", + "dev:types": "tsc --emitDeclarationOnly --watch --preserveWatchOutput", + "publish:latest": "pnpm run build && npm publish --tag latest --access public", + "publish:next": "pnpm run build && npm publish --tag next --access public", + "check:all": "concurrently -n type,pretty \"pnpm run check:types\" \"pnpm run check:pretty\"", + "check:pretty": "prettier --list-different './src/**/*.{ts,tsx}'", + "check:types": "tsc --noEmit", + "types:create": "tsc --emitDeclarationOnly" + }, + "devDependencies": { + "@types/ndarray": "^1.0.14", + "chalk": "^5.3.0", + "concurrently": "^8.2.2", + "esbuild": "^0.19.11", + "eslint": "^8.51.0", + "lodash-es": "^4.17.21", + "typescript": "^5.3.3" + }, + "dependencies": { + "@cesdk/cesdk-js": "^1.66.1", + "@cesdk/engine": "^1.66.1", + "@imgly/plugin-ai-apps-web": "^0.2.15", + "@imgly/plugin-ai-generation-web": "^0.2.15", + "@imgly/plugin-ai-image-generation-web": "^0.2.15", + "@imgly/plugin-ai-text-generation-web": "^0.2.15", + "@imgly/plugin-ai-video-generation-web": "^0.2.15", + "dotenv": "^16.5.0" + } +} diff --git a/examples/runware-demo/scripts/build.mjs b/examples/runware-demo/scripts/build.mjs new file mode 100644 index 00000000..b8e529d8 --- /dev/null +++ b/examples/runware-demo/scripts/build.mjs @@ -0,0 +1,4 @@ +import * as esbuild from 'esbuild'; +import config from '../esbuild/config.mjs'; + +await esbuild.build(config({ isDevelopment: false })); diff --git a/examples/runware-demo/scripts/watch.mjs b/examples/runware-demo/scripts/watch.mjs new file mode 100644 index 00000000..9deaeca2 --- /dev/null +++ b/examples/runware-demo/scripts/watch.mjs @@ -0,0 +1,12 @@ +import * as esbuild from 'esbuild'; +import config from '../esbuild/config.mjs'; + +const context = await esbuild.context(config({ isDevelopment: true })); +await context.watch(); + +const { port } = await context.serve({ + servedir: '.', + port: 5180 +}); + +console.log(`\nšŸš€ Runware Demo running at: http://localhost:${port}\n`); diff --git a/examples/runware-demo/src/index.ts b/examples/runware-demo/src/index.ts new file mode 100644 index 00000000..21e597b2 --- /dev/null +++ b/examples/runware-demo/src/index.ts @@ -0,0 +1,205 @@ +import CreativeEditorSDK from '@cesdk/cesdk-js'; +import AiApps from '@imgly/plugin-ai-apps-web'; +import RunwareImage from '@imgly/plugin-ai-image-generation-web/runware'; +import RunwareVideo from '@imgly/plugin-ai-video-generation-web/runware'; +import Anthropic from '@imgly/plugin-ai-text-generation-web/anthropic'; +import { Middleware } from '@imgly/plugin-ai-generation-web'; + +function initialize( + selector: string, + options?: { + archiveUrl?: string; + license?: string; + } +) { + document.addEventListener('DOMContentLoaded', function () { + const domElement = document.querySelector(selector); + if (domElement != null) { + CreativeEditorSDK.create(domElement, { + license: options?.license ?? process.env.CESDK_LICENSE ?? '', + userId: 'plugins-vercel', + callbacks: { + onUpload: 'local', + onExport: 'download', + onLoadArchive: 'uploadArchive' + }, + featureFlags: { + archiveSceneEnabled: true, + dangerouslyDisableVideoSupportCheck: false + }, + ui: { + elements: { + navigation: { + action: { + load: true, + export: true + } + } + } + } + }).then(async (instance) => { + // @ts-ignore + window.cesdk = instance; + + await Promise.all([ + instance.addDefaultAssetSources(), + instance.addDemoAssetSources({ sceneMode: 'Design' }) + ]); + + instance.ui.setDockOrder([ + 'ly.img.ai.apps.dock', + ...instance.ui.getDockOrder().filter(({ key }) => { + return key !== 'ly.img.video.template' && key !== 'ly.img.template'; + }), + 'ly.img.spacer', + 'byok.dock' + ]); + + instance.ui.setCanvasMenuOrder([ + { + id: 'ly.img.ai.text.canvasMenu' + }, + { + id: 'ly.img.ai.image.canvasMenu' + }, + { + id: 'ly.img.separator' + }, + ...instance.ui.getCanvasMenuOrder() + ]); + + instance.feature.enable('ly.img.preview', false); + instance.feature.enable('ly.img.placeholder', false); + + await instance.engine.scene.loadFromArchiveURL( + options?.archiveUrl ?? + 'https://img.ly/showcases/cesdk/cases/ai-editor/ai_editor_design.archive' + ); + const [page] = instance.engine.scene.getPages(); + instance.engine.scene.enableZoomAutoFit(page, 'Both'); + + instance.i18n.setTranslations({ + en: {} + }); + + const errorMiddleware: Middleware = async ( + input, + options, + next + ) => { + return next(input, options).catch((error) => { + console.error('Error:', error); + if (error.name === 'AbortError') { + // Ignore abort errors + return; + } + instance.ui.showDialog({ + type: 'warning', + size: 'large', + content: + 'Due to high demand, we are currently unable to process your request. Please try again shortly - we appreciate your patience!' + }); + // Throw abort error to stop the generation without further + // error notification. + throw new DOMException( + 'Operation aborted: Rate limit exceeded', + 'AbortError' + ); + }); + }; + + instance.engine.scene.setDesignUnit('Pixel'); + instance.addPlugin( + AiApps({ + debug: true, + dryRun: false, + providers: { + text2text: Anthropic.AnthropicProvider({ + middleware: [errorMiddleware], + proxyUrl: process.env.ANTHROPIC_PROXY_URL + }), + text2image: [ + RunwareImage.NanoBanana2Pro.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Pro.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Dev.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.GptImage1.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Seedream45.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Flex.Text2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }) + ], + image2image: [ + RunwareImage.NanoBanana2Pro.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Pro.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Dev.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.GptImage1.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Seedream45.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareImage.Flux2Flex.Image2Image({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }) + ], + text2video: [ + RunwareVideo.Sora2.Text2Video({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareVideo.Veo31.Text2Video({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }) + ], + image2video: [ + RunwareVideo.Sora2.Image2Video({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareVideo.Sora2Pro.Image2Video({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }), + RunwareVideo.Veo31.Image2Video({ + middlewares: [errorMiddleware], + proxyUrl: process.env.RUNWARE_PROXY_URL + }) + ] + } + }) + ); + }); + } + }); +} + +export default initialize; diff --git a/examples/runware-demo/tsconfig.json b/examples/runware-demo/tsconfig.json new file mode 100644 index 00000000..ea17f997 --- /dev/null +++ b/examples/runware-demo/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "strict": true, + "target": "es2017", + "module": "es2020", + "lib": ["es2018", "dom"], + "moduleResolution": "bundler", + "isolatedModules": true, + "esModuleInterop": true, + "declaration": true, + "declarationDir": "dist/", + "skipLibCheck": true, + "resolveJsonModule": true + }, + "include": ["src/**/*", "esbuild/global.d.ts"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78fd930d..5031609c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -296,6 +296,55 @@ importers: specifier: ^5.3.3 version: 5.9.2 + examples/runware-demo: + dependencies: + '@cesdk/cesdk-js': + specifier: 1.66.1 + version: 1.66.1(react@18.3.1) + '@cesdk/engine': + specifier: 1.66.1 + version: 1.66.1(react@18.3.1) + '@imgly/plugin-ai-apps-web': + specifier: ^0.2.15 + version: 0.2.15(@cesdk/cesdk-js@1.66.1(react@18.3.1)) + '@imgly/plugin-ai-generation-web': + specifier: ^0.2.15 + version: 0.2.15(@cesdk/cesdk-js@1.66.1(react@18.3.1)) + '@imgly/plugin-ai-image-generation-web': + specifier: ^0.2.15 + version: 0.2.15(@cesdk/cesdk-js@1.66.1(react@18.3.1)) + '@imgly/plugin-ai-text-generation-web': + specifier: ^0.2.15 + version: 0.2.15(@cesdk/cesdk-js@1.66.1(react@18.3.1)) + '@imgly/plugin-ai-video-generation-web': + specifier: ^0.2.15 + version: 0.2.15(@cesdk/cesdk-js@1.66.1(react@18.3.1)) + dotenv: + specifier: ^16.5.0 + version: 16.6.1 + devDependencies: + '@types/ndarray': + specifier: ^1.0.14 + version: 1.0.14 + chalk: + specifier: ^5.3.0 + version: 5.6.0 + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + esbuild: + specifier: ^0.19.11 + version: 0.19.12 + eslint: + specifier: ^8.51.0 + version: 8.57.1 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + typescript: + specifier: ^5.3.3 + version: 5.9.2 + examples/web: dependencies: '@cesdk/cesdk-js':