the RunwareAI Plugin is working now
This commit is contained in:
30
resources/js/Components/LoadingSpinner.vue
Normal file
30
resources/js/Components/LoadingSpinner.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
|
||||
<div class="bg-white p-6 rounded-lg shadow-lg text-center flex flex-col items-center">
|
||||
<div class="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-12 w-12 mb-4"></div>
|
||||
<p class="text-gray-700 text-lg">{{ __('loading_spinner.processing_image') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// No script logic needed for a simple spinner
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.loader {
|
||||
border-top-color: #3498db; /* Blue color for the spinner */
|
||||
-webkit-animation: spinner 1.5s linear infinite;
|
||||
animation: spinner 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spinner {
|
||||
0% { -webkit-transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes spinner {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@
|
||||
v-for="style in styles"
|
||||
:key="style.id"
|
||||
class="style-item"
|
||||
@click="$emit('styleSelected', style)"
|
||||
@click="selectStyle(style)"
|
||||
>
|
||||
<img :src="'/storage/' + style.preview_image" :alt="style.title" class="style-thumbnail" />
|
||||
<div class="style-details">
|
||||
@@ -29,6 +29,19 @@ import { ref, onMounted } from 'vue';
|
||||
|
||||
const styles = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
position: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
image_id: {
|
||||
type: Number,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['styleSelected', 'back', 'close']);
|
||||
|
||||
const fetchStyles = () => {
|
||||
axios.get('/api/styles')
|
||||
.then(response => {
|
||||
@@ -39,18 +52,14 @@ const fetchStyles = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const selectStyle = (style) => {
|
||||
console.log('StyleSelector.vue: emitting styleSelected with image_id:', props.image_id);
|
||||
emits('styleSelected', style, props.image_id);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchStyles();
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
position: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['styleSelected', 'back', 'close']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -32,6 +32,8 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const emits = defineEmits(['keep', 'delete']);
|
||||
|
||||
console.log('StyledImageDisplay: image prop:', props.image);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<div class="main-content">
|
||||
<div class="gallery-container">
|
||||
<h1 class="text-2xl font-bold text-center my-4">{{ __('gallery_title') }}</h1>
|
||||
<div class="gallery-container" @touchstart="handleTouchStart" @touchend="handleTouchEnd">
|
||||
<h1 class="text-2xl font-bold text-center my-4">Style Gallery</h1>
|
||||
<GalleryGrid :images="paginatedImages" @imageTapped="showContextMenu" />
|
||||
<Navigation
|
||||
:currentPage="currentPage"
|
||||
@@ -25,6 +25,7 @@
|
||||
<StyleSelector
|
||||
v-if="currentOverlayComponent === 'styleSelector'"
|
||||
:position="contextMenuPosition"
|
||||
:image_id="selectedImage.image_id"
|
||||
@styleSelected="applyStyle"
|
||||
@back="goBackToContextMenu"
|
||||
@close="currentOverlayComponent = null"
|
||||
@@ -39,6 +40,8 @@
|
||||
@keep="keepStyledImage"
|
||||
@delete="deleteStyledImage"
|
||||
/>
|
||||
|
||||
<LoadingSpinner v-if="isLoading" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -48,6 +51,7 @@ import GalleryGrid from '../Components/GalleryGrid.vue';
|
||||
import ImageContextMenu from '../Components/ImageContextMenu.vue';
|
||||
import StyleSelector from '../Components/StyleSelector.vue';
|
||||
import StyledImageDisplay from '../Components/StyledImageDisplay.vue'; // Import the new component
|
||||
import LoadingSpinner from '../Components/LoadingSpinner.vue'; // Import the new component
|
||||
import axios from 'axios';
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
@@ -59,7 +63,11 @@ const contextMenuPosition = ref({ x: 0, y: 0 });
|
||||
const selectedImage = ref(null);
|
||||
const styledImage = ref(null); // To store the newly styled image
|
||||
const errorMessage = ref(null); // New ref for error messages
|
||||
const isLoading = ref(false); // New ref for loading state
|
||||
let fetchInterval = null;
|
||||
let touchStartX = 0;
|
||||
let touchEndX = 0;
|
||||
|
||||
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(images.value.length / imagesPerPage);
|
||||
@@ -108,26 +116,27 @@ const goBackToContextMenu = () => {
|
||||
currentOverlayComponent.value = 'contextMenu';
|
||||
};
|
||||
|
||||
const applyStyle = (style) => {
|
||||
console.log('Applying style:', style.title, 'to image:', selectedImage.value);
|
||||
const applyStyle = (style, imageId) => {
|
||||
console.log('Applying style:', style.title, 'to image:', imageId);
|
||||
currentOverlayComponent.value = null; // Close style selector immediately
|
||||
// You might want to show a loading indicator here
|
||||
isLoading.value = true; // Show loading spinner
|
||||
|
||||
axios.post('/api/images/style-change', {
|
||||
image_id: selectedImage.value.id,
|
||||
image_id: imageId,
|
||||
style_id: style.id,
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Style change request successful:', response.data);
|
||||
// Assuming the response contains the new styled image data
|
||||
styledImage.value = response.data.styled_image; // Adjust based on your API response structure
|
||||
currentOverlayComponent.value = 'styledImageDisplay'; // Show the new component
|
||||
styledImage.value = response.data.styled_image;
|
||||
currentOverlayComponent.value = 'styledImageDisplay';
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error applying style:', error);
|
||||
showError(error.response?.data?.error || 'Failed to apply style.');
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false; // Hide loading spinner
|
||||
});
|
||||
currentOverlayComponent.value = null;
|
||||
};
|
||||
|
||||
const keepStyledImage = (imageToKeep) => {
|
||||
@@ -156,6 +165,26 @@ const nextPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchStart = (event) => {
|
||||
touchStartX = event.touches[0].clientX;
|
||||
};
|
||||
|
||||
const handleTouchEnd = (event) => {
|
||||
touchEndX = event.changedTouches[0].clientX;
|
||||
handleSwipeGesture();
|
||||
};
|
||||
|
||||
const handleSwipeGesture = () => {
|
||||
const swipeThreshold = 50; // Minimum distance for a swipe
|
||||
if (touchEndX < touchStartX - swipeThreshold) {
|
||||
// Swiped left
|
||||
nextPage();
|
||||
} else if (touchEndX > touchStartX + swipeThreshold) {
|
||||
// Swiped right
|
||||
prevPage();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchImages();
|
||||
fetchInterval = setInterval(fetchImages, 5000);
|
||||
|
||||
@@ -18,7 +18,11 @@ createInertiaApp({
|
||||
.mixin({
|
||||
methods: {
|
||||
__: (key, replace = {}) => {
|
||||
let translation = props.initialPage.props.translations[key] || key;
|
||||
let translation = props.initialPage.props.translations[key];
|
||||
|
||||
if (translation === undefined) {
|
||||
translation = key; // Fallback to key if translation not found
|
||||
}
|
||||
|
||||
for (let placeholder in replace) {
|
||||
translation = translation.replace(`:${placeholder}`, replace[placeholder]);
|
||||
|
||||
Reference in New Issue
Block a user