FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

images generation plugin · devforth/adminforth@6278a2a · GitHub

Repository navigation

Commit 6278a2a

Browse files
committed
images generation plugin
1 parent fcf79bd commit 6278a2a

12 files changed

Lines changed: 467 additions & 85 deletions

File tree

‎Changelog.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414

1515
- `timeFormat` fix for pure time stamps
1616
- page input in list table now autogrows in large values
17+
- plugin was not able to use more then one package in `custom` folder package.json
18+
- express server proxy now has `blobStream()` method which returns stream for blob data e.g. for piping
1719

1820

1921
## [1.3.48] - 2024-10-02

‎adminforth/modules/codeInjector.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -538,13 +538,12 @@ class CodeInjector implements ICodeInjector {
538538
}
539539

540540
await this.runNpmShell({command: 'ci', cwd: CodeInjector.SPA_TMP_PATH});
541-
542541

543542
if (iconPackageNames.length) {
544543
const npmInstallCommand = `install ${[
545544
...iconPackageNames,
546545
...usersPackages,
547-
...pluginPackages.map(({ packages }) => packages)
546+
...pluginPackages.map(({ packages }) => packages.join(' ')),
548547
].join(' ')}`;
549548
await this.runNpmShell({command: npmInstallCommand, cwd: CodeInjector.SPA_TMP_PATH});
550549
}

‎adminforth/plugins/upload/custom/imageGenerator.vue‎

Lines changed: 76 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<!-- Modal content -->
77
<div class="relative bg-white rounded-lg shadow-xl dark:bg-gray-700">
88
<!-- Modal header -->
9-
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
9+
<div class="flex items-center justify-between p-3 md:p-4 border-b rounded-t dark:border-gray-600">
1010
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
1111
Generate image with AI
1212
</h3>
@@ -21,10 +21,10 @@
2121
</div>
2222
<!-- Modal body -->
2323
<div class="p-4 md:p-5 space-y-4">
24-
25-
<label for="message" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Your prompt</label>
26-
<textarea id="message" rows="4" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
27-
placeholder="Prompt which will be passed to AI network" v-model="prompt"></textarea>
24+
<textarea id="message" rows="3" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
25+
placeholder="Prompt which will be passed to AI network" v-model="prompt"
26+
title="Prompt which will be passed to AI network"
27+
></textarea>
2828

2929
<div class="flex items-center justify-center w-full relative">
3030
<div
@@ -39,7 +39,7 @@
3939

4040
<div id="gallery" class="relative w-full" data-carousel="static">
4141
<!-- Carousel wrapper -->
42-
<div class="relative h-56 overflow-hidden rounded-lg md:h-96">
42+
<div class="relative h-56 overflow-hidden rounded-lg md:h-72">
4343
<!-- Item 1 -->
4444
<div v-for="(img, index) in images" :key="index" class="hidden duration-700 ease-in-out" data-carousel-item>
4545
<img :src="img" class="absolute block max-w-full h-auto -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2" alt="">
@@ -48,12 +48,10 @@
4848
<div v-if="images.length === 0" class="flex items-center justify-center w-full h-full">
4949

5050
<button @click="generateImages" type="button" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4
51-
focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center
52-
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 ms-2">Generate images</button>
51+
focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center
52+
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 ms-2">Generate images</button>
5353

5454
</div>
55-
56-
5755

5856
</div>
5957
<!-- Slider controls -->
@@ -84,9 +82,15 @@
8482
</div>
8583
<!-- Modal footer -->
8684
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
87-
<button type="button" 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 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Use image</button>
85+
<button type="button" @click="confirmImage"
86+
:disabled="loading"
87+
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
88+
dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800
89+
disabled:opacity-50 disabled:cursor-not-allowed"
90+
>Use image</button>
8891
<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"
89-
@click="emit('close')">Cancel</button>
92+
@click="emit('close')"
93+
>Cancel</button>
9094
</div>
9195
</div>
9296
</div>
@@ -100,34 +104,36 @@
100104
<script setup lang="ts">
101105
102106
import { ref, onMounted } from 'vue'
103-
import { Carousel, initFlowbite } from 'flowbite';
104-
import internal from 'stream';
107+
import { Carousel } from 'flowbite';
108+
import { callAdminForthApi } from '@/utils';
105109
106110
const prompt = ref('');
107-
const emit = defineEmits(['close']);
111+
const emit = defineEmits(['close', 'uploadImage']);
108112
const props = defineProps(['meta', 'record']);
109113
const images = ref([]);
110114
const loading = ref(false);
111115
112-
function minifyRecord(rec: object) {
113-
// for each key make it string and make sure each field has not more then 100 characters
114-
// if more add ... at the end
115-
return Object.keys(rec).reduce((acc, key) => {
116-
if (!rec[key]) return acc;
117-
acc[key] = rec[key].toString().slice(0, 100)
118-
if (rec[key].length > 100) {
119-
acc[key] += '...'
120-
}
121-
return acc
122-
}, {})
116+
function minifyField(field: string): string {
117+
if (field.length > 100) {
118+
return field.slice(0, 100) + '...';
119+
}
120+
return field;
123121
}
124122
125123
const caurosel = ref(null);
126124
onMounted(() => {
127125
// Initialize carousel
128-
prompt.value = `Generate image for field "${props.meta.pathColumnLabel}" in ${props.meta.resourceLabel}.${
129-
Object.values(props.record || {}).length ? 'Additional context: ' + JSON.stringify(minifyRecord(props.record)) : ''
130-
}`
126+
let additionalContext = null;
127+
if (props.meta.fieldsForContext) {
128+
additionalContext = props.meta.fieldsForContext.filter((field: string) => props.record[field]).map((field: string) => {
129+
return `${field}: ${minifyField(props.record[field])}`;
130+
}).join('\n');
131+
}
132+
133+
prompt.value = `Generate image for field "${props.meta.pathColumnLabel}" in ${props.meta.resourceLabel}. No text should be on image.`
134+
if (additionalContext) {
135+
prompt.value += ` ${additionalContext}`;
136+
}
131137
132138
})
133139
@@ -145,30 +151,53 @@ async function slide(direction: number) {
145151
}
146152
}
147153
148-
async function generateImages() {
154+
async function confirmImage() {
149155
loading.value = true;
156+
150157
const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
158+
const img = images.value[currentIndex];
159+
// read url to base64 and send it to the parent component
160+
const imgBlob = await fetch(
161+
`/adminapi/v1/plugin/${props.meta.pluginInstanceId}/cors-proxy?url=${encodeURIComponent(img)}`
162+
).then(res => { return res.blob() });
151163
152-
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000 * 2));
153-
if (!images.value.length) {
154-
images.value = [
155-
'https://flowbite.s3.amazonaws.com/docs/gallery/square/image-1.jpg',
156-
'https://flowbite.s3.amazonaws.com/docs/gallery/square/image-2.jpg',
157-
'https://flowbite.s3.amazonaws.com/docs/gallery/square/image-3.jpg',
158-
];
159-
} else {
160-
images.value = [
161-
...images.value,
162-
'https://flowbite.s3.amazonaws.com/docs/gallery/square/image-4.jpg',
163-
'https://flowbite.s3.amazonaws.com/docs/gallery/square/image-5.jpg',
164-
];
164+
emit('uploadImage', imgBlob);
165+
emit('close');
166+
167+
loading.value = false;
168+
}
169+
170+
async function generateImages() {
171+
loading.value = true;
172+
const currentIndex = caurosel.value?.getActiveItem()?.position || 0;
173+
const resp = await callAdminForthApi({
174+
path: `/plugin/${props.meta.pluginInstanceId}/generate_images`,
175+
method: 'POST',
176+
body: {
177+
prompt: prompt.value,
178+
},
179+
});
180+
181+
if (resp.error) {
182+
window.adminforth.alert({
183+
message: `error: ${JSON.stringify(resp.error)}`,
184+
variant: 'danger',
185+
timeout: 15,
186+
});
187+
return;
165188
}
189+
190+
images.value = [
191+
...images.value,
192+
...resp.images.map(im => im.data[0].url),
193+
];
194+
195+
// images.value = [
196+
// 'https://via.placeholder.com/600x400?text=Image+1',
197+
// 'https://via.placeholder.com/600x400?text=Image+2',
198+
// ];
166199
await new Promise(resolve => setTimeout(resolve, 0));
167200
168-
console.log(images.value.map((img, index) => ({
169-
el: document.getElementById('gallery').querySelector(`[data-carousel-item]:nth-child(${index + 1})`),
170-
position: index,
171-
})))
172201
caurosel.value = new Carousel(
173202
document.getElementById('gallery'),
174203
images.value.map((img, index) => ({

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL