@adminforth/bulk-ai-flow 1.25.2 → 1.26.1
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/build.log +2 -2
- package/custom/ImageGenerationCarousel.vue +106 -147
- package/custom/Swiper.vue +1 -1
- package/custom/VisionAction.vue +336 -109
- package/custom/VisionTable.vue +382 -222
- package/dist/custom/ImageGenerationCarousel.vue +106 -147
- package/dist/custom/Swiper.vue +1 -1
- package/dist/custom/VisionAction.vue +336 -109
- package/dist/custom/VisionTable.vue +382 -222
- package/dist/index.js +2 -1
- package/index.ts +2 -3
- package/package.json +3 -3
- package/types.ts +6 -0
package/build.log
CHANGED
|
@@ -14,5 +14,5 @@ custom/package.json
|
|
|
14
14
|
custom/pnpm-lock.yaml
|
|
15
15
|
custom/tsconfig.json
|
|
16
16
|
|
|
17
|
-
sent
|
|
18
|
-
total size is
|
|
17
|
+
sent 129,923 bytes received 191 bytes 260,228.00 bytes/sec
|
|
18
|
+
total size is 129,205 speedup is 0.99
|
|
@@ -1,144 +1,99 @@
|
|
|
1
1
|
|
|
2
2
|
<template>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
3
|
+
<Dialog
|
|
4
|
+
ref="dialogRef"
|
|
5
|
+
:header="$t('Generate image with AI')"
|
|
6
|
+
:closable="true"
|
|
7
|
+
class="w-full lg:w-[1100px]"
|
|
8
|
+
:beforeCancelFunction="async () => { emit('close'); return true; }"
|
|
9
|
+
:buttons="dialogButtons"
|
|
10
|
+
:click-to-close-outside="false"
|
|
11
|
+
>
|
|
12
|
+
<div class="flex flex-col gap-4">
|
|
13
|
+
<Textarea
|
|
14
|
+
v-model="prompt"
|
|
15
|
+
:placeholder="$t('Prompt which will be passed to AI network')"
|
|
16
|
+
class="w-full text-sm leading-relaxed border border-gray-200 bg-gray-50/30 dark:border-gray-700 dark:bg-gray-800/40 dark:text-gray-100 rounded-xl focus:outline-none focus:border-gray-300 resize-none"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<div class="grid grid-cols-2 gap-4">
|
|
20
|
+
<div class="flex flex-col gap-2">
|
|
21
|
+
<p class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
22
|
+
{{ $t('Source Image') }}
|
|
23
|
+
</p>
|
|
24
|
+
<div class="h-96 rounded-default border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 overflow-hidden flex items-center justify-center">
|
|
25
|
+
<img
|
|
26
|
+
v-if="attachmentFiles[0]"
|
|
27
|
+
:src="attachmentFiles[0]"
|
|
28
|
+
class="w-full h-full object-cover"
|
|
29
|
+
/>
|
|
30
|
+
<div v-else class="flex flex-col items-center justify-center gap-2 text-gray-400">
|
|
31
|
+
<svg class="w-12 h-12 text-gray-300 stroke-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
32
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
33
|
+
</svg>
|
|
34
|
+
<p class="text-xs font-medium">{{ $t('No source image') }}</p>
|
|
21
35
|
</div>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
:title="$t('Prompt which will be passed to AI network')"
|
|
33
|
-
></textarea>
|
|
34
|
-
|
|
35
|
-
<!-- Thumbnails -->
|
|
36
|
-
<div class="mt-2 flex flex-wrap gap-2">
|
|
37
|
-
<img
|
|
38
|
-
v-for="(img, idx) in attachmentFiles"
|
|
39
|
-
:key="idx"
|
|
40
|
-
:src="img"
|
|
41
|
-
class="w-20 h-20 object-cover rounded cursor-pointer border hover:border-blue-500 transition"
|
|
42
|
-
:alt="`Generated image ${idx + 1}`"
|
|
43
|
-
@click="zoomImage(img)"
|
|
44
|
-
/>
|
|
45
|
-
</div>
|
|
46
|
-
|
|
47
|
-
<!-- Fullscreen Modal -->
|
|
48
|
-
<div
|
|
49
|
-
v-if="zoomedImage"
|
|
50
|
-
class="w-full h-full fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-80"
|
|
51
|
-
@click.self="closeZoom"
|
|
52
|
-
>
|
|
53
|
-
<img
|
|
54
|
-
:src="zoomedImage"
|
|
55
|
-
ref="zoomedImg"
|
|
56
|
-
class="max-w-full max-h-full rounded-lg object-contain cursor-grab z-75"
|
|
57
|
-
/>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<div class="flex flex-col items-center justify-center w-full relative">
|
|
61
|
-
<div
|
|
62
|
-
v-if="loading"
|
|
63
|
-
class=" absolute flex items-center justify-center w-full h-full z-40 bg-white/80 dark:bg-gray-900/80 rounded-lg"
|
|
64
|
-
>
|
|
65
|
-
<div role="status" class="absolute -translate-x-1/2 -translate-y-1/2 top-2/4 left-1/2">
|
|
66
|
-
<svg aria-hidden="true" class="w-8 h-8 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/><path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/></svg>
|
|
67
|
-
<span class="sr-only">{{ $t('Loading...') }}</span>
|
|
68
|
-
</div>
|
|
69
|
-
</div>
|
|
70
|
-
|
|
71
|
-
<div v-if="loadingTimer" class="absolute pt-12 flex items-center justify-center w-full h-full z-40 bg-white/80 dark:bg-gray-900/80 rounded-lg">
|
|
72
|
-
<div class="text-gray-800 dark:text-gray-100 text-lg font-semibold"
|
|
73
|
-
v-if="!historicalAverage"
|
|
74
|
-
>
|
|
75
|
-
{{ formatTime(loadingTimer) }} {{ $t('passed...') }}
|
|
76
|
-
</div>
|
|
77
|
-
<div class="w-64" v-else>
|
|
78
|
-
<ProgressBar
|
|
79
|
-
class="absolute max-w-full"
|
|
80
|
-
:currentValue="loadingTimer < historicalAverage ? loadingTimer : historicalAverage"
|
|
81
|
-
:minValue="0"
|
|
82
|
-
:maxValue="historicalAverage"
|
|
83
|
-
:showValues="false"
|
|
84
|
-
:progressFormatter="(value: number, percentage: number) => `${ formatTime(loadingTimer) } ( ~ ${ Math.floor( (
|
|
85
|
-
loadingTimer < historicalAverage ? loadingTimer : historicalAverage
|
|
86
|
-
) / historicalAverage * 100) }% )`"
|
|
87
|
-
/>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<div v-if="attachmentFiles.length > 1" class="flex flex-wrap gap-1.5">
|
|
38
|
+
<img
|
|
39
|
+
v-for="(img, idx) in attachmentFiles"
|
|
40
|
+
:key="idx"
|
|
41
|
+
:src="img"
|
|
42
|
+
class="w-10 h-10 object-cover rounded border border-gray-200"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
90
46
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
47
|
+
<div class="flex flex-col gap-2">
|
|
48
|
+
<p class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
|
49
|
+
{{ $t('Generated Image') }}
|
|
50
|
+
</p>
|
|
51
|
+
<div class="relative h-96 rounded-default border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 overflow-hidden">
|
|
52
|
+
<div v-if="loading || loadingTimer" class="absolute inset-0 z-10 flex flex-col items-center justify-center bg-white/80 dark:bg-gray-900/80">
|
|
53
|
+
<Spinner v-if="loading" class="w-8 h-8" />
|
|
54
|
+
<div v-if="loadingTimer" class="mt-3">
|
|
55
|
+
<div v-if="!historicalAverage" class="text-gray-800 dark:text-gray-100 text-sm font-semibold">
|
|
56
|
+
{{ formatTime(loadingTimer) }} {{ $t('passed...') }}
|
|
95
57
|
</div>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
:
|
|
104
|
-
|
|
105
|
-
|
|
58
|
+
<div v-else class="w-40">
|
|
59
|
+
<ProgressBar
|
|
60
|
+
:currentValue="loadingTimer < historicalAverage ? loadingTimer : historicalAverage"
|
|
61
|
+
:minValue="0"
|
|
62
|
+
:maxValue="historicalAverage"
|
|
63
|
+
:showValues="false"
|
|
64
|
+
:progressFormatter="(_value: number, _percentage: number) => `${ formatTime(loadingTimer) } ( ~ ${ Math.floor( (
|
|
65
|
+
loadingTimer < historicalAverage ? loadingTimer : historicalAverage
|
|
66
|
+
) / historicalAverage * 100) }% )`"
|
|
67
|
+
/>
|
|
106
68
|
</div>
|
|
107
69
|
</div>
|
|
108
70
|
</div>
|
|
109
|
-
|
|
110
|
-
<div class="
|
|
111
|
-
|
|
112
|
-
@click="generateImages"
|
|
113
|
-
>{{ $t('Regenerate') }}</button>
|
|
114
|
-
<div class="flex gap-3">
|
|
115
|
-
<button type="button" class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
116
|
-
@click="emit('close')"
|
|
117
|
-
>{{ $t('Cancel') }}</button>
|
|
118
|
-
<button type="button" @click="confirmImage"
|
|
119
|
-
:disabled="loading || images.length === 0"
|
|
120
|
-
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center
|
|
121
|
-
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
|
|
122
|
-
disabled:opacity-50 disabled:cursor-not-allowed"
|
|
123
|
-
>{{ $t('Use image') }}</button>
|
|
124
|
-
</div>
|
|
71
|
+
|
|
72
|
+
<div v-if="errorMessage" class="absolute inset-0 z-10 flex items-center justify-center bg-white/80 dark:bg-gray-900/80">
|
|
73
|
+
<p class="text-red-500 dark:text-red-400 text-sm font-semibold px-4 text-center">{{ errorMessage }}</p>
|
|
125
74
|
</div>
|
|
75
|
+
|
|
76
|
+
<Skeleton v-if="!images.length && !loading && !loadingTimer && !errorMessage" type="image" class="w-full h-full" />
|
|
77
|
+
<Swiper v-else ref="sliderRef" :images="images" class="h-full" />
|
|
78
|
+
</div>
|
|
126
79
|
</div>
|
|
80
|
+
</div>
|
|
127
81
|
</div>
|
|
128
|
-
</
|
|
82
|
+
</Dialog>
|
|
83
|
+
|
|
129
84
|
</template>
|
|
130
85
|
|
|
131
86
|
<script setup lang="ts">
|
|
132
87
|
|
|
133
|
-
import { ref, onMounted, nextTick, Ref,
|
|
134
|
-
import { Carousel } from 'flowbite';
|
|
88
|
+
import { ref, onMounted, nextTick, Ref, computed } from 'vue'
|
|
135
89
|
import { callAdminForthApi } from '@/utils';
|
|
136
90
|
import { useI18n } from 'vue-i18n';
|
|
137
91
|
import adminforth from '@/adminforth';
|
|
138
|
-
import { ProgressBar } from '@/afcl';
|
|
92
|
+
import { ProgressBar, Dialog, Textarea, Spinner, Skeleton } from '@/afcl';
|
|
139
93
|
import Swiper from './Swiper.vue';
|
|
140
94
|
|
|
141
|
-
const { t
|
|
95
|
+
const { t } = useI18n();
|
|
96
|
+
const dialogRef = ref(null)
|
|
142
97
|
const sliderRef = ref(null)
|
|
143
98
|
|
|
144
99
|
const prompt = ref('');
|
|
@@ -168,8 +123,35 @@ onMounted(async () => {
|
|
|
168
123
|
template = 'Generate image for field {{field}} in {{resource}}. No text should be on image.';
|
|
169
124
|
}
|
|
170
125
|
prompt.value = template;
|
|
126
|
+
dialogRef.value?.open();
|
|
171
127
|
});
|
|
172
128
|
|
|
129
|
+
const dialogButtons = computed(() => [
|
|
130
|
+
{
|
|
131
|
+
label: t('Cancel'),
|
|
132
|
+
options: {
|
|
133
|
+
class: 'afcl-button',
|
|
134
|
+
mode: 'secondary',
|
|
135
|
+
},
|
|
136
|
+
onclick: () => emit('close'),
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: t('Use image'),
|
|
140
|
+
options: {
|
|
141
|
+
class: 'afcl-button',
|
|
142
|
+
disabled: loading.value || images.value.length === 0,
|
|
143
|
+
loader: loading.value,
|
|
144
|
+
},
|
|
145
|
+
onclick: () => confirmImage(),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
label: t('Regenerate'),
|
|
149
|
+
options: {
|
|
150
|
+
class: 'afcl-button',
|
|
151
|
+
},
|
|
152
|
+
onclick: () => generateImages(),
|
|
153
|
+
},
|
|
154
|
+
]);
|
|
173
155
|
|
|
174
156
|
async function confirmImage() {
|
|
175
157
|
loading.value = true;
|
|
@@ -275,7 +257,7 @@ async function generateImages() {
|
|
|
275
257
|
error = resp.error;
|
|
276
258
|
}
|
|
277
259
|
if (!resp) {
|
|
278
|
-
error =
|
|
260
|
+
error = t('Error creating image generation job');
|
|
279
261
|
}
|
|
280
262
|
|
|
281
263
|
if (error) {
|
|
@@ -306,7 +288,7 @@ async function generateImages() {
|
|
|
306
288
|
};
|
|
307
289
|
jobStatus = jobResponse?.job?.status;
|
|
308
290
|
if (jobStatus === 'failed') {
|
|
309
|
-
error = jobResponse?.job?.error ||
|
|
291
|
+
error = jobResponse?.job?.error || t('Image generation job failed');
|
|
310
292
|
}
|
|
311
293
|
await new Promise((resolve) => setTimeout(resolve, props.regenerateImagesRefreshRate));
|
|
312
294
|
}
|
|
@@ -339,31 +321,8 @@ async function generateImages() {
|
|
|
339
321
|
sliderRef.value?.slideTo(images.value.length-1);
|
|
340
322
|
|
|
341
323
|
await nextTick();
|
|
342
|
-
|
|
343
|
-
loading.value = false;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
import mediumZoom from 'medium-zoom'
|
|
347
324
|
|
|
348
|
-
|
|
349
|
-
const zoomedImg = ref(null)
|
|
350
|
-
|
|
351
|
-
function zoomImage(img) {
|
|
352
|
-
zoomedImage.value = img
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function closeZoom() {
|
|
356
|
-
zoomedImage.value = null
|
|
325
|
+
loading.value = false;
|
|
357
326
|
}
|
|
358
327
|
|
|
359
|
-
watch(zoomedImage, async (val) => {
|
|
360
|
-
await nextTick()
|
|
361
|
-
if (val && zoomedImg.value) {
|
|
362
|
-
mediumZoom(zoomedImg.value, {
|
|
363
|
-
margin: 24,
|
|
364
|
-
background: 'rgba(0, 0, 0, 0.9)',
|
|
365
|
-
scrollOffset: 150
|
|
366
|
-
}).show()
|
|
367
|
-
}
|
|
368
|
-
})
|
|
369
328
|
</script>
|
package/custom/Swiper.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<swiper-container class="flex items-center justify-center w-full h-full">
|
|
3
3
|
<swiper-slide v-for="(image, index) in images" :key="index">
|
|
4
|
-
<img :src="image" class="object-
|
|
4
|
+
<img :src="image" class="object-cover w-full h-full" />
|
|
5
5
|
</swiper-slide>
|
|
6
6
|
</swiper-container>
|
|
7
7
|
</template>
|