From 36d7844989a0bbd97b38e8d4777dc75ed61ed189 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:47:03 +0100 Subject: [PATCH 1/5] Fix deployment log view UX issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Prevent text selection from being cleared when logs are re-rendered during polling - Preserve fullscreen state when toggling debug logs or other Livewire updates - Fix log filtering to properly apply when debug mode is toggled 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- .../Project/Application/Deployment/Show.php | 2 + .../application/deployment/show.blade.php | 57 ++++++++++++------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/app/Livewire/Project/Application/Deployment/Show.php b/app/Livewire/Project/Application/Deployment/Show.php index 44ab419c2..8c0ee1a3f 100644 --- a/app/Livewire/Project/Application/Deployment/Show.php +++ b/app/Livewire/Project/Application/Deployment/Show.php @@ -20,6 +20,8 @@ class Show extends Component public bool $is_debug_enabled = false; + public bool $fullscreen = false; + private bool $deploymentFinishedDispatched = false; public function getListeners() diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index f2cde05cf..f125cde91 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -6,7 +6,7 @@
- @if (data_get($application_deployment_queue, 'status') === 'in_progress') -
Deployment is -
- {{ Str::headline(data_get($this->application_deployment_queue, 'status')) }}. -
- -
- {{--
Logs will be updated automatically.
--}} - @else -
Deployment is {{ Str::headline(data_get($application_deployment_queue, 'status')) }}. -
- @endif -
+
+ :class="fullscreen ? 'h-full' : 'border border-dotted rounded-sm'">
- - +
+ @if (data_get($application_deployment_queue, 'status') === 'in_progress') +
+ Deployment is + In Progress + +
+ @else +
+ Deployment is + {{ Str::headline(data_get($application_deployment_queue, 'status')) }} +
+ @endif + +
$searchableContent = $line['timestamp'] . ' ' . $lineContent; @endphp
isset($line['command']) && $line['command'], 'flex gap-2', ])> From d40c2caca20113023576e6d711087750532e7a7c Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:49:26 +0100 Subject: [PATCH 2/5] Fix text disappearing during selection in deployment logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure initial render happens even when selection is active by checking if element already has content before skipping re-render. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../livewire/project/application/deployment/show.blade.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index f125cde91..365db724d 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -63,7 +63,8 @@ }, renderHighlightedLog(el, text) { // Skip re-render if user has text selected in logs (preserves copy ability) - if (this.hasActiveLogSelection()) { + // But always render if the element is empty (initial render) + if (el.textContent && this.hasActiveLogSelection()) { return; } From 5cc822c9963312dd9f7bd3f5ba8b7a45d5e771e2 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:52:00 +0100 Subject: [PATCH 3/5] Fix text selection issue in runtime logs view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply the same selection preservation fix to the runtime logs component (get-logs.blade.php) that was applied to deployment logs: - Add hasActiveLogSelection() helper to detect active text selection - Skip re-render when user has text selected (preserves copy ability) - Add renderTrigger mechanism to ensure filtering works after refresh - Use x-effect for hidden state to properly react to Livewire updates 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../project/shared/get-logs.blade.php | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 8504a160f..692138c5b 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -9,6 +9,7 @@ scrollDebounce: null, colorLogs: localStorage.getItem('coolify-color-logs') === 'true', searchQuery: '', + renderTrigger: 0, containerName: '{{ $container ?? "logs" }}', makeFullscreen() { this.fullscreen = !this.fullscreen; @@ -80,6 +81,18 @@ if (!this.searchQuery.trim()) return true; return line.toLowerCase().includes(this.searchQuery.toLowerCase()); }, + hasActiveLogSelection() { + const selection = window.getSelection(); + if (!selection || selection.isCollapsed || !selection.toString().trim()) { + return false; + } + const logsContainer = document.getElementById('logs'); + if (!logsContainer) return false; + + // Check if selection is within the logs container + const range = selection.getRangeAt(0); + return logsContainer.contains(range.commonAncestorContainer); + }, decodeHtml(text) { // Decode HTML entities, handling double-encoding with max iteration limit to prevent DoS let decoded = text; @@ -96,6 +109,12 @@ return decoded; }, renderHighlightedLog(el, text) { + // Skip re-render if user has text selected in logs (preserves copy ability) + // But always render if the element is empty (initial render) + if (el.textContent && this.hasActiveLogSelection()) { + return; + } + const decoded = this.decodeHtml(text); el.textContent = ''; @@ -167,6 +186,12 @@ this.$wire.getLogs(true); this.logsLoaded = true; } + // Re-render logs after Livewire updates + Livewire.hook('commit', ({ succeed }) => { + succeed(() => { + this.$nextTick(() => { this.renderTrigger++; }); + }); + }); } }"> @if ($collapsible) @@ -350,8 +375,8 @@ class="text-gray-500 dark:text-gray-400 py-2"> @endphp
{{ $timestamp }} @endif
@endforeach From 6b9c633fe744ab2aa47a3a6341bae775628c8268 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:53:02 +0100 Subject: [PATCH 4/5] Prevent Livewire from morphing logs when text is selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use Livewire's morph.updating hook to skip DOM morphing of the logs container when user has text selected. This prevents the selection from being lost when polling or manual refresh occurs. The previous fix only prevented the JavaScript-based re-render, but Livewire's morphing was still replacing the DOM elements entirely. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../livewire/project/application/deployment/show.blade.php | 6 ++++++ resources/views/livewire/project/shared/get-logs.blade.php | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/resources/views/livewire/project/application/deployment/show.blade.php b/resources/views/livewire/project/application/deployment/show.blade.php index 365db724d..5f37786f5 100644 --- a/resources/views/livewire/project/application/deployment/show.blade.php +++ b/resources/views/livewire/project/application/deployment/show.blade.php @@ -147,6 +147,12 @@ } }, init() { + // Prevent Livewire from morphing logs container when text is selected + Livewire.hook('morph.updating', ({ el, component, toEl, skip }) => { + if (el.id === 'logs' && this.hasActiveLogSelection()) { + skip(); + } + }); // Re-render logs after Livewire updates document.addEventListener('livewire:navigated', () => { this.$nextTick(() => { this.renderTrigger++; }); diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 692138c5b..91f615227 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -186,6 +186,12 @@ this.$wire.getLogs(true); this.logsLoaded = true; } + // Prevent Livewire from morphing logs container when text is selected + Livewire.hook('morph.updating', ({ el, component, toEl, skip }) => { + if (el.id === 'logs' && this.hasActiveLogSelection()) { + skip(); + } + }); // Re-render logs after Livewire updates Livewire.hook('commit', ({ succeed }) => { succeed(() => { From 987252a179f9ae1575977cb9a86374a63443c501 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 15 Dec 2025 10:57:07 +0100 Subject: [PATCH 5/5] Move polling button next to refresh button in runtime logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reorder toolbar buttons so that Refresh and Stream Logs (polling) are adjacent, making the related actions easier to find. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../project/shared/get-logs.blade.php | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/resources/views/livewire/project/shared/get-logs.blade.php b/resources/views/livewire/project/shared/get-logs.blade.php index 91f615227..c4b610873 100644 --- a/resources/views/livewire/project/shared/get-logs.blade.php +++ b/resources/views/livewire/project/shared/get-logs.blade.php @@ -266,6 +266,23 @@ class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text- d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" /> + -