@goodandready/dsh-image-gen 0.10.3 → 0.10.5
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 +15 -0
- package/README.ru.md +12 -0
- package/README.zh.md +8 -0
- package/lib/client.js +57 -30
- package/lib/frontend-assets.js +436 -0
- package/lib/index.js +566 -3
- package/lib/providers.js +50 -2
- package/lib/resolve-image.js +243 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
## 🛠️ Complete Tools Reference
|
|
46
46
|
|
|
47
47
|
* **`generate_image`**: Generate images with pluggable providers, seeds, aspect ratios, and style presets.
|
|
48
|
+
* **`edit_image`**: Targeted inpainting and modification with automatic session reference resolution (#142, #144, #145).
|
|
49
|
+
* **`vary_image`**: Controlled variation generation preserving composition (#143, #144).
|
|
48
50
|
* **`remove_background`**: Extract subject with transparent PNG output (FAL BiRefNet / Rembg).
|
|
49
51
|
* **`upscale_image`**: 2x / 4x super-resolution with clarity reconstruction.
|
|
50
52
|
* **`vectorize_image`**: Convert raster graphics to clean scalable SVG vectors with palette quantization.
|
|
@@ -52,9 +54,22 @@
|
|
|
52
54
|
* **`generate_image_pack`**: Simultaneous multi-aspect ratio rendering with graceful partial recovery.
|
|
53
55
|
* **`compare_images`**: Pixel-level visual difference ratio comparison.
|
|
54
56
|
* **`inspect_image_quality`**: Automated visual audit, Laplacian sharpness scoring, and defect detection.
|
|
57
|
+
* **`extract_design_tokens`**: Extract CSS Variables, Tailwind color palettes, and W3C Design Tokens from concept art (#172).
|
|
58
|
+
* **`image_to_css_gradient`**: Generate lightweight pure CSS Mesh / Radial / Linear gradients (< 1KB) from image colors (#174).
|
|
59
|
+
* **`check_image_contrast`**: Evaluate background luminance and WCAG 2.1 AA/AAA contrast for text with scrim suggestions (#175).
|
|
60
|
+
* **`optimize_vector_svg`**: Clean and sanitize SVG, normalize viewBox, and export ready-to-use React TSX components (#176).
|
|
61
|
+
* **`generate_pwa_icon_suite`**: Generate full PWA icon sets, HTML meta tags, and web app manifest.json (#190).
|
|
55
62
|
|
|
56
63
|
---
|
|
57
64
|
|
|
65
|
+
|
|
66
|
+
## 🚀 Updates in v0.10.4: Cordis Lifecycle, Full Settings GUI, and i18n
|
|
67
|
+
- **Cordis Lifecycle (#136)**: wrapped all 7 tool registrations in `ctx.effect` for proper disposal on reload.
|
|
68
|
+
- **Settings GUI Completeness (#137)**: exposed fields for Replicate, SeaDream, Gemini, Local ComfyUI/A1111, style presets, and LLM enhancer.
|
|
69
|
+
- **Package Manifest (#138)**: declared kernel `peerDependencies` (`host-webserver`, `settings`, `llm`, `system-prompt`).
|
|
70
|
+
- **React Cleanup (#139)**: removed dead state hooks from `FalImageCard`.
|
|
71
|
+
- **Complete Localization (#140)**: eliminated hardcoded strings, wiring comprehensive dictionaries for en, ru, and zh.
|
|
72
|
+
|
|
58
73
|
## 🎨 Supported Generation Backends
|
|
59
74
|
|
|
60
75
|
* **`fal`** (Default): FAL.ai queue for FLUX.1, SDXL, Clarity Upscaler, and BiRefNet.
|
package/README.ru.md
CHANGED
|
@@ -45,6 +45,8 @@ graph LR
|
|
|
45
45
|
T6[generate_image_pack]
|
|
46
46
|
T7[compare_images]
|
|
47
47
|
T8[inspect_image_quality]
|
|
48
|
+
T9[edit_image]
|
|
49
|
+
T10[vary_image]
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
subgraph Core [🛡️ Ядро надежности и кэша]
|
|
@@ -90,6 +92,8 @@ graph LR
|
|
|
90
92
|
| Инструмент | Назначение | Ключевые параметры |
|
|
91
93
|
|---|---|---|
|
|
92
94
|
| **`generate_image`** | Генерация изображения по тексту | `prompt`, `image_size`, `seed`, `style`, `negative_prompt`, `count` |
|
|
95
|
+
| **`edit_image`** | Направленное редактирование / inpainting с авто-поиском оригинала (#142, #144, #145) | `prompt`, `image` (по умолч. `latest`), `mask`, `strength` |
|
|
96
|
+
| **`vary_image`** | Генерация вариаций существующей картинки (#143, #144) | `image` (по умолч. `latest`), `prompt`, `variation_strength` (0.1–0.9), `count` |
|
|
93
97
|
| **`remove_background`** | Удаление фона с сохранением прозрачного PNG | `image`, `model`, `output_name` |
|
|
94
98
|
| **`upscale_image`** | Увеличение разрешения в 2x / 4x с детализацией | `image`, `scale` (2/4), `prompt`, `creativity` |
|
|
95
99
|
| **`vectorize_image`** | Векторизация растра в чистый SVG с квантованием палитры | `image`, `color_mode`, `paletteSize` |
|
|
@@ -111,6 +115,14 @@ graph LR
|
|
|
111
115
|
|
|
112
116
|
---
|
|
113
117
|
|
|
118
|
+
|
|
119
|
+
## 🚀 Обновления v0.10.4: жизненный цикл Cordis, полнота настроек и интернационализация
|
|
120
|
+
- **Жизненный цикл Cordis (#136)**: регистрация всех 7 инструментов обёрнута в `ctx.effect` для штатной отписки при перезагрузке плагина.
|
|
121
|
+
- **Полнота настроек в GUI (#137)**: в карточку настроек добавлены поля для Replicate, SeaDream, Gemini, Local ComfyUI/A1111, пресетов стилей и LLM-улучшителя.
|
|
122
|
+
- **Манифест зависимостей (#138)**: в `peerDependencies` задекларированы все используемые службы ядра (`host-webserver`, `settings`, `llm`, `system-prompt`).
|
|
123
|
+
- **Оптимизация компонентов (#139)**: из `FalImageCard` удалён неиспользуемый мёртвый стейт.
|
|
124
|
+
- **100% интернационализация (#140)**: устранены захардкоженные русские строки, добавлены переводы на русский, английский и китайский языки.
|
|
125
|
+
|
|
114
126
|
## 🎨 Поддерживаемые провайдеры генерации
|
|
115
127
|
|
|
116
128
|
* **`fal`** *(дефолт)*: Сверхбыстрая очередь FAL.ai (`FLUX.1-schnell`, `FLUX-dev`, `SDXL`, BiRefNet, Clarity Upscaler).
|
package/README.zh.md
CHANGED
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
|
|
38
38
|
---
|
|
39
39
|
|
|
40
|
+
|
|
41
|
+
## 🚀 v0.10.4 更新:Cordis 生命周期、完整设置界面与国际化
|
|
42
|
+
- **Cordis 生命周期 (#136)**:将所有 7 个工具注册包装在 `ctx.effect` 中,以便在热重载时正常释放。
|
|
43
|
+
- **设置界面完整性 (#137)**:添加了 Replicate、SeaDream、Gemini、本地 ComfyUI/A1111、风格预设和 LLM 增强器的配置字段。
|
|
44
|
+
- **包清单依赖 (#138)**:在 `peerDependencies` 中补齐了内核服务依赖。
|
|
45
|
+
- **React 组件优化 (#139)**:从 `FalImageCard` 中移除了未使用的无用状态。
|
|
46
|
+
- **全面国际化 (#140)**:消除了硬编码文本,为中、英、俄语提供完整翻译。
|
|
47
|
+
|
|
40
48
|
## 📦 安装
|
|
41
49
|
|
|
42
50
|
```bash
|
package/lib/client.js
CHANGED
|
@@ -465,22 +465,53 @@ window.__ModuleLoader__.load({
|
|
|
465
465
|
|
|
466
466
|
/** Field specs in display order. */
|
|
467
467
|
const booleanField = (name) => ({ name, type: 'boolean', parse: (v) => v === 'true' || v === true, format: (v) => String(!!v) })
|
|
468
|
+
const STYLE_PRESET_OPTIONS = ['', 'cinematic', 'anime', 'digital_art', 'photographic', 'fantasy', 'isometric', 'cyberpunk', 'watercolor', 'origami', 'oil_painting', 'pixel_art', 'logo']
|
|
469
|
+
|
|
468
470
|
const FIELDS = [
|
|
469
471
|
{ field: 'enabled', spec: booleanField('enabled'), kind: 'select', options: ['true', 'false'], labelKey: 'f.enabled', hintKey: 'f.enabledHint' },
|
|
470
|
-
{ field: 'provider', spec: selectField('provider', PROVIDERS), kind: 'select', options: PROVIDERS, labelKey: 'f.provider', hintKey: 'f.providerHint', providerNeeds: { fal: '
|
|
472
|
+
{ field: 'provider', spec: selectField('provider', PROVIDERS), kind: 'select', options: PROVIDERS, labelKey: 'f.provider', hintKey: 'f.providerHint', providerNeeds: { fal: 'f.needFal', custom: 'f.needCustom', codex: 'f.needCodex', grok: 'f.needGrok', local: 'f.needLocal', seedream: 'f.needSeedream', gemini: 'f.needGemini', replicate: 'f.needReplicate' } },
|
|
473
|
+
|
|
474
|
+
// FAL
|
|
471
475
|
{ field: 'model', spec: textField('model'), kind: 'text', labelKey: 'f.model', hintKey: 'f.modelHint', placeholderKey: 'p.model', when: ['fal'] },
|
|
472
476
|
{ field: 'apiKeyEnv', spec: textField('apiKeyEnv'), kind: 'text', labelKey: 'f.apiKeyEnv', hintKey: 'f.apiKeyEnvHint', placeholderKey: 'p.apiKeyEnv', when: ['fal'] },
|
|
473
477
|
{ field: 'baseURL', spec: textField('baseURL'), kind: 'text', labelKey: 'f.baseURL', hintKey: 'f.baseURLHint', placeholderKey: 'p.baseURL', when: ['fal'] },
|
|
474
|
-
{ field: 'defaultSize', spec: selectField('defaultSize', IMAGE_SIZES), kind: 'select', options: IMAGE_SIZES, labelKey: 'f.defaultSize', hintKey: 'f.defaultSizeHint', sizePixels: SIZE_PIXELS },
|
|
475
|
-
{ field: 'defaultFormat', spec: selectField('defaultFormat', OUTPUT_FORMATS), kind: 'select', options: OUTPUT_FORMATS, labelKey: 'f.defaultFormat', hintKey: 'f.defaultFormatHint' },
|
|
476
478
|
{ field: 'pollIntervalMs', spec: numberField('pollIntervalMs'), kind: 'number', labelKey: 'f.pollIntervalMs', hintKey: 'f.pollIntervalMsHint', placeholderKey: 'p.pollIntervalMs', when: ['fal'] },
|
|
477
479
|
{ field: 'timeoutMs', spec: numberField('timeoutMs'), kind: 'number', labelKey: 'f.timeoutMs', hintKey: 'f.timeoutMsHint', placeholderKey: 'p.timeoutMs', when: ['fal'] },
|
|
478
|
-
|
|
480
|
+
|
|
481
|
+
// Custom
|
|
479
482
|
{ field: 'customBaseURL', spec: textField('customBaseURL'), kind: 'text', labelKey: 'f.customBaseURL', hintKey: 'f.customBaseURLHint', placeholderKey: 'p.customBaseURL', when: ['custom'] },
|
|
480
483
|
{ field: 'customModel', spec: textField('customModel'), kind: 'text', labelKey: 'f.customModel', hintKey: 'f.customModelHint', placeholderKey: 'p.customModel', when: ['custom'] },
|
|
481
484
|
{ field: 'customKeyEnv', spec: textField('customKeyEnv'), kind: 'text', labelKey: 'f.customKeyEnv', hintKey: 'f.customKeyEnvHint', placeholderKey: 'p.customKeyEnv', when: ['custom'] },
|
|
482
485
|
{ field: 'customSize', spec: textField('customSize'), kind: 'text', labelKey: 'f.customSize', hintKey: 'f.customSizeHint', placeholderKey: 'p.customSize', when: ['custom'] },
|
|
486
|
+
|
|
487
|
+
// Replicate
|
|
488
|
+
{ field: 'replicateModel', spec: textField('replicateModel'), kind: 'text', labelKey: 'f.replicateModel', hintKey: 'f.replicateModelHint', placeholderKey: 'p.replicateModel', when: ['replicate'] },
|
|
489
|
+
{ field: 'replicateKeyEnv', spec: textField('replicateKeyEnv'), kind: 'text', labelKey: 'f.replicateKeyEnv', hintKey: 'f.replicateKeyEnvHint', placeholderKey: 'p.replicateKeyEnv', when: ['replicate'] },
|
|
490
|
+
|
|
491
|
+
// SeaDream
|
|
492
|
+
{ field: 'seedreamModel', spec: textField('seedreamModel'), kind: 'text', labelKey: 'f.seedreamModel', hintKey: 'f.seedreamModelHint', placeholderKey: 'p.seedreamModel', when: ['seedream'] },
|
|
493
|
+
{ field: 'seedreamKeyEnv', spec: textField('seedreamKeyEnv'), kind: 'text', labelKey: 'f.seedreamKeyEnv', hintKey: 'f.seedreamKeyEnvHint', placeholderKey: 'p.seedreamKeyEnv', when: ['seedream'] },
|
|
494
|
+
{ field: 'seedreamBaseURL', spec: textField('seedreamBaseURL'), kind: 'text', labelKey: 'f.seedreamBaseURL', hintKey: 'f.seedreamBaseURLHint', placeholderKey: 'p.seedreamBaseURL', when: ['seedream'] },
|
|
495
|
+
|
|
496
|
+
// Gemini
|
|
497
|
+
{ field: 'geminiModel', spec: textField('geminiModel'), kind: 'text', labelKey: 'f.geminiModel', hintKey: 'f.geminiModelHint', placeholderKey: 'p.geminiModel', when: ['gemini'] },
|
|
498
|
+
{ field: 'geminiKeyEnv', spec: textField('geminiKeyEnv'), kind: 'text', labelKey: 'f.geminiKeyEnv', hintKey: 'f.geminiKeyEnvHint', placeholderKey: 'p.geminiKeyEnv', when: ['gemini'] },
|
|
499
|
+
|
|
500
|
+
// Local ComfyUI / A1111
|
|
501
|
+
{ field: 'localComfyUrl', spec: textField('localComfyUrl'), kind: 'text', labelKey: 'f.localComfyUrl', hintKey: 'f.localComfyUrlHint', placeholderKey: 'p.localComfyUrl', when: ['local'] },
|
|
502
|
+
{ field: 'localA1111Url', spec: textField('localA1111Url'), kind: 'text', labelKey: 'f.localA1111Url', hintKey: 'f.localA1111UrlHint', placeholderKey: 'p.localA1111Url', when: ['local'] },
|
|
503
|
+
|
|
504
|
+
// Common Style & LLM Enhancer
|
|
505
|
+
{ field: 'stylePreset', spec: selectField('stylePreset', STYLE_PRESET_OPTIONS), kind: 'select', options: STYLE_PRESET_OPTIONS, labelKey: 'f.stylePreset', hintKey: 'f.stylePresetHint' },
|
|
506
|
+
{ field: 'enableLlmEnhancer', spec: booleanField('enableLlmEnhancer'), kind: 'select', options: ['true', 'false'], labelKey: 'f.enableLlmEnhancer', hintKey: 'f.enableLlmEnhancerHint' },
|
|
507
|
+
|
|
508
|
+
// Output & Defaults
|
|
509
|
+
{ field: 'defaultSize', spec: selectField('defaultSize', IMAGE_SIZES), kind: 'select', options: IMAGE_SIZES, labelKey: 'f.defaultSize', hintKey: 'f.defaultSizeHint', sizePixels: SIZE_PIXELS },
|
|
510
|
+
{ field: 'defaultFormat', spec: selectField('defaultFormat', OUTPUT_FORMATS), kind: 'select', options: OUTPUT_FORMATS, labelKey: 'f.defaultFormat', hintKey: 'f.defaultFormatHint' },
|
|
511
|
+
{ field: 'deliverAs', spec: selectField('deliverAs', DELIVERY_MODES), kind: 'select', options: DELIVERY_MODES, labelKey: 'f.deliverAs', hintKey: 'f.deliverAsHint' },
|
|
483
512
|
{ field: 'outputDir', spec: textField('outputDir'), kind: 'text', labelKey: 'f.outputDir', hintKey: 'f.outputDirHint', placeholderKey: 'p.outputDir' },
|
|
513
|
+
{ field: 'cacheTtlDays', spec: numberField('cacheTtlDays'), kind: 'number', labelKey: 'f.cacheTtlDays', hintKey: 'f.cacheTtlDaysHint', placeholderKey: 'p.cacheTtlDays' },
|
|
514
|
+
{ field: 'maxHistoryEntries', spec: numberField('maxHistoryEntries'), kind: 'number', labelKey: 'f.maxHistoryEntries', hintKey: 'f.maxHistoryEntriesHint', placeholderKey: 'p.maxHistoryEntries' },
|
|
484
515
|
]
|
|
485
516
|
|
|
486
517
|
function Field(props) {
|
|
@@ -501,7 +532,7 @@ window.__ModuleLoader__.load({
|
|
|
501
532
|
}
|
|
502
533
|
if (entry.kind === 'select') {
|
|
503
534
|
const px = entry.sizePixels && entry.sizePixels[state.text] ? ` — ${entry.sizePixels[state.text]}` : ''
|
|
504
|
-
const need = entry.providerNeeds && entry.providerNeeds[state.text] ? ` (
|
|
535
|
+
const need = entry.providerNeeds && entry.providerNeeds[state.text] ? ` (${t(entry.providerNeeds[state.text])})` : ''
|
|
505
536
|
const hint = base.hint + px + need
|
|
506
537
|
return react_jsx_runtime.jsx(SelectField, { ...base, hint, options: entry.options, inheritLabel: t('f.inherit') })
|
|
507
538
|
}
|
|
@@ -828,19 +859,17 @@ function FalImageCard(props) {
|
|
|
828
859
|
prompt = raw ? (JSON.parse(raw).prompt || '') : ''
|
|
829
860
|
} catch (e) { prompt = '' }
|
|
830
861
|
|
|
862
|
+
const t = props.t || ((k) => k)
|
|
831
863
|
const repeatText = (() => {
|
|
832
864
|
const parts = [String(args.prompt || '').trim()]
|
|
833
865
|
const size = args.image_size || args.size
|
|
834
866
|
const seed = args.seed
|
|
835
|
-
if (size) parts.push(
|
|
836
|
-
if (seed !== undefined) parts.push(
|
|
867
|
+
if (size) parts.push(`${t('card.sizePrefix')}: ${size}`)
|
|
868
|
+
if (seed !== undefined) parts.push(`${t('card.seedPrefix')}: ${seed}`)
|
|
837
869
|
return parts.filter(Boolean).join(', ')
|
|
838
870
|
})()
|
|
839
871
|
|
|
840
872
|
const [editing, setEditing] = react.useState(false)
|
|
841
|
-
const [inpaintOpen, setInpaintOpen] = react.useState(false)
|
|
842
|
-
const [compareSlider, setCompareSlider] = react.useState(50)
|
|
843
|
-
const [showCompare, setShowCompare] = react.useState(false)
|
|
844
873
|
const [editText, setEditText] = react.useState('')
|
|
845
874
|
|
|
846
875
|
const onEditPrompt = () => {
|
|
@@ -862,8 +891,6 @@ function FalImageCard(props) {
|
|
|
862
891
|
}
|
|
863
892
|
|
|
864
893
|
const [copied, setCopied] = react.useState(false)
|
|
865
|
-
const canvasRef = react.useRef(null)
|
|
866
|
-
const [drawing, setDrawing] = react.useState(false)
|
|
867
894
|
|
|
868
895
|
const sendActionPrompt = async (text) => {
|
|
869
896
|
try {
|
|
@@ -893,23 +920,23 @@ function FalImageCard(props) {
|
|
|
893
920
|
|
|
894
921
|
const onReroll = () => {
|
|
895
922
|
const nextSeed = (typeof args.seed === 'number' ? args.seed + 1 : Math.floor(Math.random() * 100000))
|
|
896
|
-
sendActionPrompt(
|
|
923
|
+
sendActionPrompt(`${t('card.actionReroll')} (${nextSeed}): ${prompt || repeatText}`)
|
|
897
924
|
}
|
|
898
925
|
|
|
899
926
|
const onUpscale = () => {
|
|
900
927
|
const ref = parsed.attachment ? parsed.attachment.attachmentId : (parsed.url || '')
|
|
901
|
-
sendActionPrompt(
|
|
928
|
+
sendActionPrompt(`${t('card.actionUpscale')}: upscale_image(image: "${ref}")`)
|
|
902
929
|
}
|
|
903
930
|
|
|
904
931
|
const onRemoveBg = () => {
|
|
905
932
|
const ref = parsed.attachment ? parsed.attachment.attachmentId : (parsed.url || '')
|
|
906
|
-
sendActionPrompt(
|
|
933
|
+
sendActionPrompt(`${t('card.actionRemoveBg')}: remove_background(image: "${ref}")`)
|
|
907
934
|
}
|
|
908
935
|
|
|
909
936
|
|
|
910
937
|
|
|
911
938
|
const head = react.createElement('div', { className: 'fal-head' },
|
|
912
|
-
running ? '
|
|
939
|
+
running ? t('card.generating') : failed ? t('card.failed') : t('card.image'))
|
|
913
940
|
|
|
914
941
|
// «Повторить генерацию»: отправляет в чат новое сообщение с теми же
|
|
915
942
|
// промптом и параметрами, извлечёнными из исходного вызова инструмента.
|
|
@@ -933,10 +960,10 @@ function FalImageCard(props) {
|
|
|
933
960
|
const body = []
|
|
934
961
|
if (prompt) body.push(react.createElement('div', { className: 'fal-prompt', key: 'p' }, prompt))
|
|
935
962
|
if (args.originalPrompt && args.originalPrompt !== prompt) {
|
|
936
|
-
body.push(react.createElement('div', { className: 'fal-meta', key: 'orig' },
|
|
963
|
+
body.push(react.createElement('div', { className: 'fal-meta', key: 'orig' }, `${t('card.origPrompt')}: ${args.originalPrompt}`))
|
|
937
964
|
}
|
|
938
965
|
if (failed) {
|
|
939
|
-
body.push(react.createElement('div', { className: 'fal-err', key: 'e' }, parsed.text || '
|
|
966
|
+
body.push(react.createElement('div', { className: 'fal-err', key: 'e' }, parsed.text || t('card.unknownError')))
|
|
940
967
|
} else if (!parsed.attachment && parsed.url) {
|
|
941
968
|
body.push(react.createElement('img', {
|
|
942
969
|
key: 'i',
|
|
@@ -990,7 +1017,7 @@ function FalImageCard(props) {
|
|
|
990
1017
|
type: 'button',
|
|
991
1018
|
className: 'fal-action-btn',
|
|
992
1019
|
onClick: onReroll,
|
|
993
|
-
title: '
|
|
1020
|
+
title: t('card.rerollTitle')
|
|
994
1021
|
}, '🔄 Re-roll'))
|
|
995
1022
|
|
|
996
1023
|
btns.push(react.createElement('button', {
|
|
@@ -998,7 +1025,7 @@ function FalImageCard(props) {
|
|
|
998
1025
|
type: 'button',
|
|
999
1026
|
className: 'fal-action-btn',
|
|
1000
1027
|
onClick: onUpscale,
|
|
1001
|
-
title: '
|
|
1028
|
+
title: t('card.upscaleTitle')
|
|
1002
1029
|
}, '🔍 2x Upscale'))
|
|
1003
1030
|
|
|
1004
1031
|
btns.push(react.createElement('button', {
|
|
@@ -1006,7 +1033,7 @@ function FalImageCard(props) {
|
|
|
1006
1033
|
type: 'button',
|
|
1007
1034
|
className: 'fal-action-btn',
|
|
1008
1035
|
onClick: onRemoveBg,
|
|
1009
|
-
title: '
|
|
1036
|
+
title: t('card.removeBgTitle')
|
|
1010
1037
|
}, '✂️ Remove BG'))
|
|
1011
1038
|
|
|
1012
1039
|
if (prompt) {
|
|
@@ -1015,8 +1042,8 @@ function FalImageCard(props) {
|
|
|
1015
1042
|
type: 'button',
|
|
1016
1043
|
className: 'fal-action-btn',
|
|
1017
1044
|
onClick: onCopyPrompt,
|
|
1018
|
-
title: '
|
|
1019
|
-
}, copied ? '
|
|
1045
|
+
title: t('card.copyPromptTitle')
|
|
1046
|
+
}, copied ? t('card.copied') : t('card.copyPrompt')))
|
|
1020
1047
|
}
|
|
1021
1048
|
|
|
1022
1049
|
if (args.seed !== undefined) {
|
|
@@ -1025,8 +1052,8 @@ function FalImageCard(props) {
|
|
|
1025
1052
|
type: 'button',
|
|
1026
1053
|
className: 'fal-action-btn',
|
|
1027
1054
|
onClick: onCopySeed,
|
|
1028
|
-
title: '
|
|
1029
|
-
}, `🎲
|
|
1055
|
+
title: t('card.copySeedTitle')
|
|
1056
|
+
}, `🎲 ${t('card.seedPrefix')}: ${args.seed}`))
|
|
1030
1057
|
}
|
|
1031
1058
|
|
|
1032
1059
|
body.push(react.createElement('div', { key: 'action-row', className: 'fal-btn-row' }, btns))
|
|
@@ -1042,7 +1069,7 @@ function FalImageCard(props) {
|
|
|
1042
1069
|
type: 'button',
|
|
1043
1070
|
className: 'fal-repeat',
|
|
1044
1071
|
onClick: repeat,
|
|
1045
|
-
}, '
|
|
1072
|
+
}, t('card.repeatGen')))
|
|
1046
1073
|
}
|
|
1047
1074
|
if (!running && !failed) {
|
|
1048
1075
|
if (editing) {
|
|
@@ -1051,7 +1078,7 @@ function FalImageCard(props) {
|
|
|
1051
1078
|
type: 'text',
|
|
1052
1079
|
className: 'fal-input',
|
|
1053
1080
|
value: editText,
|
|
1054
|
-
placeholder: prompt || '
|
|
1081
|
+
placeholder: prompt || t('card.newPromptPlaceholder'),
|
|
1055
1082
|
onChange: (e) => setEditText(e.target.value),
|
|
1056
1083
|
}))
|
|
1057
1084
|
body.push(react.createElement('button', {
|
|
@@ -1071,14 +1098,14 @@ function FalImageCard(props) {
|
|
|
1071
1098
|
setEditing(false)
|
|
1072
1099
|
setEditText('')
|
|
1073
1100
|
},
|
|
1074
|
-
}, '
|
|
1101
|
+
}, t('card.send')))
|
|
1075
1102
|
} else {
|
|
1076
1103
|
body.push(react.createElement('button', {
|
|
1077
1104
|
key: 'edit',
|
|
1078
1105
|
type: 'button',
|
|
1079
1106
|
className: 'fal-repeat',
|
|
1080
1107
|
onClick: () => { setEditText(prompt || ''); setEditing(true) },
|
|
1081
|
-
}, '
|
|
1108
|
+
}, t('card.editPrompt')))
|
|
1082
1109
|
}
|
|
1083
1110
|
}
|
|
1084
1111
|
if (!running && !failed && downloadUrl) {
|
|
@@ -1087,7 +1114,7 @@ function FalImageCard(props) {
|
|
|
1087
1114
|
className: 'fal-repeat',
|
|
1088
1115
|
href: downloadUrl,
|
|
1089
1116
|
download: 'generated-image.png',
|
|
1090
|
-
}, '
|
|
1117
|
+
}, t('card.download')))
|
|
1091
1118
|
}
|
|
1092
1119
|
|
|
1093
1120
|
return react.createElement('div', { className: 'fal-card' }, head, body)
|