fixed migrations, changed settings to global settings, changed image list to have a "delete all" button instead of "create", fixed printing, added imagick for printing.
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
</svg>
|
||||
Schließen
|
||||
</button>
|
||||
<button @click="$emit('print', image)" class="context-menu-button">
|
||||
<button v-if="settings.show_print_button === '1'" @click="$emit('print', image)" class="context-menu-button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" class="w-8 h-8 inline-block align-middle mr-2"><!--!Font Awesome Free v6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M128 0C92.7 0 64 28.7 64 64l0 96 64 0 0-96 226.7 0L384 93.3l0 66.7 64 0 0-66.7c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0L128 0zM384 352l0 32 0 64-256 0 0-64 0-16 0-16 256 0zm64 32l32 0c17.7 0 32-14.3 32-32l0-96c0-35.3-28.7-64-64-64L64 192c-35.3 0-64 28.7-64 64l0 96c0 17.7 14.3 32 32 32l32 0 0 64c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-64zM432 248a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"/></svg>
|
||||
Drucken
|
||||
</button>
|
||||
@@ -22,12 +22,12 @@
|
||||
Stil ändern
|
||||
</button>
|
||||
</div>
|
||||
<StyleSelector
|
||||
<StyleSelector
|
||||
v-else
|
||||
:image_id="image.image_id"
|
||||
@styleSelected="(style, imageId) => $emit('styleSelected', style, imageId)"
|
||||
@back="showStyleSelectorView = false"
|
||||
@close="$emit('close')"
|
||||
@styleSelected="(style, imageId) => $emit('styleSelected', style, imageId)"
|
||||
@back="showStyleSelectorView = false"
|
||||
@close="$emit('close')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -36,8 +36,12 @@
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { usePage } from '@inertiajs/vue3';
|
||||
import StyleSelector from './StyleSelector.vue';
|
||||
|
||||
const page = usePage();
|
||||
const settings = page.props.settings;
|
||||
|
||||
const props = defineProps({
|
||||
position: {
|
||||
type: Object,
|
||||
@@ -115,4 +119,4 @@ const showStyleSelectorView = ref(false);
|
||||
.context-menu-button:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -19,14 +19,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ImageContextMenu
|
||||
v-if="currentOverlayComponent === 'contextMenu'"
|
||||
:position="contextMenuPosition"
|
||||
:image="selectedImage"
|
||||
@close="currentOverlayComponent = null; selectedImage = null"
|
||||
@print="printImage"
|
||||
@changeStyle="showStyleSelector"
|
||||
@styleSelected="applyStyle"
|
||||
<ImageContextMenu
|
||||
v-if="currentOverlayComponent === 'contextMenu'"
|
||||
:position="contextMenuPosition"
|
||||
:image="selectedImage"
|
||||
@close="currentOverlayComponent = null; selectedImage = null"
|
||||
@print="printImage"
|
||||
@changeStyle="showStyleSelector"
|
||||
@styleSelected="applyStyle"
|
||||
/>
|
||||
|
||||
<StyleSelector
|
||||
v-if="currentOverlayComponent === 'styleSelector'"
|
||||
:image_id="selectedImage.id"
|
||||
@styleSelected="applyStyle"
|
||||
@back="goBackToContextMenu"
|
||||
@close="currentOverlayComponent = null; selectedImage = null"
|
||||
/>
|
||||
|
||||
<div v-if="errorMessage" class="fixed bottom-4 right-4 bg-red-500 text-white p-4 rounded-lg shadow-lg z-50">
|
||||
@@ -87,7 +95,6 @@ const currentOverlayComponent = ref(null); // null, 'contextMenu', 'styleSelecto
|
||||
const contextMenuPosition = ref({ x: 0, y: 0 });
|
||||
const selectedImage = ref(null);
|
||||
const styledImage = ref(null); // To store the newly styled image
|
||||
const processingImageUuid = ref(null); // To store the UUID of the image being processed
|
||||
const processingProgress = ref(0); // To store the progress percentage
|
||||
const errorMessage = ref(null); // New ref for error messages
|
||||
const isLoading = ref(false); // New ref for loading state
|
||||
@@ -168,91 +175,124 @@ const applyStyle = (style, imageId) => {
|
||||
console.log('Applying style:', style.title, 'to image:', imageId);
|
||||
currentOverlayComponent.value = null; // Close style selector immediately
|
||||
isLoading.value = true; // Show loading spinner
|
||||
processingImageUuid.value = selectedImage.value.uuid; // Set the UUID of the image being processed
|
||||
processingProgress.value = 0; // Reset progress
|
||||
|
||||
axios.get('/api/comfyui-url')
|
||||
.then(response => {
|
||||
const comfyUiBaseUrl = response.data.comfyui_url;
|
||||
const wsUrl = `ws://${new URL(comfyUiBaseUrl).host}/ws`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
// Send style change request to backend first
|
||||
axios.post('/api/images/style-change', {
|
||||
image_id: imageId,
|
||||
style_id: style.id,
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Style change request sent:', response.data);
|
||||
// Store the prompt_id and plugin from the backend response
|
||||
const promptId = response.data.prompt_id;
|
||||
const plugin = response.data.plugin;
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket connected to ComfyUI.');
|
||||
// Send prompt to ComfyUI via HTTP, then listen for progress via WS
|
||||
axios.post('/api/images/style-change', {
|
||||
image_id: imageId,
|
||||
style_id: style.id,
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Style change request sent:', response.data);
|
||||
// Store the prompt_id from the backend response
|
||||
const promptId = response.data.prompt_id;
|
||||
// Handle different plugins differently
|
||||
if (plugin === 'ComfyUi') {
|
||||
// For ComfyUI, use WebSocket for progress monitoring
|
||||
axios.get(`/api/comfyui-url?style_id=${style.id}`)
|
||||
.then(comfyResponse => {
|
||||
const comfyUiBaseUrl = comfyResponse.data.comfyui_url;
|
||||
const wsUrl = `ws://${new URL(comfyUiBaseUrl).host}/ws`;
|
||||
const ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === 'progress') {
|
||||
console.log('ComfyUI Progress Message:', message);
|
||||
const { value, max } = message.data;
|
||||
const progress = (max > 0) ? (value / max) * 100 : 0;
|
||||
if (message.data.prompt_id === promptId) {
|
||||
processingProgress.value = progress;
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket connected to ComfyUI.');
|
||||
|
||||
if (processingProgress.value >= 100) {
|
||||
console.log('Frontend: Progress reached 100%. Attempting to fetch final image.', { promptId: promptId });
|
||||
// Fetch the final styled image from the backend
|
||||
axios.get(`/api/images/fetch-styled/${promptId}`)
|
||||
.then(imageResponse => {
|
||||
console.log('Frontend: Successfully fetched styled image.', imageResponse.data);
|
||||
styledImage.value = imageResponse.data.styled_image;
|
||||
currentOverlayComponent.value = 'styledImageDisplay';
|
||||
fetchImages(); // Refresh gallery
|
||||
})
|
||||
.catch(imageError => {
|
||||
console.error('Frontend: Error fetching styled image:', imageError.response?.data?.error || imageError.message);
|
||||
showError(imageError.response?.data?.error || 'Failed to fetch styled image.');
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('Frontend: Final fetch process completed.');
|
||||
isLoading.value = false;
|
||||
processingImageUuid.value = null;
|
||||
processingProgress.value = 0;
|
||||
ws.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('Received unexpected WebSocket message type:', message.type, message);
|
||||
}
|
||||
};
|
||||
ws.onmessage = (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === 'progress') {
|
||||
console.log('ComfyUI Progress Message:', message);
|
||||
const { value, max } = message.data;
|
||||
const progress = (max > 0) ? (value / max) * 100 : 0;
|
||||
if (message.data.prompt_id === promptId) {
|
||||
processingProgress.value = progress;
|
||||
|
||||
if (processingProgress.value >= 100) {
|
||||
console.log('Frontend: Progress reached 100%. Attempting to fetch final image.', { promptId: promptId });
|
||||
// Fetch the final styled image from the backend
|
||||
axios.get(`/api/images/fetch-styled/${promptId}`)
|
||||
.then(imageResponse => {
|
||||
console.log('Frontend: Successfully fetched styled image.', imageResponse.data);
|
||||
styledImage.value = imageResponse.data.styled_image;
|
||||
currentOverlayComponent.value = 'styledImageDisplay';
|
||||
fetchImages(); // Refresh gallery
|
||||
})
|
||||
.catch(imageError => {
|
||||
console.error('Frontend: Error fetching styled image:', imageError.response?.data?.error || imageError.message);
|
||||
showError(imageError.response?.data?.error || 'Failed to fetch styled image.');
|
||||
})
|
||||
.finally(() => {
|
||||
console.log('Frontend: Final fetch process completed.');
|
||||
isLoading.value = false;
|
||||
processingProgress.value = 0;
|
||||
ws.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('Received unexpected WebSocket message type:', message.type, message);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
showError('WebSocket connection error.');
|
||||
isLoading.value = false;
|
||||
processingProgress.value = 0;
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket closed.');
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error applying style:', error);
|
||||
showError(error.response?.data?.error || 'Failed to apply style.');
|
||||
console.error('Error fetching ComfyUI URL:', error);
|
||||
showError(error.response?.data?.error || 'Failed to get ComfyUI URL.');
|
||||
isLoading.value = false;
|
||||
processingImageUuid.value = null;
|
||||
processingProgress.value = 0;
|
||||
ws.close();
|
||||
});
|
||||
} else {
|
||||
// For other plugins, use polling approach
|
||||
const pollForStyledImage = () => {
|
||||
axios.get(`/api/images/fetch-styled/${promptId}`)
|
||||
.then(imageResponse => {
|
||||
console.log('Frontend: Successfully fetched styled image.', imageResponse.data);
|
||||
styledImage.value = imageResponse.data.styled_image;
|
||||
currentOverlayComponent.value = 'styledImageDisplay';
|
||||
fetchImages(); // Refresh gallery
|
||||
isLoading.value = false;
|
||||
processingProgress.value = 0;
|
||||
})
|
||||
.catch(imageError => {
|
||||
console.error('Frontend: Error fetching styled image:', imageError.response?.data?.error || imageError.message);
|
||||
// If the image is not ready yet, continue polling
|
||||
if (imageError.response?.status === 404) {
|
||||
// Update progress if available
|
||||
if (imageError.response?.data?.progress !== undefined) {
|
||||
processingProgress.value = imageError.response.data.progress;
|
||||
}
|
||||
// Continue polling
|
||||
setTimeout(pollForStyledImage, 2000); // Poll every 2 seconds
|
||||
} else {
|
||||
showError(imageError.response?.data?.error || 'Failed to fetch styled image.');
|
||||
isLoading.value = false;
|
||||
processingProgress.value = 0;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
showError('WebSocket connection error.');
|
||||
isLoading.value = false;
|
||||
processingImageUuid.value = null;
|
||||
processingProgress.value = 0;
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket closed.');
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching ComfyUI URL:', error);
|
||||
showError(error.response?.data?.error || 'Failed to get ComfyUI URL.');
|
||||
isLoading.value = false;
|
||||
});
|
||||
|
||||
// Start polling for the styled image
|
||||
pollForStyledImage();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error applying style:', error);
|
||||
showError(error.response?.data?.error || 'Failed to apply style.');
|
||||
isLoading.value = false;
|
||||
processingProgress.value = 0;
|
||||
});
|
||||
};
|
||||
|
||||
const keepStyledImage = (imageToKeep) => {
|
||||
|
||||
@@ -103,6 +103,11 @@ return [
|
||||
'image_refresh_interval' => 'Bildaktualisierungsintervall (Sekunden)',
|
||||
'new_image_timespan_minutes' => 'Neue Bilder Zeitspanne (Minuten)',
|
||||
'gallery_heading' => 'Galerie Überschrift',
|
||||
'max_number_of_copies' => 'Maximale Anzahl an Kopien',
|
||||
'show_print_button' => 'Button \'Drucken\' beim Bild anzeigen',
|
||||
'printer' => 'Drucker',
|
||||
'custom_printer' => 'Eigener Drucker/Adresse',
|
||||
'custom_printer_address' => 'Druckername oder -adresse',
|
||||
],
|
||||
],
|
||||
'plugin' => [
|
||||
@@ -155,6 +160,6 @@ return [
|
||||
'delete_button' => 'Löschen',
|
||||
],
|
||||
'loading_spinner' => [
|
||||
'processing_image' => 'Bild wird verarbeitet...',
|
||||
'processing_image' => 'Bild wird verarbeitet...',
|
||||
],
|
||||
];
|
||||
];
|
||||
|
||||
@@ -102,6 +102,8 @@ return [
|
||||
'image_refresh_interval' => 'Image Refresh Interval (seconds)',
|
||||
'new_image_timespan_minutes' => 'New Image Timespan (minutes)',
|
||||
'gallery_heading' => 'Gallery Heading',
|
||||
"max_number_of_copies" => "Max Number of Copies",
|
||||
"show_print_button" => "Show 'Print' button on image",
|
||||
],
|
||||
],
|
||||
'user' => [
|
||||
|
||||
9
resources/views/filament/pages/global-settings.blade.php
Normal file
9
resources/views/filament/pages/global-settings.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<x-filament-panels::page>
|
||||
<form wire:submit="save">
|
||||
{{ $this->form }}
|
||||
|
||||
<x-filament-panels::form.actions
|
||||
:actions="$this->getFormActions()"
|
||||
/>
|
||||
</form>
|
||||
</x-filament-panels::page>
|
||||
@@ -22,37 +22,37 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($this->getTableRecords() as $record)
|
||||
@foreach ($plugins as $plugin)
|
||||
<x-filament-tables::row>
|
||||
<x-filament-tables::cell>
|
||||
{{ $record->name }}
|
||||
{{ $plugin->name }}
|
||||
</x-filament-tables::cell>
|
||||
<x-filament-tables::cell>
|
||||
{{ $record->identifier }}
|
||||
{{ $plugin->identifier }}
|
||||
</x-filament-tables::cell>
|
||||
<x-filament-tables::cell>
|
||||
@if ($record->enabled)
|
||||
@if ($plugin->enabled)
|
||||
<x-filament::icon
|
||||
icon="heroicon-o-check-circle"
|
||||
class="text-success-500"
|
||||
class="text-success-500 w-5 h-5"
|
||||
/>
|
||||
@else
|
||||
<x-filament::icon
|
||||
icon="heroicon-o-x-circle"
|
||||
class="text-danger-500"
|
||||
class="text-danger-500 w-5 h-5"
|
||||
/>
|
||||
@endif
|
||||
</x-filament-tables::cell>
|
||||
<x-filament-tables::cell>
|
||||
@if ($record->configured)
|
||||
@if ($plugin->configured)
|
||||
<x-filament::icon
|
||||
icon="heroicon-o-check-circle"
|
||||
class="text-success-500"
|
||||
class="text-success-500 w-5 h-5"
|
||||
/>
|
||||
@else
|
||||
<x-filament::icon
|
||||
icon="heroicon-o-x-circle"
|
||||
class="text-danger-500"
|
||||
class="text-danger-500 w-5 h-5"
|
||||
/>
|
||||
@endif
|
||||
</x-filament-tables::cell>
|
||||
|
||||
Reference in New Issue
Block a user