@adminforth/bulk-ai-flow 1.7.4 → 1.8.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/README.md CHANGED
@@ -1 +1,7 @@
1
- Readme
1
+ # AdminForth Bulk AI Flow Plugin
2
+
3
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT" /> <img src="https://woodpecker.devforth.io/api/badges/3848/status.svg" alt="Build Status" /> <a href="https://www.npmjs.com/package/@adminforth/bulk-ai-flow"> <img src="https://img.shields.io/npm/dt/@adminforth/bulk-ai-flow" alt="npm downloads" /></a> <a href="https://www.npmjs.com/package/@adminforth/bulk-ai-flow"><img src="https://img.shields.io/npm/v/@adminforth/bulk-ai-flow" alt="npm version" /></a> <a href="https://www.npmjs.com/package/@adminforth/bulk-ai-flow">
4
+
5
+ Allows you to log changes made to table records via adminforth.
6
+
7
+ ## For usage, see [AdminForth Bulk AI Flow Documentation](https://adminforth.dev/docs/tutorial/Plugins/bulk-ai-flow/)
package/build.log CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  sending incremental file list
6
6
  custom/
7
- custom/imageGenerationCarousel.vue
7
+ custom/ImageGenerationCarousel.vue
8
+ custom/VisionAction.vue
9
+ custom/VisionTable.vue
8
10
  custom/package-lock.json
9
11
  custom/package.json
10
12
  custom/tsconfig.json
11
- custom/visionAction.vue
12
- custom/visionTable.vue
13
13
 
14
- sent 180,683 bytes received 134 bytes 361,634.00 bytes/sec
15
- total size is 180,148 speedup is 1.00
14
+ sent 179,998 bytes received 134 bytes 360,264.00 bytes/sec
15
+ total size is 179,465 speedup is 1.00
@@ -1,8 +1,8 @@
1
1
 
2
2
  <template>
3
3
  <!-- Main modal -->
4
- <div tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 bottom-0 z-10 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-black/50 dark:bg-gray-900 dark:bg-opacity-50">
5
- <div class="relative p-4 w-10/12 max-w-full max-h-full ">
4
+ <div tabindex="-1" class="fixed inset-0 z-10 flex justify-center items-center dark:bg-gray-900/50 overflow-y-auto">
5
+ <div class="relative p-4 w-full max-w-[1600px] max-h-[90vh] ">
6
6
  <!-- Modal content -->
7
7
  <div class="relative bg-white rounded-lg shadow-xl dark:bg-gray-700">
8
8
  <!-- Modal header -->
@@ -95,20 +95,20 @@
95
95
  </div>
96
96
 
97
97
 
98
- <div id="gallery" class="relative w-full" data-carousel="static">
98
+ <div id="gallery" class="relative w-full min-w-0" data-carousel="static">
99
99
  <!-- Carousel wrapper -->
100
100
  <div class="relative h-56 overflow-hidden rounded-lg md:h-[calc(100vh-400px)]">
101
101
  <!-- Item 1 -->
102
102
  <div
103
103
  v-for="(img, index) in images"
104
104
  :key="index"
105
+ class="flex items-center justify-center w-full h-full"
105
106
  :class="[
106
- index === 0 ? 'block' : 'hidden',
107
- 'duration-700 ease-in-out'
107
+ index === 0 ? 'block' : 'hidden'
108
108
  ]"
109
109
  data-carousel-item
110
110
  >
111
- <img :src="img" class="absolute block max-w-full max-h-full -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 object-cover"
111
+ <img :src="img" class="max-w-full max-h-full object-contain"
112
112
  :alt="`Generated image ${index + 1}`"
113
113
  />
114
114
  </div>
@@ -175,10 +175,6 @@
175
175
  </div>
176
176
  </div>
177
177
  </div>
178
-
179
-
180
-
181
-
182
178
  </template>
183
179
 
184
180
  <script setup lang="ts">
@@ -193,8 +189,8 @@ import { ProgressBar } from '@/afcl';
193
189
  const { t: $t } = useI18n();
194
190
 
195
191
  const prompt = ref('');
196
- const emit = defineEmits(['close', 'selectImage', 'error']);
197
- const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage']);
192
+ const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
193
+ const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex']);
198
194
  const images = ref([]);
199
195
  const loading = ref(false);
200
196
  const attachmentFiles = ref<string[]>([])
@@ -208,12 +204,14 @@ function minifyField(field: string): string {
208
204
 
209
205
  const caurosel = ref(null);
210
206
  onMounted(async () => {
211
- images.value.push((props.images || []));
207
+ for (const img of props.images || []) {
208
+ images.value.push(img);
209
+ }
212
210
  const temp = await getGenerationPrompt() || '';
213
211
  prompt.value = temp[props.fieldName];
214
212
  await nextTick();
215
213
 
216
- const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
214
+ const currentIndex = props.carouselImageIndex || 0;
217
215
  caurosel.value = new Carousel(
218
216
  document.getElementById('gallery'),
219
217
  images.value.map((img, index) => {
@@ -298,6 +296,12 @@ async function confirmImage() {
298
296
  const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
299
297
  const img = images.value[currentIndex];
300
298
 
299
+ props.images.splice(0, props.images.length);
300
+ for (const i of images.value) {
301
+ props.images.push(i);
302
+ }
303
+
304
+ emit('updateCarouselIndex', currentIndex, props.recordId, props.fieldName);
301
305
  emit('selectImage', img, props.recordId, props.fieldName);
302
306
  emit('close');
303
307
 
@@ -5,60 +5,38 @@
5
5
  </div>
6
6
  <p class="text-justify max-h-[18px] truncate max-w-[60vw] md:max-w-none">{{ props.meta.actionName }}</p>
7
7
  </div>
8
- <Dialog ref="confirmDialog">
9
- <div
10
- class="fixed inset-0 z-20 flex items-center justify-center bg-black/40"
11
- >
12
- <div
13
- class="bulk-vision-dialog flex items-center justify-center relative w-[100vw] h-[100vh] max-h-[100vh] md:w-auto md:max-w-[95vw] md:min-w-[640px] md:h-auto md:max-h-[90vh] bg-white dark:bg-gray-900 rounded-none md:rounded-md shadow-2xl overflow-hidden"
14
- >
15
- <div class="bulk-vision-table flex flex-col items-center justify-evenly md:max-h-[90vh] gap-3 md:gap-4 w-full h-full p-4 md:p-6 overflow-y-auto overflow-x-auto">
16
- <button type="button"
17
- @click="closeDialog"
18
- class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" >
19
- <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
20
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
21
- </svg>
22
- </button>
23
-
24
- <div v-if="records && props.checkboxes.length" class="w-full overflow-x-auto">
25
- <VisionTable
26
- :checkbox="props.checkboxes"
27
- :records="records"
28
- :index="0"
29
- :meta="props.meta"
30
- :images="images"
31
- :tableHeaders="tableHeaders"
32
- :tableColumns="tableColumns"
33
- :customFieldNames="customFieldNames"
34
- :tableColumnsIndexes="tableColumnsIndexes"
35
- :selected="selected"
36
- :isAiResponseReceivedAnalize="isAiResponseReceivedAnalize"
37
- :isAiResponseReceivedImage="isAiResponseReceivedImage"
38
- :primaryKey="primaryKey"
39
- :openGenerationCarousel="openGenerationCarousel"
40
- @error="handleTableError"
41
- />
42
- </div>
43
- <div class="flex w-full flex-col md:flex-row items-stretch md:items-end justify-end gap-3 md:gap-4">
44
- <div class="h-full text-red-600 font-semibold flex items-center justify-center md:mb-2">
45
- <p v-if="isError === true">{{ errorMessage }}</p>
46
- </div>
47
- <button type="button" class="w-full md:w-auto 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"
48
- @click="closeDialog"
49
- >
50
- {{'Cancel'}}
51
- </button>
52
- <Button
53
- class="w-full md:w-64"
54
- @click="saveData"
55
- :disabled="isLoading || checkedCount < 1 || isCriticalError"
56
- :loader="isLoading"
57
- >
58
- {{ checkedCount > 1 ? 'Save fields' : 'Save field' }}
59
- </Button>
60
- </div>
61
- </div>
8
+ <Dialog
9
+ ref="confirmDialog"
10
+ header="Bulk AI Flow"
11
+ class="!max-w-full w-full lg:w-[1600px] !lg:max-w-[1600px]"
12
+ :buttons="[
13
+ { label: checkedCount > 1 ? 'Save fields' : 'Save field', options: { disabled: isLoading || checkedCount < 1 || isCriticalError || isFetchingRecords, loader: isLoading, class: 'w-fit sm:w-40' }, onclick: (dialog) => { saveData(); dialog.hide(); } },
14
+ { label: 'Cancel', onclick: (dialog) => dialog.hide() },
15
+ ]"
16
+ >
17
+ <div class="bulk-vision-table flex flex-col items-center max-w-[1560px] md:max-h-[90vh] gap-3 md:gap-4 w-full h-full overflow-y-auto">
18
+ <div v-if="records && props.checkboxes.length" class="w-full overflow-x-auto">
19
+ <VisionTable
20
+ :checkbox="props.checkboxes"
21
+ :records="records"
22
+ :meta="props.meta"
23
+ :images="images"
24
+ :tableHeaders="tableHeaders"
25
+ :tableColumns="tableColumns"
26
+ :customFieldNames="customFieldNames"
27
+ :tableColumnsIndexes="tableColumnsIndexes"
28
+ :selected="selected"
29
+ :isAiResponseReceivedAnalize="isAiResponseReceivedAnalize"
30
+ :isAiResponseReceivedImage="isAiResponseReceivedImage"
31
+ :primaryKey="primaryKey"
32
+ :openGenerationCarousel="openGenerationCarousel"
33
+ @error="handleTableError"
34
+ :carouselSaveImages="carouselSaveImages"
35
+ :carouselImageIndex="carouselImageIndex"
36
+ />
37
+ </div>
38
+ <div class="text-red-600 flex items-center w-full">
39
+ <p v-if="isError === true">{{ errorMessage }}</p>
62
40
  </div>
63
41
  </div>
64
42
  </Dialog>
@@ -68,13 +46,11 @@
68
46
  import { callAdminForthApi } from '@/utils';
69
47
  import { Ref, ref, watch } from 'vue'
70
48
  import { Dialog, Button } from '@/afcl';
71
- import VisionTable from './visionTable.vue'
49
+ import VisionTable from './VisionTable.vue'
72
50
  import adminforth from '@/adminforth';
73
51
  import { useI18n } from 'vue-i18n';
74
- import { useRoute } from 'vue-router';
75
52
  import { AdminUser, type AdminForthResourceCommon } from '@/types';
76
53
 
77
- const route = useRoute();
78
54
  const { t } = useI18n();
79
55
 
80
56
  const props = defineProps<{
@@ -99,11 +75,14 @@ const tableColumns = ref([]);
99
75
  const tableColumnsIndexes = ref([]);
100
76
  const customFieldNames = ref([]);
101
77
  const selected = ref<any[]>([]);
78
+ const carouselSaveImages = ref<any[]>([]);
79
+ const carouselImageIndex = ref<any[]>([]);
102
80
  const isAiResponseReceivedAnalize = ref([]);
103
81
  const isAiResponseReceivedImage = ref([]);
104
82
  const primaryKey = props.meta.primaryKey;
105
83
  const openGenerationCarousel = ref([]);
106
84
  const isLoading = ref(false);
85
+ const isFetchingRecords = ref(false);
107
86
  const isError = ref(false);
108
87
  const isCriticalError = ref(false);
109
88
  const isImageGenerationError = ref(false);
@@ -128,7 +107,7 @@ const openDialog = async () => {
128
107
  return acc;
129
108
  },{[primaryKey]: records.value[i][primaryKey]} as Record<string, boolean>);
130
109
  }
131
- isLoading.value = true;
110
+ isFetchingRecords.value = true;
132
111
  const tasks = [];
133
112
  if (props.meta.isFieldsForAnalizeFromImages) {
134
113
  tasks.push(runAiAction({
@@ -152,7 +131,12 @@ const openDialog = async () => {
152
131
  }));
153
132
  }
154
133
  await Promise.all(tasks);
155
- isLoading.value = false;
134
+
135
+ if (props.meta.isImageGeneration) {
136
+ fillCarouselSaveImages();
137
+ }
138
+
139
+ isFetchingRecords.value = false;
156
140
  }
157
141
 
158
142
  watch(selected, (val) => {
@@ -160,22 +144,22 @@ watch(selected, (val) => {
160
144
  checkedCount.value = val.filter(item => item.isChecked === true).length;
161
145
  }, { deep: true });
162
146
 
163
- const closeDialog = () => {
164
- confirmDialog.value.close();
165
- isAiResponseReceivedAnalize.value = [];
166
- isAiResponseReceivedImage.value = [];
167
-
168
- records.value = [];
169
- images.value = [];
170
- selected.value = [];
171
- tableColumns.value = [];
172
- tableColumnsIndexes.value = [];
173
- isError.value = false;
174
- isCriticalError.value = false;
175
- isImageGenerationError.value = false;
176
- errorMessage.value = '';
147
+ function fillCarouselSaveImages() {
148
+ for (const item of selected.value) {
149
+ const tempItem: any = {};
150
+ const tempItemIndex: any = {};
151
+ for (const [key, value] of Object.entries(item)) {
152
+ if (props.meta.outputImageFields?.includes(key)) {
153
+ tempItem[key] = [value];
154
+ tempItemIndex[key] = 0;
155
+ }
156
+ }
157
+ carouselSaveImages.value.push(tempItem);
158
+ carouselImageIndex.value.push(tempItemIndex);
159
+ }
177
160
  }
178
161
 
162
+
179
163
  function formatLabel(str) {
180
164
  return str
181
165
  .split('_')
@@ -270,7 +254,6 @@ async function getRecords() {
270
254
  console.error('Failed to get records:', error);
271
255
  isError.value = true;
272
256
  errorMessage.value = `Failed to fetch records. Please, try to re-run the action.`;
273
- // Handle error appropriately
274
257
  }
275
258
  }
276
259
 
@@ -288,7 +271,6 @@ async function getImages() {
288
271
  console.error('Failed to get images:', error);
289
272
  isError.value = true;
290
273
  errorMessage.value = `Failed to fetch images. Please, try to re-run the action.`;
291
- // Handle error appropriately
292
274
  }
293
275
  }
294
276
 
@@ -1,9 +1,8 @@
1
1
  <template>
2
- <div>
3
2
  <Table
4
3
  :columns="tableHeaders"
5
4
  :data="tableColumns"
6
- :pageSize="8"
5
+ :pageSize="6"
7
6
  >
8
7
  <!-- HEADER TEMPLATE -->
9
8
  <template #header:checkboxes="{ item }">
@@ -28,18 +27,22 @@
28
27
  @click="zoomImage(image)"
29
28
  />
30
29
  </div>
30
+ </div>
31
+ <transition name="fade">
31
32
  <div
32
33
  v-if="zoomedImage"
33
34
  class="fixed inset-0 z-50 flex items-center justify-center bg-black/60"
34
35
  @click.self="closeZoom"
35
36
  >
36
- <img
37
- :src="zoomedImage"
38
- ref="zoomedImg"
39
- class="max-w-full max-h-full rounded-lg object-contain cursor-grab z-75"
40
- />
37
+ <transition name="zoom">
38
+ <img
39
+ v-if="zoomedImage"
40
+ :src="zoomedImage"
41
+ class="max-w-full max-h-full rounded-lg object-contain cursor-grab z-75"
42
+ />
43
+ </transition>
41
44
  </div>
42
- </div>
45
+ </transition>
43
46
  </div>
44
47
  </template>
45
48
  <!-- CUSTOM FIELD TEMPLATES -->
@@ -73,7 +76,7 @@
73
76
  <Input
74
77
  type="number"
75
78
  v-model="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
76
- class="w-full "
79
+ class="w-full min-w-[80px]"
77
80
  :fullWidth="true"
78
81
  />
79
82
  </div>
@@ -91,13 +94,15 @@
91
94
  <div>
92
95
  <GenerationCarousel
93
96
  v-if="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
94
- :images="selected[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
97
+ :images="carouselSaveImages[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
95
98
  :recordId="item[primaryKey]"
96
99
  :meta="props.meta"
97
100
  :fieldName="n"
101
+ :carouselImageIndex="carouselImageIndex[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n]"
98
102
  @error="handleError"
99
103
  @close="openGenerationCarousel[tableColumnsIndexes.findIndex(el => el[primaryKey] === item[primaryKey])][n] = false"
100
104
  @selectImage="updateSelectedImage"
105
+ @updateCarouselIndex="updateActiveIndex"
101
106
  />
102
107
  </div>
103
108
  </div>
@@ -112,14 +117,12 @@
112
117
  </div>
113
118
  </template>
114
119
  </Table>
115
- </div>
116
120
  </template>
117
121
 
118
122
  <script lang="ts" setup>
119
- import { ref, nextTick, watch } from 'vue'
120
- import mediumZoom from 'medium-zoom'
123
+ import { ref } from 'vue'
121
124
  import { Select, Input, Textarea, Table, Checkbox, Skeleton, Toggle } from '@/afcl'
122
- import GenerationCarousel from './imageGenerationCarousel.vue'
125
+ import GenerationCarousel from './ImageGenerationCarousel.vue'
123
126
 
124
127
  const props = defineProps<{
125
128
  meta: any,
@@ -134,12 +137,13 @@ const props = defineProps<{
134
137
  openGenerationCarousel: any
135
138
  isError: boolean,
136
139
  errorMessage: string
140
+ carouselSaveImages: any[]
141
+ carouselImageIndex: any[]
137
142
  }>();
138
143
  const emit = defineEmits(['error']);
139
144
 
140
145
 
141
146
  const zoomedImage = ref(null)
142
- const zoomedImg = ref(null)
143
147
 
144
148
 
145
149
  function zoomImage(img) {
@@ -150,17 +154,6 @@ function closeZoom() {
150
154
  zoomedImage.value = null
151
155
  }
152
156
 
153
- watch(zoomedImage, async (val) => {
154
- await nextTick()
155
- if (val && zoomedImg.value) {
156
- mediumZoom(zoomedImg.value, {
157
- margin: 24,
158
- background: 'rgba(0, 0, 0, 0.9)',
159
- scrollOffset: 150
160
- }).show()
161
- }
162
- })
163
-
164
157
  function isInColumnEnum(key: string): boolean {
165
158
  const colEnum = props.meta.columnEnums?.find(c => c.name === key);
166
159
  if (!colEnum) {
@@ -193,4 +186,25 @@ function handleError({ isError, errorMessage }) {
193
186
  });
194
187
  }
195
188
 
196
- </script>
189
+ function updateActiveIndex(newIndex: number, id: any, fieldName: string) {
190
+ props.carouselImageIndex[props.tableColumnsIndexes.findIndex(el => el[props.primaryKey] === id)][fieldName] = newIndex;
191
+ }
192
+
193
+ </script>
194
+
195
+ <style scoped>
196
+ .fade-enter-active, .fade-leave-active {
197
+ transition: opacity 0.2s ease;
198
+ }
199
+ .fade-enter-from, .fade-leave-to {
200
+ opacity: 0;
201
+ }
202
+
203
+ .zoom-enter-active, .zoom-leave-active {
204
+ transition: transform 0.2s ease, opacity 0.2s ease;
205
+ }
206
+ .zoom-enter-from, .zoom-leave-to {
207
+ transform: scale(0.95);
208
+ opacity: 0;
209
+ }
210
+ </style>
@@ -1,8 +1,8 @@
1
1
 
2
2
  <template>
3
3
  <!-- Main modal -->
4
- <div tabindex="-1" class="overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 bottom-0 z-10 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-black/50 dark:bg-gray-900 dark:bg-opacity-50">
5
- <div class="relative p-4 w-10/12 max-w-full max-h-full ">
4
+ <div tabindex="-1" class="fixed inset-0 z-10 flex justify-center items-center dark:bg-gray-900/50 overflow-y-auto">
5
+ <div class="relative p-4 w-full max-w-[1600px] max-h-[90vh] ">
6
6
  <!-- Modal content -->
7
7
  <div class="relative bg-white rounded-lg shadow-xl dark:bg-gray-700">
8
8
  <!-- Modal header -->
@@ -95,20 +95,20 @@
95
95
  </div>
96
96
 
97
97
 
98
- <div id="gallery" class="relative w-full" data-carousel="static">
98
+ <div id="gallery" class="relative w-full min-w-0" data-carousel="static">
99
99
  <!-- Carousel wrapper -->
100
100
  <div class="relative h-56 overflow-hidden rounded-lg md:h-[calc(100vh-400px)]">
101
101
  <!-- Item 1 -->
102
102
  <div
103
103
  v-for="(img, index) in images"
104
104
  :key="index"
105
+ class="flex items-center justify-center w-full h-full"
105
106
  :class="[
106
- index === 0 ? 'block' : 'hidden',
107
- 'duration-700 ease-in-out'
107
+ index === 0 ? 'block' : 'hidden'
108
108
  ]"
109
109
  data-carousel-item
110
110
  >
111
- <img :src="img" class="absolute block max-w-full max-h-full -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 object-cover"
111
+ <img :src="img" class="max-w-full max-h-full object-contain"
112
112
  :alt="`Generated image ${index + 1}`"
113
113
  />
114
114
  </div>
@@ -175,10 +175,6 @@
175
175
  </div>
176
176
  </div>
177
177
  </div>
178
-
179
-
180
-
181
-
182
178
  </template>
183
179
 
184
180
  <script setup lang="ts">
@@ -193,8 +189,8 @@ import { ProgressBar } from '@/afcl';
193
189
  const { t: $t } = useI18n();
194
190
 
195
191
  const prompt = ref('');
196
- const emit = defineEmits(['close', 'selectImage', 'error']);
197
- const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage']);
192
+ const emit = defineEmits(['close', 'selectImage', 'error', 'updateCarouselIndex']);
193
+ const props = defineProps(['meta', 'record', 'images', 'recordId', 'prompt', 'fieldName', 'isError', 'errorMessage', 'carouselImageIndex']);
198
194
  const images = ref([]);
199
195
  const loading = ref(false);
200
196
  const attachmentFiles = ref<string[]>([])
@@ -208,12 +204,14 @@ function minifyField(field: string): string {
208
204
 
209
205
  const caurosel = ref(null);
210
206
  onMounted(async () => {
211
- images.value.push((props.images || []));
207
+ for (const img of props.images || []) {
208
+ images.value.push(img);
209
+ }
212
210
  const temp = await getGenerationPrompt() || '';
213
211
  prompt.value = temp[props.fieldName];
214
212
  await nextTick();
215
213
 
216
- const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
214
+ const currentIndex = props.carouselImageIndex || 0;
217
215
  caurosel.value = new Carousel(
218
216
  document.getElementById('gallery'),
219
217
  images.value.map((img, index) => {
@@ -298,6 +296,12 @@ async function confirmImage() {
298
296
  const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
299
297
  const img = images.value[currentIndex];
300
298
 
299
+ props.images.splice(0, props.images.length);
300
+ for (const i of images.value) {
301
+ props.images.push(i);
302
+ }
303
+
304
+ emit('updateCarouselIndex', currentIndex, props.recordId, props.fieldName);
301
305
  emit('selectImage', img, props.recordId, props.fieldName);
302
306
  emit('close');
303
307