finished the upgrade to filament 4. completely revamped the frontend with codex, now it looks great!

This commit is contained in:
2025-11-13 17:48:49 +01:00
parent b311188bc1
commit cf41055cbd
3 changed files with 31 additions and 41 deletions

View File

@@ -50,9 +50,8 @@
<p class="text-sm font-normal text-slate-500 dark:text-slate-400">Lass die KI dein Motiv verzaubern.</p> <p class="text-sm font-normal text-slate-500 dark:text-slate-400">Lass die KI dein Motiv verzaubern.</p>
</div> </div>
<span class="flex h-12 w-12 items-center justify-center rounded-full bg-white/60 text-slate-900 shadow-md dark:bg-slate-800/70 dark:text-white"> <span class="flex h-12 w-12 items-center justify-center rounded-full bg-white/60 text-slate-900 shadow-md dark:bg-slate-800/70 dark:text-white">
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"> <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.813 15.904a2.25 2.25 0 0 1 0-3.808l.99-.572a1.125 1.125 0 0 0 0-1.948l-.99-.572a2.25 2.25 0 0 1 0-3.808l5.25-3.038A2.25 2.25 0 0 1 17.625 3v18a2.25 2.25 0 0 1-3.563 1.844l-4.249-2.457Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M12 3v3m0 12v3m9-9h-3M6 12H3m13.5-6 1.5 1.5M7.5 18 6 19.5M18 7.5 19.5 6M6 4.5 4.5 6m9.75 3.75-2.122 2.122a2.25 2.25 0 0 0 0 3.182l2.122 2.121a2.25 2.25 0 0 0 3.182 0l2.121-2.121a2.25 2.25 0 0 0 0-3.182l-2.121-2.122a2.25 2.25 0 0 0-3.182 0Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.25a.75.75 0 0 1 .75.75v12a.75.75 0 0 1-1.5 0v-12a.75.75 0 0 1 .75-.75Zm13.5 0a.75.75 0 0 1 .75.75v12a.75.75 0 0 1-1.5 0v-12a.75.75 0 0 1 .75-.75Z" />
</svg> </svg>
</span> </span>
</button> </button>

View File

@@ -14,7 +14,7 @@
</button> </button>
</div> </div>
<div ref="stylesContainer" class="flex-1 space-y-3 overflow-y-auto pr-1"> <div class="flex-1 space-y-3 overflow-y-auto pr-1">
<p v-if="isLoading" class="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-center text-sm text-slate-300"> <p v-if="isLoading" class="rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-center text-sm text-slate-300">
Stile werden geladen Stile werden geladen
</p> </p>
@@ -31,7 +31,8 @@
> >
<img <img
v-if="style.preview_image" v-if="style.preview_image"
:data-src="'/storage/' + style.preview_image" :src="buildPreviewPath(style.preview_image)"
loading="lazy"
class="h-24 w-24 flex-none rounded-2xl object-cover opacity-90 transition group-hover:opacity-100" class="h-24 w-24 flex-none rounded-2xl object-cover opacity-90 transition group-hover:opacity-100"
alt="Style preview" alt="Style preview"
/> />
@@ -53,13 +54,11 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { nextTick, onMounted, onUnmounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
const styles = ref([]); const styles = ref([]);
const isLoading = ref(true); const isLoading = ref(true);
const loadError = ref(null); const loadError = ref(null);
const stylesContainer = ref(null);
let observer = null;
const props = defineProps({ const props = defineProps({
image_id: { image_id: {
@@ -70,31 +69,6 @@ const props = defineProps({
const emits = defineEmits(['styleSelected', 'close']); const emits = defineEmits(['styleSelected', 'close']);
const hydrateObserver = () => {
if (!stylesContainer.value) {
return;
}
if (observer) {
observer.disconnect();
}
observer = new IntersectionObserver(
(entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
obs.unobserve(img);
}
});
},
{ root: stylesContainer.value }
);
stylesContainer.value.querySelectorAll('img[data-src]').forEach((img) => observer.observe(img));
};
const fetchStyles = () => { const fetchStyles = () => {
isLoading.value = true; isLoading.value = true;
loadError.value = null; loadError.value = null;
@@ -102,7 +76,6 @@ const fetchStyles = () => {
.get('/api/styles') .get('/api/styles')
.then((response) => { .then((response) => {
styles.value = response.data; styles.value = response.data;
nextTick(hydrateObserver);
}) })
.catch((error) => { .catch((error) => {
console.error('Error fetching styles:', error); console.error('Error fetching styles:', error);
@@ -117,13 +90,9 @@ const selectStyle = (style) => {
emits('styleSelected', style, props.image_id); emits('styleSelected', style, props.image_id);
}; };
const buildPreviewPath = (preview) => `/storage/${preview}`;
onMounted(() => { onMounted(() => {
fetchStyles(); fetchStyles();
}); });
onUnmounted(() => {
if (observer) {
observer.disconnect();
}
});
</script> </script>

View File

@@ -163,6 +163,24 @@ let refreshTimer = null;
const getImageIdentifier = (image) => image?.id ?? image?.image_id ?? null; const getImageIdentifier = (image) => image?.id ?? image?.image_id ?? null;
const extractFilenameFromHeader = (disposition) => {
if (!disposition) {
return null;
}
const filenameMatch = disposition.match(/filename\*=UTF-8''([^;]+)|filename="?([^";]+)"?/i);
if (!filenameMatch) {
return null;
}
const encodedName = filenameMatch[1] || filenameMatch[2];
try {
return decodeURIComponent(encodedName);
} catch (error) {
return encodedName;
}
};
watch( watch(
() => props.images, () => props.images,
(newImages) => { (newImages) => {
@@ -306,8 +324,12 @@ const downloadImage = (imageParam = null) => {
const blob = new Blob([response.data]); const blob = new Blob([response.data]);
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
const disposition = response.headers?.['content-disposition'];
const filenameFromHeader = extractFilenameFromHeader(disposition);
const fallbackName = image.name || `stylegallery_${new Date().toISOString().replace(/[:.]/g, '-')}`;
link.href = url; link.href = url;
link.download = image.name || 'image'; link.download = filenameFromHeader || fallbackName;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);