@imgly/plugin-ai-generation-web 0.1.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +798 -0
- package/dist/__tests__/middleware-disposer.test.d.ts +1 -0
- package/dist/__tests__/middleware-rateLimiting.test.d.ts +1 -0
- package/dist/__tests__/middleware.test.d.ts +1 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/common/renderImageUrlProperty.d.ts +14 -0
- package/dist/generation/generate.d.ts +16 -0
- package/dist/generation/getAssetResultForGenerated.d.ts +4 -0
- package/dist/generation/getAssetResultForPlaceholder.d.ts +4 -0
- package/dist/generation/getDryRunOutput.d.ts +3 -0
- package/dist/generation/handleGenerationError.d.ts +9 -0
- package/dist/generation/initProvider.d.ts +20 -0
- package/dist/generation/middleware/alwaysOnTopMiddleware.d.ts +14 -0
- package/dist/generation/middleware/editModeMiddleware.d.ts +30 -0
- package/dist/generation/middleware/highlightBlocksMiddleware.d.ts +24 -0
- package/dist/generation/middleware/lockMiddleware.d.ts +19 -0
- package/dist/generation/middleware/loggingMiddleware.d.ts +4 -0
- package/dist/generation/middleware/middleware.d.ts +35 -0
- package/dist/generation/middleware/pendingMiddleware.d.ts +14 -0
- package/dist/generation/middleware/rateLimitMiddleware.d.ts +40 -0
- package/dist/generation/openapi/dereferenceDocument.d.ts +14 -0
- package/dist/generation/openapi/getProperties.d.ts +5 -0
- package/dist/generation/openapi/isOpenAPISchema.d.ts +9 -0
- package/dist/generation/openapi/renderProperty.d.ts +6 -0
- package/dist/generation/openapi/types.d.ts +41 -0
- package/dist/generation/previewUri.d.ts +2 -0
- package/dist/generation/provider.d.ts +373 -0
- package/dist/generation/quickAction/consumeGeneratedResult.d.ts +23 -0
- package/dist/generation/quickAction/getQuickActionMenu.d.ts +10 -0
- package/dist/generation/quickAction/registerQuickActionMenuComponent.d.ts +12 -0
- package/dist/generation/quickAction/types.d.ts +25 -0
- package/dist/generation/quickAction/utils.d.ts +13 -0
- package/dist/generation/registerPanelInputCustom.d.ts +6 -0
- package/dist/generation/registerPanelInputSchema.d.ts +9 -0
- package/dist/generation/renderGenerationComponents.d.ts +15 -0
- package/dist/generation/types.d.ts +56 -0
- package/dist/icons.d.ts +3 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.mjs +81 -0
- package/dist/index.mjs.map +7 -0
- package/dist/registerDockComponent.d.ts +10 -0
- package/dist/utils.d.ts +66 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
# IMG.LY AI Generation Utilities
|
|
2
|
+
|
|
3
|
+
A powerful toolkit for implementing AI generation providers in CreativeEditor SDK.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides the foundation for creating AI generation plugins for CreativeEditor SDK. It offers a standardized interface for implementing AI generation providers that can create images, videos, audio, or text assets. The package includes utilities for handling:
|
|
8
|
+
|
|
9
|
+
- Provider registration and initialization
|
|
10
|
+
- User interface generation
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
### Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @imgly/plugin-ai-generation-web
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Creating a Custom Provider
|
|
21
|
+
|
|
22
|
+
The core of this package is the `Provider` interface which defines the contract for AI generation providers. Before we go into details, here's how to implement a basic provider:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import {
|
|
26
|
+
Provider,
|
|
27
|
+
ImageOutput,
|
|
28
|
+
initProvider,
|
|
29
|
+
loggingMiddleware
|
|
30
|
+
} from '@imgly/plugin-ai-generation-web';
|
|
31
|
+
|
|
32
|
+
// Create your image generation provider
|
|
33
|
+
const myImageProvider: Provider<'image', MyInputType, ImageOutput> = {
|
|
34
|
+
// Unique identifier for this provider
|
|
35
|
+
id: 'my-image-provider',
|
|
36
|
+
|
|
37
|
+
// Define output asset type, other options are 'video', 'audio', 'text'
|
|
38
|
+
kind: 'image',
|
|
39
|
+
|
|
40
|
+
// Initialize the provider
|
|
41
|
+
initialize: async ({ engine, cesdk }) => {
|
|
42
|
+
// Setup APIs, register further components, etc.
|
|
43
|
+
myAIApi.configure({ apiKey: 'YOUR_API_KEY' });
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// Define input panel and UI components
|
|
47
|
+
input: {
|
|
48
|
+
// Define how the input panel is rendered
|
|
49
|
+
panel: {
|
|
50
|
+
// Option 1: Schema-based UI (using OpenAPI)
|
|
51
|
+
type: 'schema',
|
|
52
|
+
document: myApiSchema,
|
|
53
|
+
inputReference: '#/components/schemas/GenerationInput',
|
|
54
|
+
getBlockInput: async (input) => ({
|
|
55
|
+
image: { width: 1024, height: 1024 }
|
|
56
|
+
})
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// Optional: Quick actions for the quick action (sub)menu in the canvas menu
|
|
60
|
+
quickActions: {
|
|
61
|
+
actions: [
|
|
62
|
+
{
|
|
63
|
+
id: 'enhance-image',
|
|
64
|
+
version: '1',
|
|
65
|
+
enable: true,
|
|
66
|
+
render: (context, quickActionContext) => {
|
|
67
|
+
// Render quick action UI
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Define output generation behavior
|
|
75
|
+
output: {
|
|
76
|
+
// Allow cancellation
|
|
77
|
+
abortable: true,
|
|
78
|
+
|
|
79
|
+
// Store generated assets, options are:
|
|
80
|
+
// - false: No history
|
|
81
|
+
// - '@imgly/local': In-memory storage (lost on refresh)
|
|
82
|
+
// - '@imgly/indexedDB': Browser IndexedDB storage
|
|
83
|
+
// - any other string: Handled as a custom asset source ID
|
|
84
|
+
history: '@imgly/indexedDB',
|
|
85
|
+
|
|
86
|
+
// Add middleware for pre/post-processing of the generation
|
|
87
|
+
middleware: [loggingMiddleware()],
|
|
88
|
+
|
|
89
|
+
// Configure success/error notifications
|
|
90
|
+
notification: {
|
|
91
|
+
success: {
|
|
92
|
+
show: true,
|
|
93
|
+
message: 'Generation successful!'
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// Core generation function
|
|
98
|
+
generate: async (input, { abortSignal, engine }) => {
|
|
99
|
+
// Call your AI API and return result
|
|
100
|
+
const response = await myAIApi.generateImage(input);
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
kind: 'image',
|
|
104
|
+
url: response.imageUrl
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Provider Interface
|
|
112
|
+
|
|
113
|
+
The Provider interface is generic and type-safe, supporting four output kinds:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// K: Output kind ('image', 'video', 'audio', 'text')
|
|
117
|
+
// I: Input type specific to your provider, i.e. what does the generate function need
|
|
118
|
+
// O: Output type (ImageOutput, VideoOutput, AudioOutput, TextOutput)
|
|
119
|
+
// C: Chunk type for streaming (optional, defaults to O)
|
|
120
|
+
interface Provider<K extends OutputKind, I, O extends Output, C = O> { ... }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Key Provider Options
|
|
124
|
+
|
|
125
|
+
- **id**: Unique identifier for your provider
|
|
126
|
+
- **kind**: Type of asset generated ('image', 'video', 'audio', 'text')
|
|
127
|
+
- **name**: Optional human-readable name
|
|
128
|
+
- **initialize**: Setup function called when the provider is loaded
|
|
129
|
+
- **input**: Configuration for input UI and parameters
|
|
130
|
+
- **output**: Configuration for generation and result handling
|
|
131
|
+
|
|
132
|
+
#### Provider Output Options
|
|
133
|
+
|
|
134
|
+
The `output` property has several important options:
|
|
135
|
+
|
|
136
|
+
- **generate**: Main function that performs the actual generation
|
|
137
|
+
- **history**: Asset storage strategy ('false', '@imgly/local', '@imgly/indexedDB', or custom ID)
|
|
138
|
+
- **abortable**: Whether generation can be cancelled by the user
|
|
139
|
+
- **middleware**: Array of middleware functions for pre/post-processing
|
|
140
|
+
- **notification**: Success and error notification configuration
|
|
141
|
+
- **generationHintText**: Text to display below the generation button
|
|
142
|
+
|
|
143
|
+
##### Notification Configuration
|
|
144
|
+
|
|
145
|
+
The notification system allows fine-grained control over success and error messages:
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
notification: {
|
|
149
|
+
success: {
|
|
150
|
+
// Control whether to show notifications (can be dynamic)
|
|
151
|
+
show: true, // or (context) => shouldShow(context)
|
|
152
|
+
|
|
153
|
+
// Message text or i18n key (can be dynamic)
|
|
154
|
+
message: 'Generation successful!', // or (context) => getMessage(context)
|
|
155
|
+
|
|
156
|
+
// Optional action button
|
|
157
|
+
action: {
|
|
158
|
+
label: 'View', // or (context) => getLabel(context)
|
|
159
|
+
onClick: (context) => { /* handle click */ }
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// How long to show the notification
|
|
163
|
+
duration: 'short' // or 'medium', 'long', 'infinite'
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
error: {
|
|
167
|
+
// Similar options for error notifications
|
|
168
|
+
show: true,
|
|
169
|
+
message: 'Generation failed', // or (context) => getErrorMessage(context)
|
|
170
|
+
// ...
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
##### Streaming Generation
|
|
176
|
+
|
|
177
|
+
The `generate` function can return a simple output object or an AsyncGenerator for streaming results:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
// Simple response
|
|
181
|
+
generate: async (input, options) => {
|
|
182
|
+
const result = await api.generateImage(input);
|
|
183
|
+
return { kind: 'image', url: result.url };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Streaming response (right now only supported for text)
|
|
187
|
+
generate: async function* (input, options) {
|
|
188
|
+
const stream = api.streamGenerationResult(input);
|
|
189
|
+
|
|
190
|
+
let inferredText: string = '';
|
|
191
|
+
// Yield interim results
|
|
192
|
+
for await (const chunk of stream) {
|
|
193
|
+
inferredText += chunk;
|
|
194
|
+
yield { kind: 'text', text: inferredText };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Return final result
|
|
198
|
+
return { kind: 'text', url: inferredText };
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
##### Generation Hint Text
|
|
203
|
+
|
|
204
|
+
The `generationHintText` property allows providers to display helpful information below the generation button:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
generationHintText: "Generation may take up to a minute. You can close this panel and will be notified when ready."
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Input Panel Types
|
|
211
|
+
|
|
212
|
+
The package supports two approaches for creating input panels:
|
|
213
|
+
|
|
214
|
+
### 1. Schema-based Input Panels
|
|
215
|
+
|
|
216
|
+
The `schema` type uses OpenAPI specification to declaratively define your input form.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
input: {
|
|
220
|
+
panel: {
|
|
221
|
+
type: 'schema',
|
|
222
|
+
// Complete OpenAPI v3 document describing your inputs
|
|
223
|
+
document: myOpenAPISchema,
|
|
224
|
+
// JSON pointer to your input schema within the document
|
|
225
|
+
inputReference: '#/components/schemas/GenerationInput',
|
|
226
|
+
// Optional property to control display order
|
|
227
|
+
orderExtensionKeyword: 'x-order-properties',
|
|
228
|
+
// Function that converts input to block parameters
|
|
229
|
+
getBlockInput: async (input) => ({
|
|
230
|
+
image: { width: 1024, height: 1024 }
|
|
231
|
+
}),
|
|
232
|
+
// Optional custom renderers for specific properties found in the schema
|
|
233
|
+
renderCustomProperty: {
|
|
234
|
+
// This is a custom renderer for a fictional `imageUrl` property
|
|
235
|
+
imageUrl: (context, property) => {
|
|
236
|
+
const valueState = context.state('imageUrl', '');
|
|
237
|
+
context.builder.TextInput('imageUrl', {
|
|
238
|
+
label: 'Image URL',
|
|
239
|
+
...valueState
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Return a function that returns the value for this property
|
|
243
|
+
return () => { id: property.id, type: 'string', value: valueState.value };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
#### OpenAPI Schema Example
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"openapi": "3.0.0",
|
|
255
|
+
"components": {
|
|
256
|
+
"schemas": {
|
|
257
|
+
"GenerationInput": {
|
|
258
|
+
"type": "object",
|
|
259
|
+
"required": ["prompt"],
|
|
260
|
+
"properties": {
|
|
261
|
+
"prompt": {
|
|
262
|
+
"type": "string",
|
|
263
|
+
"title": "Prompt",
|
|
264
|
+
"description": "Describe what you want to generate",
|
|
265
|
+
"x-imgly-builder": {
|
|
266
|
+
"component": "TextArea"
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"width": {
|
|
270
|
+
"type": "integer",
|
|
271
|
+
"title": "Width",
|
|
272
|
+
"default": 1024,
|
|
273
|
+
"enum": [512, 1024, 2048],
|
|
274
|
+
"x-imgly-builder": {
|
|
275
|
+
"component": "Select"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
"x-order-properties": ["prompt", "width"]
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### Benefits of Schema-based Input
|
|
287
|
+
|
|
288
|
+
- Built-in validation based on schema constraints
|
|
289
|
+
- AI provider like fal.ai provide schemas for their models
|
|
290
|
+
- Automatic UI component generation based on property types
|
|
291
|
+
- Extensions like `x-imgly-builder` to specify component types
|
|
292
|
+
- Property ordering via `orderExtensionKeyword`
|
|
293
|
+
- Customizable property rendering with `renderCustomProperty`
|
|
294
|
+
|
|
295
|
+
### 2. Custom Input Panels
|
|
296
|
+
|
|
297
|
+
The `custom` type gives you complete control over UI components.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
input: {
|
|
301
|
+
panel: {
|
|
302
|
+
type: 'custom',
|
|
303
|
+
render: (context, options) => {
|
|
304
|
+
// Use the builder pattern to create UI components
|
|
305
|
+
const promptState = context.state('prompt', '');
|
|
306
|
+
context.builder.TextArea('prompt', {
|
|
307
|
+
label: 'Prompt',
|
|
308
|
+
...promptState
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// Set up width selection
|
|
312
|
+
const widthState = context.state('width', 1024);
|
|
313
|
+
context.builder.Select('width', {
|
|
314
|
+
label: 'Width',
|
|
315
|
+
options: [
|
|
316
|
+
{ value: 512, label: '512px' },
|
|
317
|
+
{ value: 1024, label: '1024px' },
|
|
318
|
+
{ value: 2048, label: '2048px' }
|
|
319
|
+
],
|
|
320
|
+
...widthState
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Return functions to get input values and block parameters
|
|
324
|
+
return {
|
|
325
|
+
// The input for the generate function
|
|
326
|
+
getInput: () => ({
|
|
327
|
+
prompt: promptState.value,
|
|
328
|
+
width: widthState.value
|
|
329
|
+
}),
|
|
330
|
+
// The input for the block creation
|
|
331
|
+
getBlockInput: () => ({
|
|
332
|
+
image: {
|
|
333
|
+
width: widthState.value,
|
|
334
|
+
height: widthState.value,
|
|
335
|
+
label: `AI Image: ${promptState.value.substring(0, 20)}...`
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
#### Benefits of Custom Input Panels
|
|
345
|
+
|
|
346
|
+
- Complete control over UI components and layout
|
|
347
|
+
- Complex logic between fields (dependencies, conditionals)
|
|
348
|
+
- Dynamic UI that changes based on user interactions
|
|
349
|
+
|
|
350
|
+
#### Panel User Flow Options
|
|
351
|
+
|
|
352
|
+
Both panel types accept additional configuration:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
panel: {
|
|
356
|
+
type: 'schema', // or 'custom'
|
|
357
|
+
// ...panel type specific options
|
|
358
|
+
|
|
359
|
+
// Control the generation flow
|
|
360
|
+
userFlow: 'placeholder', // or 'generation-only' (default)
|
|
361
|
+
|
|
362
|
+
// Include/exclude history library from panel
|
|
363
|
+
includeHistoryLibrary: true // (default)
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
- **userFlow**:
|
|
368
|
+
|
|
369
|
+
- `placeholder`: Creates a block as a placeholder with loading state when generation starts
|
|
370
|
+
- `generation-only`: Only triggers generation without creating a placeholder
|
|
371
|
+
|
|
372
|
+
- **includeHistoryLibrary**: Controls whether the history library is shown in the panel
|
|
373
|
+
|
|
374
|
+
## The `getBlockInput` Function
|
|
375
|
+
|
|
376
|
+
The `getBlockInput` function is crucial for both panel types. It converts your input into the parameters needed to create a block in CreativeEditor SDK.
|
|
377
|
+
|
|
378
|
+
### What It Does
|
|
379
|
+
|
|
380
|
+
- Defines dimensions, duration, and appearance of asset blocks
|
|
381
|
+
- Creates placeholders before generation completes
|
|
382
|
+
- Maps your AI provider's inputs to standardized block parameters
|
|
383
|
+
|
|
384
|
+
### Required Return Values by Output Kind
|
|
385
|
+
|
|
386
|
+
Each output kind requires specific parameters:
|
|
387
|
+
|
|
388
|
+
#### For Images
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
getBlockInput: async (input) => ({
|
|
392
|
+
image: {
|
|
393
|
+
width: 1024, // Required - Width in pixels
|
|
394
|
+
height: 1024, // Required - Height in pixels
|
|
395
|
+
label: 'My Image' // Optional - Display name
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### For Videos
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
getBlockInput: async (input) => ({
|
|
404
|
+
video: {
|
|
405
|
+
width: 1280, // Required - Width in pixels
|
|
406
|
+
height: 720, // Required - Height in pixels
|
|
407
|
+
duration: 10, // Required - Duration in seconds
|
|
408
|
+
label: 'My Video' // Optional - Display name
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### For Audio
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
getBlockInput: async (input) => ({
|
|
417
|
+
audio: {
|
|
418
|
+
duration: 30, // Optional - Duration in seconds
|
|
419
|
+
thumbnailUrl: 'path/to/img.jpg', // Optional - URL for thumbnail
|
|
420
|
+
label: 'My Audio' // Optional - Display name
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
#### For Text
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
getBlockInput: async (input) => ({
|
|
429
|
+
text: {
|
|
430
|
+
length: 250, // Required - Approximate character length
|
|
431
|
+
label: 'My Text' // Optional - Display name
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Quick Actions
|
|
437
|
+
|
|
438
|
+
Quick Actions provide context-aware AI generation capabilities directly in CreativeEditor SDK's canvas menu. Unlike panels (which appear in the side panel), quick actions appear when users select elements on the canvas.
|
|
439
|
+
|
|
440
|
+
Instead of adding a single button for every different generation provider, a single quick action menu is registered and used by all providers. This allows users to select the desired action without cluttering the UI.
|
|
441
|
+
|
|
442
|
+
### Purpose of Quick Actions
|
|
443
|
+
|
|
444
|
+
- **Context-Aware**: Operate on selected blocks in the canvas
|
|
445
|
+
- **Streamlined Workflow**: Allow users to apply AI transformations without switching to a panel
|
|
446
|
+
- **Immediate Feedback**: Provide quick access to common AI operations
|
|
447
|
+
- **In-Canvas Experience**: Keep users in their creative workflow
|
|
448
|
+
|
|
449
|
+
### Quick Action Structure
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
quickActions: {
|
|
453
|
+
actions: [
|
|
454
|
+
{
|
|
455
|
+
// Unique identifier
|
|
456
|
+
id: 'enhance-image',
|
|
457
|
+
|
|
458
|
+
// Version for compatibility
|
|
459
|
+
version: '1',
|
|
460
|
+
|
|
461
|
+
// Enable/disable based on condition
|
|
462
|
+
// E.g., you should check for the correct block type here.
|
|
463
|
+
enable: true, // or a function: (context) => boolean
|
|
464
|
+
|
|
465
|
+
// Optional scope requirements
|
|
466
|
+
scopes: ['text.edit'],
|
|
467
|
+
|
|
468
|
+
// Optional confirmation after generation
|
|
469
|
+
confirmation: true,
|
|
470
|
+
|
|
471
|
+
// Prevent selection changes during confirmation
|
|
472
|
+
lockDuringConfirmation: true,
|
|
473
|
+
|
|
474
|
+
// Basic menu item rendering
|
|
475
|
+
render: (context, quickActionContext) => {
|
|
476
|
+
// Add button to quick action menu
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
// Optional expanded mode rendering
|
|
480
|
+
renderExpanded: (context, quickActionContext) => {
|
|
481
|
+
// Render more complex interface
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Simple Quick Action Example
|
|
489
|
+
|
|
490
|
+
```typescript
|
|
491
|
+
{
|
|
492
|
+
id: 'enhance-image',
|
|
493
|
+
version: '1',
|
|
494
|
+
enable: true,
|
|
495
|
+
render: ({ builder }, { generate, closeMenu }) => {
|
|
496
|
+
builder.Button('enhance', {
|
|
497
|
+
label: 'Enhance Image',
|
|
498
|
+
icon: '@imgly/MagicWand',
|
|
499
|
+
onClick: async () => {
|
|
500
|
+
// Generate with fixed parameters
|
|
501
|
+
await generate({
|
|
502
|
+
prompt: 'Enhance this image and improve quality'
|
|
503
|
+
});
|
|
504
|
+
closeMenu();
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Quick Action with Expanded Menu
|
|
512
|
+
|
|
513
|
+
This example shows how to create a quick action that expands into a more complex UI with input fields. During expansion, the content of the complete menu is replaced with the `renderExpanded` function.
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
{
|
|
517
|
+
id: 'change-image',
|
|
518
|
+
version: '1',
|
|
519
|
+
enable: true,
|
|
520
|
+
render: ({ builder }, { toggleExpand }) => {
|
|
521
|
+
// Render basic button that expands to complex UI
|
|
522
|
+
builder.Button('change', {
|
|
523
|
+
label: 'Change Image',
|
|
524
|
+
icon: '@imgly/Edit',
|
|
525
|
+
onClick: toggleExpand // Switch to expanded view
|
|
526
|
+
});
|
|
527
|
+
},
|
|
528
|
+
renderExpanded: ({ builder, state }, { generate, toggleExpand }) => {
|
|
529
|
+
// Create more complex UI with input fields
|
|
530
|
+
const promptState = state('prompt', '');
|
|
531
|
+
|
|
532
|
+
builder.TextArea('prompt', {
|
|
533
|
+
label: 'Prompt',
|
|
534
|
+
placeholder: 'Describe the changes you want...',
|
|
535
|
+
...promptState
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
builder.Separator('separator');
|
|
539
|
+
|
|
540
|
+
// Add footer with cancel/apply buttons
|
|
541
|
+
builder.ButtonRow('footer', {
|
|
542
|
+
children: () => {
|
|
543
|
+
builder.Button('cancel', {
|
|
544
|
+
label: 'Back',
|
|
545
|
+
onClick: toggleExpand // Return to basic view
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
builder.Button('apply', {
|
|
549
|
+
label: 'Generate',
|
|
550
|
+
color: 'accent',
|
|
551
|
+
onClick: async () => {
|
|
552
|
+
await generate({
|
|
553
|
+
prompt: promptState.value
|
|
554
|
+
});
|
|
555
|
+
toggleExpand(); // Close expanded view after generation
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Quick Action Context
|
|
565
|
+
|
|
566
|
+
The `quickActionContext` parameter gives you access to:
|
|
567
|
+
|
|
568
|
+
- **blockIds**: Array of currently selected block IDs
|
|
569
|
+
- **closeMenu**: Function to close the quick action menu
|
|
570
|
+
- **toggleExpand**: Function to toggle between basic and expanded views
|
|
571
|
+
- **generate**: Function to trigger generation with input parameters
|
|
572
|
+
- **handleGenerationError**: Function to handle and display generation errors
|
|
573
|
+
|
|
574
|
+
### Key Use Cases
|
|
575
|
+
|
|
576
|
+
1. **One-Click Actions**: Simple transformations with predefined parameters
|
|
577
|
+
2. **Context-Aware Generation**: Using properties of selected blocks as inputs
|
|
578
|
+
3. **Multi-Step Workflows**: Expanded UIs for complex generation tasks
|
|
579
|
+
4. **Block Manipulation**: Applying AI-generated content to existing blocks
|
|
580
|
+
5. **Visual Selection**: Displaying visual options that apply different styles
|
|
581
|
+
|
|
582
|
+
### Quick Action vs Panel: When to Use Each
|
|
583
|
+
|
|
584
|
+
**Use Quick Actions when:**
|
|
585
|
+
|
|
586
|
+
- The operation is context-dependent on selected blocks
|
|
587
|
+
- The operation is commonly used and benefits from quick access
|
|
588
|
+
- The input requirements are simple or can be preset
|
|
589
|
+
- The action works directly with content already on the canvas
|
|
590
|
+
|
|
591
|
+
**Use Panels when:**
|
|
592
|
+
|
|
593
|
+
- The operation requires complex input forms
|
|
594
|
+
- The generation is independent of canvas selections
|
|
595
|
+
- Users need to browse a history of generated assets
|
|
596
|
+
- The operation creates entirely new content rather than modifying existing content
|
|
597
|
+
|
|
598
|
+
## Using Your Provider
|
|
599
|
+
|
|
600
|
+
Once you've created your provider, you need to initialize it with CreativeEditor SDK and integrate it into the UI. This section covers how to use your provider effectively.
|
|
601
|
+
|
|
602
|
+
### Initializing Your Provider
|
|
603
|
+
|
|
604
|
+
The most basic way is to use the `initProvider` function to register your provider with CreativeEditor SDK:
|
|
605
|
+
|
|
606
|
+
```typescript
|
|
607
|
+
import { initProvider } from '@imgly/plugin-ai-generation-web';
|
|
608
|
+
|
|
609
|
+
// Create your provider
|
|
610
|
+
const myProvider = createMyProvider({
|
|
611
|
+
proxyUrl: 'https://your-api-proxy.example.com'
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// Initialize the provider with CreativeEditor SDK
|
|
615
|
+
function setupMyProvider(cesdk) {
|
|
616
|
+
const result = initProvider(
|
|
617
|
+
myProvider,
|
|
618
|
+
{
|
|
619
|
+
engine: cesdk.engine,
|
|
620
|
+
cesdk
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
debug: false, // Enable/disable debug logging
|
|
624
|
+
dryRun: false // Enable/disable dry run mode (no actual API calls)
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
return result;
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
Now you can render the provider's UI components in your application. The `initProvider` function takes care of registering the provider with CreativeEditor SDK and setting up the necessary UI components (see the next section for the ids of the panel).
|
|
633
|
+
|
|
634
|
+
Alternatively, the `initProvider` function returns an object with `renderBuilderFunctions` which contains render functions that can be used to create custom UI.
|
|
635
|
+
|
|
636
|
+
### Panel IDs and Registration
|
|
637
|
+
|
|
638
|
+
When a provider is initialized, it automatically registers panels with specific IDs. These panel IDs follow a consistent pattern:
|
|
639
|
+
|
|
640
|
+
```
|
|
641
|
+
ly.img.ai/{provider-id}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
For example:
|
|
645
|
+
|
|
646
|
+
- A provider with ID `my-image-provider` registers a panel with ID `ly.img.ai/my-image-provider`
|
|
647
|
+
- A provider with ID `fal-ai/recraft-v3` registers a panel with ID `ly.img.ai/fal-ai/recraft-v3`
|
|
648
|
+
|
|
649
|
+
You can programmatically get a panel ID using the `getPanelId` function:
|
|
650
|
+
|
|
651
|
+
```typescript
|
|
652
|
+
import { getPanelId } from '@imgly/plugin-ai-generation-web';
|
|
653
|
+
|
|
654
|
+
// Get panel ID for a provider
|
|
655
|
+
const panelId = getPanelId('my-image-provider'); // returns "ly.img.ai/my-image-provider"
|
|
656
|
+
|
|
657
|
+
// Open the panel
|
|
658
|
+
cesdk.ui.openPanel(panelId);
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
For quick actions in the canvas menu, the following format is used:
|
|
662
|
+
|
|
663
|
+
```
|
|
664
|
+
ly.img.ai.{kind}.canvasMenu
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
For example:
|
|
668
|
+
|
|
669
|
+
- Image quick actions: `ly.img.ai.image.canvasMenu`
|
|
670
|
+
- Video quick actions: `ly.img.ai.video.canvasMenu`
|
|
671
|
+
- Audio quick actions: `ly.img.ai.audio.canvasMenu`
|
|
672
|
+
- Text quick actions: `ly.img.ai.text.canvasMenu`
|
|
673
|
+
|
|
674
|
+
### Using with Existing AI Generation Plugins
|
|
675
|
+
|
|
676
|
+
IMG.LY offers several pre-built AI generation packages that contain a few popular providers as well as combining different models for the same `kind`, e.g. to combine text2image and image2image models in a single panel.
|
|
677
|
+
|
|
678
|
+
- `@imgly/plugin-ai-image-generation-web`: For image generation
|
|
679
|
+
- `@imgly/plugin-ai-video-generation-web`: For video generation
|
|
680
|
+
- `@imgly/plugin-ai-audio-generation-web`: For audio generation
|
|
681
|
+
- `@imgly/plugin-ai-text-generation-web`: For text generation
|
|
682
|
+
|
|
683
|
+
You can use these packages with different AI provider implementations:
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
import CreativeEditorSDK from '@cesdk/cesdk-js';
|
|
687
|
+
|
|
688
|
+
// Import plugin packages
|
|
689
|
+
import ImageGeneration from '@imgly/plugin-ai-image-generation-web';
|
|
690
|
+
import FalAiImage from '@imgly/plugin-ai-image-generation-web/fal-ai';
|
|
691
|
+
import VideoGeneration from '@imgly/plugin-ai-video-generation-web';
|
|
692
|
+
import FalAiVideo from '@imgly/plugin-ai-video-generation-web/fal-ai';
|
|
693
|
+
import AudioGeneration from '@imgly/plugin-ai-audio-generation-web';
|
|
694
|
+
import Elevenlabs from '@imgly/plugin-ai-audio-generation-web/elevenlabs';
|
|
695
|
+
import TextGeneration from '@imgly/plugin-ai-text-generation-web';
|
|
696
|
+
import Anthropic from '@imgly/plugin-ai-text-generation-web/anthropic';
|
|
697
|
+
|
|
698
|
+
// Initialize CreativeEditor SDK
|
|
699
|
+
CreativeEditorSDK.create(domElement, {
|
|
700
|
+
license: 'your-license-key'
|
|
701
|
+
// Other configuration options...
|
|
702
|
+
}).then(async (cesdk) => {
|
|
703
|
+
// Add default asset sources
|
|
704
|
+
await cesdk.addDefaultAssetSources();
|
|
705
|
+
|
|
706
|
+
// Text generation with Anthropic
|
|
707
|
+
cesdk.addPlugin(
|
|
708
|
+
TextGeneration({
|
|
709
|
+
provider: Anthropic.AnthropicProvider({
|
|
710
|
+
proxyUrl: 'https://your-anthropic-proxy.example.com'
|
|
711
|
+
})
|
|
712
|
+
})
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
// Image generation with Fal.ai models
|
|
716
|
+
cesdk.addPlugin(
|
|
717
|
+
ImageGeneration({
|
|
718
|
+
text2image: FalAiImage.RecraftV3({
|
|
719
|
+
proxyUrl: 'https://your-falai-proxy.example.com'
|
|
720
|
+
}),
|
|
721
|
+
image2image: FalAiImage.GeminiFlashEdit({
|
|
722
|
+
proxyUrl: 'https://your-falai-proxy.example.com'
|
|
723
|
+
})
|
|
724
|
+
})
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
// Video generation
|
|
728
|
+
cesdk.addPlugin(
|
|
729
|
+
VideoGeneration({
|
|
730
|
+
text2video: FalAiVideo.MinimaxVideo01Live({
|
|
731
|
+
proxyUrl: 'https://your-falai-proxy.example.com'
|
|
732
|
+
}),
|
|
733
|
+
image2video: FalAiVideo.MinimaxVideo01LiveImageToVideo({
|
|
734
|
+
proxyUrl: 'https://your-falai-proxy.example.com'
|
|
735
|
+
})
|
|
736
|
+
})
|
|
737
|
+
);
|
|
738
|
+
|
|
739
|
+
// Audio generation
|
|
740
|
+
cesdk.addPlugin(
|
|
741
|
+
AudioGeneration({
|
|
742
|
+
text2speech: Elevenlabs.ElevenMultilingualV2({
|
|
743
|
+
proxyUrl: 'https://your-elevenlabs-proxy.example.com'
|
|
744
|
+
}),
|
|
745
|
+
text2sound: Elevenlabs.ElevenSoundEffects({
|
|
746
|
+
proxyUrl: 'https://your-elevenlabs-proxy.example.com'
|
|
747
|
+
})
|
|
748
|
+
})
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
// Add the quick action menu to the canvas menu
|
|
752
|
+
cesdk.ui.setCanvasMenuOrder([
|
|
753
|
+
'ly.img.ai.text.canvasMenu',
|
|
754
|
+
'ly.img.ai.image.canvasMenu',
|
|
755
|
+
...cesdk.ui.getCanvasMenuOrder()
|
|
756
|
+
]);
|
|
757
|
+
});
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
## Advanced Features
|
|
761
|
+
|
|
762
|
+
### Middleware
|
|
763
|
+
|
|
764
|
+
The package includes a middleware system to augment the generation flow:
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
// NOTE:: This middleware will not protect against calling the server directly as
|
|
768
|
+
// many times as you want. It is only meant to be used for rate-limiting the UI before it
|
|
769
|
+
// hits the server.
|
|
770
|
+
// Always secure your API endpoints with authentication and authorization or server-side
|
|
771
|
+
// rate-limiting.
|
|
772
|
+
import { rateLimitMiddleware } from '@imgly/plugin-ai-generation-web';
|
|
773
|
+
|
|
774
|
+
// Create a rate limiting middleware
|
|
775
|
+
const rateLimit = rateLimitMiddleware({
|
|
776
|
+
maxRequests: 10,
|
|
777
|
+
timeWindowMs: 60000, // 1 minute
|
|
778
|
+
onRateLimitExceeded: (input, options, info) => {
|
|
779
|
+
console.log(
|
|
780
|
+
`Rate limit exceeded: ${info.currentCount}/${info.maxRequests}`
|
|
781
|
+
);
|
|
782
|
+
return false; // Reject request
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// Apply middleware to your provider
|
|
787
|
+
const provider = {
|
|
788
|
+
// ...provider config
|
|
789
|
+
output: {
|
|
790
|
+
middleware: [rateLimit]
|
|
791
|
+
// ...other output config
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
## TypeScript Support
|
|
797
|
+
|
|
798
|
+
This package is fully typed with TypeScript, providing excellent IntelliSense support during development.
|