Merge remote-tracking branch 'origin/next' into fix/form-state

This commit is contained in:
Andras Bacsai committed 2026-05-25 16:08:19 +02:00
commit 27b76a4e97
65 files changed
+4703 -1945

No files matched your search

+2 -1
View File
@@ -172,7 +172,8 @@
}
@auth
window.Pusher = Pusher;
window.Echo = new Echo({
const EchoConstructor = typeof Echo === 'function' ? Echo : Echo.default;
window.Echo = new EchoConstructor({
broadcaster: 'pusher',
cluster: "{{ config('constants.pusher.host') }}" || window.location.hostname,
key: "{{ config('constants.pusher.app_key') }}" || 'coolify',
@@ -9,8 +9,11 @@
fullscreen: @entangle('fullscreen'),
alwaysScroll: {{ $isKeepAliveOn ? 'true' : 'false' }},
rafId: null,
scrollTimeout: null,
scrollDebounce: null,
isScrolling: false,
destroyed: false,
deploymentFinishedCleanup: null,
lastTouchY: 0,
showTimestamps: true,
searchQuery: '',
@@ -20,20 +23,28 @@
this.fullscreen = !this.fullscreen;
},
scrollToBottom() {
const logsContainer = document.getElementById('logsContainer');
if (this.destroyed) return;
const logsContainer = this.$root.querySelector('#logsContainer');
if (logsContainer) {
this.isScrolling = true;
logsContainer.scrollTop = logsContainer.scrollHeight;
setTimeout(() => { this.isScrolling = false; }, 50);
requestAnimationFrame(() => { this.isScrolling = false; });
}
},
cancelScrollLoop() {
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
if (this.scrollTimeout) {
clearTimeout(this.scrollTimeout);
this.scrollTimeout = null;
}
},
disableFollow() {
if (!this.alwaysScroll) return;
this.alwaysScroll = false;
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
this.cancelScrollLoop();
},
handleWheel(event) {
if (this.alwaysScroll && event.deltaY < 0) {
@@ -59,10 +70,11 @@
}
},
handleScroll(event) {
if (this.isScrolling) return;
if (this.isScrolling || this.destroyed) return;
const el = event.target;
clearTimeout(this.scrollDebounce);
this.scrollDebounce = setTimeout(() => {
const el = event.target;
if (this.destroyed) return;
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
if (!this.alwaysScroll && distanceFromBottom <= 10) {
this.alwaysScroll = true;
@@ -71,11 +83,12 @@
}, 150);
},
scheduleScroll() {
if (!this.alwaysScroll) return;
if (!this.alwaysScroll || this.destroyed) return;
this.rafId = requestAnimationFrame(() => {
if (!this.alwaysScroll || this.destroyed) return;
this.scrollToBottom();
if (this.alwaysScroll) {
setTimeout(() => this.scheduleScroll(), 250);
if (this.alwaysScroll && !this.destroyed) {
this.scrollTimeout = setTimeout(() => this.scheduleScroll(), 250);
}
});
},
@@ -84,10 +97,7 @@
if (this.alwaysScroll) {
this.scheduleScroll();
} else {
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
this.cancelScrollLoop();
}
},
hasActiveLogSelection() {
@@ -189,10 +199,7 @@
stopScroll() {
this.scrollToBottom();
this.alwaysScroll = false;
if (this.rafId) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}
this.cancelScrollLoop();
},
init() {
// Watch search query changes
@@ -200,21 +207,28 @@
this.applySearch();
});
// Apply search after Livewire updates
// Apply search after Livewire updates.
// Livewire.hook() has no deregister API, so this callback survives
// wire:navigate. It is made harmless after teardown by the
// `destroyed` guard and by only reacting to DOM inside this root.
Livewire.hook('morph.updated', ({ el }) => {
if (el.id === 'logs') {
this.$nextTick(() => {
this.applySearch();
if (this.alwaysScroll) {
this.scrollToBottom();
}
});
}
if (this.destroyed) return;
if (el.id !== 'logs' || !this.$root.contains(el)) return;
this.$nextTick(() => {
if (this.destroyed) return;
this.applySearch();
if (this.alwaysScroll) {
this.scrollToBottom();
}
});
});
// Stop auto-scroll when deployment finishes
Livewire.on('deploymentFinished', () => {
// Stop auto-scroll when deployment finishes.
// Livewire.on() returns an unregister fn; keep it for destroy().
this.deploymentFinishedCleanup = Livewire.on('deploymentFinished', () => {
if (this.destroyed) return;
setTimeout(() => {
if (this.destroyed) return;
this.stopScroll();
}, 500);
});
@@ -223,6 +237,20 @@
if (this.alwaysScroll) {
this.scheduleScroll();
}
},
destroy() {
// Runs when Alpine tears the component down (wire:navigate away).
this.destroyed = true;
this.alwaysScroll = false;
this.cancelScrollLoop();
if (this.scrollDebounce) {
clearTimeout(this.scrollDebounce);
this.scrollDebounce = null;
}
if (typeof this.deploymentFinishedCleanup === 'function') {
this.deploymentFinishedCleanup();
this.deploymentFinishedCleanup = null;
}
}
}" class="flex flex-1 min-h-0 flex-col overflow-hidden">
<livewire:project.application.deployment-navbar
@@ -274,10 +274,13 @@
<div>({{ $pull_request }})</div>
@endif
@if ($streamLogs)
<x-loading wire:poll.2000ms='getLogs(true)' />
<x-loading />
@endif
</div>
@endif
@if ($streamLogs)
<div class="sr-only" wire:poll.2000ms="getLogs(true)" aria-hidden="true"></div>
@endif
<div x-show="expanded" {{ $collapsible ? 'x-collapse' : '' }}
:class="fullscreen ? 'fullscreen flex flex-col !overflow-visible' : 'relative w-full {{ $collapsible ? 'py-4' : '' }} mx-auto'"
:style="fullscreen ? 'max-height: none !important; height: 100% !important;' : ''">
@@ -290,8 +290,8 @@
function createGithubApp(webhook_endpoint, preview_deployment_permissions, administration) {
const {
organization,
uuid,
html_url
html_url,
uuid
} = @json($github_app);
if (!webhook_endpoint) {
alert('Please select a webhook endpoint.');
@@ -299,6 +299,7 @@
}
let baseUrl = webhook_endpoint;
const name = @js($name);
const manifestState = @js($manifestState);
const isDev = @js(config('app.env')) ===
'local';
const devWebhook = @js(config('constants.webhooks.dev_webhook'));
@@ -340,7 +341,7 @@
};
const form = document.createElement('form');
form.setAttribute('method', 'post');
form.setAttribute('action', `${html_url}/${path}?state=${uuid}`);
form.setAttribute('action', `${html_url}/${path}?state=${manifestState}`);
const input = document.createElement('input');
input.setAttribute('id', 'manifest');
input.setAttribute('name', 'manifest');