diff --git a/resources/js/terminal.js b/resources/js/terminal.js index 25867e500..bdcc3824d 100644 --- a/resources/js/terminal.js +++ b/resources/js/terminal.js @@ -8,7 +8,14 @@ import { } from './terminal-session-timer.js'; import { FitAddon } from '@xterm/addon-fit'; -const terminalDebugEnabled = import.meta.env.DEV; +const terminalDebugParameter = new URLSearchParams(window.location.search).get('terminal-debug'); + +if (terminalDebugParameter === '1' || terminalDebugParameter === '0') { + localStorage.setItem('coolify-terminal-debug', terminalDebugParameter); +} + +const terminalDebugEnabled = import.meta.env.DEV + || localStorage.getItem('coolify-terminal-debug') === '1'; const baseApplicationTerminalTheme = { black: '#675f70', @@ -393,9 +400,15 @@ export function initializeTerminalComponent() { setTerminalTheme(themeName) { if (!applicationTerminalThemes[themeName]) { + logTerminal('warn', '[Terminal Theme] Unknown theme', { + requestedTheme: themeName, + availableThemes: Object.keys(applicationTerminalThemes), + }); return; } + logTerminal('log', '[Terminal Theme] Applying theme', this.terminalThemeDebugSnapshot(themeName)); + if (themeName === 'system') { applicationTerminalThemes.system = createSystemTerminalTheme(); } @@ -417,10 +430,36 @@ export function initializeTerminalComponent() { this.term.options.cursorBlink = cursorBlink; this.term.refresh(0, Math.max(0, this.term.rows - 1)); this.term.focus(); + logTerminal('log', '[Terminal Theme] Theme applied', this.terminalThemeDebugSnapshot(themeName)); }); } }, + terminalThemeDebugSnapshot(themeName) { + const shell = this.$el.closest('.application-console-shell'); + const viewport = this.term?.element?.querySelector('.xterm-viewport'); + const screen = this.term?.element?.querySelector('.xterm-screen'); + + return { + requestedTheme: themeName, + selectedTheme: this.selectedTheme, + terminalExists: Boolean(this.term), + terminalOpened: Boolean(this.term?.element), + terminalActive: this.terminalActive, + connectionState: this.connectionState, + shellTheme: shell?.dataset.consoleTheme, + shellBackground: shell ? getComputedStyle(shell).background : null, + shellThemeBackground: shell ? getComputedStyle(shell).getPropertyValue('--console-theme-background') : null, + shellThemeOpacity: shell ? getComputedStyle(shell).getPropertyValue('--console-theme-opacity') : null, + shellPseudoBackground: shell ? getComputedStyle(shell, '::before').background : null, + viewportBackground: viewport ? getComputedStyle(viewport).background : null, + screenBackground: screen ? getComputedStyle(screen).background : null, + xtermBackground: this.term?.options.theme?.background, + xtermForeground: this.term?.options.theme?.foreground, + xtermCursor: this.term?.options.theme?.cursor, + }; + }, + resetTerminal() { if (this.term) { this.$wire.dispatch('error', 'Terminal websocket connection lost. Reconnecting...'); diff --git a/resources/views/components/forms/listbox.blade.php b/resources/views/components/forms/listbox.blade.php index 050bea8b3..0dd64ccf2 100644 --- a/resources/views/components/forms/listbox.blade.php +++ b/resources/views/components/forms/listbox.blade.php @@ -1,5 +1,6 @@ @props([ 'id' => null, + 'htmlId' => null, 'label' => null, 'helper' => null, 'required' => false, @@ -14,6 +15,10 @@ 'tooltip' => true, ]) +@php + $triggerId = ($htmlId ?? $id).'-trigger'; +@endphp +
@if ($label) {{-- @@ -23,7 +28,7 @@ --}} {{-- Fixed h-4 matches the helper icon so side-by-side fields align with or without a helper. --}}
-
@else
+ class="application-settings-section-body is-flush mt-1 w-full scroll-mt-28 overflow-visible"> @foreach ($domainGroups as $appId => $rows) @php $app = collect($serviceApps)->firstWhere('id', (int) $appId); @@ -190,21 +190,14 @@
{{ $heading }} @can('update', $service) -
- {{ $redirectLabel }} - - - +
@else {{ $redirectLabel }} diff --git a/resources/views/livewire/terminal/index.blade.php b/resources/views/livewire/terminal/index.blade.php index 123c3af43..6939049f9 100644 --- a/resources/views/livewire/terminal/index.blade.php +++ b/resources/views/livewire/terminal/index.blade.php @@ -102,7 +102,7 @@ } }"> @if ($selected_uuid === 'default') -
@@ -161,7 +161,7 @@
@else -
diff --git a/tests/Feature/RealtimeTerminalPackagingTest.php b/tests/Feature/RealtimeTerminalPackagingTest.php index 5754ddc52..2295531cc 100644 --- a/tests/Feature/RealtimeTerminalPackagingTest.php +++ b/tests/Feature/RealtimeTerminalPackagingTest.php @@ -183,11 +183,12 @@ it('mounts the realtime terminal utilities in local development compose files', 'maxio dev compose' => 'docker-compose-maxio.dev.yml', ]); -it('keeps terminal browser logging restricted to Vite development mode', function () { +it('keeps terminal browser logging restricted to development or explicit diagnostics', function () { $terminalClient = file_get_contents(base_path('resources/js/terminal.js')); expect($terminalClient) - ->toContain('const terminalDebugEnabled = import.meta.env.DEV;') + ->toContain('const terminalDebugEnabled = import.meta.env.DEV') + ->toContain("localStorage.getItem('coolify-terminal-debug') === '1'") ->toContain("logTerminal('log', '[Terminal] WebSocket connection established.');") ->not->toContain("console.log('[Terminal] WebSocket connection established. Cool cool cool cool cool cool.');"); }); diff --git a/tests/Feature/ServiceDomainsTest.php b/tests/Feature/ServiceDomainsTest.php index 11fdc0993..b9cb8cd70 100644 --- a/tests/Feature/ServiceDomainsTest.php +++ b/tests/Feature/ServiceDomainsTest.php @@ -104,9 +104,11 @@ it('groups configured domains with their service redirect and excludes services expect($html) ->toContain("service-domain-group-{$this->apiApp->id}") - ->toContain("service-domain-redirect-{$this->apiApp->id}") - ->toContain("wire:model.change=\"serviceRedirects.{$this->apiApp->id}\"") - ->toContain("wire:target=\"serviceRedirects.{$this->apiApp->id}\"") + ->toContain("id=\"service-domain-redirect-{$this->apiApp->id}-trigger\"") + ->toContain("serviceRedirects.{$this->apiApp->id}") + ->toContain('class="listbox-trigger"') + ->toContain('application-settings-section-body is-flush mt-1 w-full scroll-mt-28 overflow-visible') + ->not->toContain("