1111 {{ $t('Generate image with AI') }}
1212 </h3 >
1313 <button type =" button"
14- @click =" emit('close')"
14+ @click =" () => {stopGeneration = true; emit('close')} "
1515 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" >
1616 <svg class =" w-3 h-3" aria-hidden =" true" xmlns =" http://www.w3.org/2000/svg" fill =" none" viewBox =" 0 0 14 14" >
1717 <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" />
161161 disabled:opacity-50 disabled:cursor-not-allowed"
162162 >{{ $t('Use image') }}</button >
163163 <button type =" button" class =" py-2.5 px-5 ms-3 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"
164- @click =" emit('close')"
164+ @click =" () => {stopGeneration = true; emit('close')} "
165165 >{{ $t('Cancel') }}</button >
166166 </div >
167167 </div >
@@ -181,6 +181,7 @@ import { callAdminForthApi } from '@/utils';
181181import { useI18n } from ' vue-i18n' ;
182182import adminforth from ' @/adminforth' ;
183183import { ProgressBar } from ' @/afcl' ;
184+ import * as Handlebars from ' handlebars' ;
184185
185186const { t : $t } = useI18n ();
186187
@@ -190,6 +191,7 @@ const props = defineProps(['meta', 'record']);
190191const images = ref ([]);
191192const loading = ref (false );
192193const attachmentFiles = ref <string []>([])
194+ const stopGeneration = ref (false );
193195
194196function minifyField(field : string ): string {
195197 if (field .length > 100 ) {
@@ -213,28 +215,9 @@ onMounted(async () => {
213215 }
214216 // iterate over all variables in template and replace them with their values from props.record[field].
215217 // if field is not present in props.record[field] then replace it with empty string and drop warning
216- const regex = / {{(. *? )}}/ g ;
217- const matches = template .match (regex );
218- if (matches ) {
219- matches .forEach ((match ) => {
220- const field = match .replace (/ {{| }}/ g , ' ' ).trim ();
221- if (field in context ) {
222- return ;
223- } else if (field in props .record ) {
224- context [field ] = minifyField (props .record [field ]);
225- } else {
226- adminforth .alert ({
227- message: $t (' Field {{field}} defined in template but not found in record' , { field }),
228- variant: ' warning' ,
229- timeout: 15 ,
230- });
231- }
232- });
233- }
234-
235- prompt .value = template .replace (regex , (_ , field ) => {
236- return context [field .trim ()] || ' ' ;
237- });
218+ const tpl = Handlebars .compile (template );
219+ const compiledTemplate = tpl (props .record );
220+ prompt .value = compiledTemplate ;
238221
239222 const recordId = props .record [props .meta .recorPkFieldName ];
240223 if (! recordId ) return ;
@@ -248,7 +231,6 @@ onMounted(async () => {
248231
249232 if (resp ?.files ?.length ) {
250233 attachmentFiles .value = resp .files ;
251- console .log (' attachmentFiles' , attachmentFiles .value );
252234 }
253235 } catch (err ) {
254236 console .error (' Failed to fetch attachment files' , err );
@@ -337,7 +319,7 @@ async function generateImages() {
337319 let error = null ;
338320 try {
339321 resp = await callAdminForthApi ({
340- path: ` /plugin/${props .meta .pluginInstanceId }/generate_images ` ,
322+ path: ` /plugin/${props .meta .pluginInstanceId }/create-image-generation-job ` ,
341323 method: ' POST' ,
342324 body: {
343325 prompt: prompt .value ,
@@ -346,16 +328,13 @@ async function generateImages() {
346328 });
347329 } catch (e ) {
348330 console .error (e );
349- } finally {
350- clearInterval (ticker );
351- loadingTimer .value = null ;
352- loading .value = false ;
353331 }
332+
354333 if (resp ?.error ) {
355334 error = resp .error ;
356335 }
357336 if (! resp ) {
358- error = $t (' Error generating images, something went wrong ' );
337+ error = $t (' Error creating image generation job ' );
359338 }
360339
361340 if (error ) {
@@ -371,11 +350,55 @@ async function generateImages() {
371350 return ;
372351 }
373352
353+ const jobId = resp .jobId ;
354+ let jobStatus = null ;
355+ let jobResponse = null ;
356+ do {
357+ jobResponse = await callAdminForthApi ({
358+ path: ` /plugin/${props .meta .pluginInstanceId }/get-image-generation-job-status ` ,
359+ method: ' POST' ,
360+ body: { jobId },
361+ });
362+ if (jobResponse !== null ) {
363+ if (jobResponse ?.error ) {
364+ error = jobResponse .error ;
365+ break ;
366+ };
367+ jobStatus = jobResponse ?.job ?.status ;
368+ if (jobStatus === ' failed' ) {
369+ error = jobResponse ?.job ?.error || $t (' Image generation job failed' );
370+ }
371+ if (jobStatus === ' timeout' ) {
372+ error = jobResponse ?.job ?.error || $t (' Image generation job timeout' );
373+ }
374+ }
375+ await new Promise ((resolve ) => setTimeout (resolve , 2000 ));
376+ } while ((jobStatus === ' in_progress' || jobStatus === null ) && ! stopGeneration .value );
377+
378+ if (error ) {
379+ adminforth .alert ({
380+ message: error ,
381+ variant: ' danger' ,
382+ timeout: ' unlimited' ,
383+ });
384+ clearInterval (ticker );
385+ loadingTimer .value = null ;
386+ loading .value = false ;
387+ return ;
388+ }
389+
390+ const respImages = jobResponse ?.job ?.images || [];
391+
374392 images .value = [
375393 ... images .value ,
376- ... resp . images ,
394+ ... respImages ,
377395 ];
378396
397+ clearInterval (ticker );
398+ loadingTimer .value = null ;
399+ loading .value = false ;
400+
401+
379402 // images.value = [
380403 // 'https://via.placeholder.com/600x400?text=Image+1',
381404 // 'https://via.placeholder.com/600x400?text=Image+2',
@@ -386,7 +409,6 @@ async function generateImages() {
386409 caurosel .value = new Carousel (
387410 document .getElementById (' gallery' ),
388411 images .value .map ((img , index ) => {
389- console .log (' mapping image' , img , index );
390412 return {
391413 image: img ,
392414 el: document .getElementById (' gallery' ).querySelector (` [data-carousel-item]:nth-child(${index + 1 }) ` ),
0 commit comments