diff --git a/extensions/Servers/Proxmox/routes.php b/extensions/Servers/Proxmox/routes.php
index fb371f72f..ed026088c 100644
--- a/extensions/Servers/Proxmox/routes.php
+++ b/extensions/Servers/Proxmox/routes.php
@@ -17,6 +17,8 @@ use Paymenter\Extensions\Servers\Proxmox\Livewire\VNC;
use Paymenter\Extensions\Servers\Proxmox\Livewire\XTerm;
use Paymenter\Extensions\Servers\Proxmox\Livewire\Help;
use Paymenter\Extensions\Servers\Proxmox\Livewire\Statistics;
+use Paymenter\Extensions\Servers\Proxmox\Models\Server;
+use Paymenter\Extensions\Servers\Proxmox\Proxmox;
Route::group([
'prefix' => 'servers',
@@ -39,4 +41,27 @@ Route::group([
Route::get('{server}/vnc', VNC::class)->name('server.vnc');
Route::get('{server}/xterm', XTerm::class)->name('server.xterm');
Route::get('{server}/help', Help::class)->name('server.help');
+
+ // Popup console routes (minimal layout for popup windows)
+ Route::get('{server}/vnc-popup', function (Server $server) {
+ if (!$server->node->vnc_enabled) {
+ abort(403, 'VNC is not enabled on this server node.');
+ }
+ $proxmox = new Proxmox;
+ return view('proxmox::vnc-popup', [
+ 'server' => $server,
+ 'vnc' => $proxmox->vnc($server),
+ ]);
+ })->name('server.vnc-popup');
+
+ Route::get('{server}/xterm-popup', function (Server $server) {
+ if (!$server->node->vnc_enabled) {
+ abort(403, 'Console access is not enabled on this server node.');
+ }
+ $proxmox = new Proxmox;
+ return view('proxmox::xterm-popup', [
+ 'server' => $server,
+ 'xterm' => $proxmox->xterm($server),
+ ]);
+ })->name('server.xterm-popup');
});
diff --git a/extensions/Servers/Proxmox/views/components/layout.blade.php b/extensions/Servers/Proxmox/views/components/layout.blade.php
index 06ad385f7..42506b312 100644
--- a/extensions/Servers/Proxmox/views/components/layout.blade.php
+++ b/extensions/Servers/Proxmox/views/components/layout.blade.php
@@ -1588,8 +1588,8 @@
'label' => 'Console',
'dropdown' => true,
'items' => [
- ['label' => 'NoVNC', 'view' => 'vnc', 'route' => route('proxmox.server.vnc', ['server' => $server->id])],
- ['label' => 'Xterm.js', 'view' => 'xterm', 'route' => route('proxmox.server.xterm', ['server' => $server->id])],
+ ['label' => 'NoVNC', 'view' => 'vnc', 'route' => route('proxmox.server.vnc-popup', ['server' => $server->id]), 'popup' => true],
+ ['label' => 'Xterm.js', 'view' => 'xterm', 'route' => route('proxmox.server.xterm-popup', ['server' => $server->id]), 'popup' => true],
]
];
}
@@ -1974,15 +1974,26 @@
@foreach($tab['items'] as $itemIndex => $item)
@php
$itemAdvanced = $item['advanced'] ?? false;
+ $isPopup = $item['popup'] ?? false;
@endphp
-
- {{ $item['label'] }}
-
+ @if($isPopup)
+
+ {{ $item['label'] }}
+
+ @else
+
+ {{ $item['label'] }}
+
+ @endif
@if($itemIndex < count($tab['items']) - 1 && $itemAdvanced && !($tab['items'][$itemIndex + 1]['advanced'] ?? false))
@endif
diff --git a/extensions/Servers/Proxmox/views/overview.blade.php b/extensions/Servers/Proxmox/views/overview.blade.php
index b77374def..12c86147a 100644
--- a/extensions/Servers/Proxmox/views/overview.blade.php
+++ b/extensions/Servers/Proxmox/views/overview.blade.php
@@ -1408,6 +1408,63 @@
let charts = {};
let peakRates = {};
+ // Handle chart toggle clicks via event delegation (works for dynamically created elements)
+ const handleChartToggle = (interfaceId) => {
+ const chartContainer = document.querySelector(`[data-chart-container="${interfaceId}"]`);
+
+ if (chartContainer) {
+ chartContainer.classList.toggle('open');
+
+ // Create chart on first open
+ if (chartContainer.classList.contains('open') && !charts[interfaceId]) {
+ const attemptCreateChart = (retries = 5, delay = 200) => {
+ const interfaceData = networkData?.interfaces?.find(i =>
+ i.id.replace(/[^a-z0-9]/gi, '-') === interfaceId
+ );
+
+ const canvas = document.getElementById(`chart-${interfaceId}`);
+
+ if (canvas && interfaceData && interfaceData.history) {
+ const historyLength = Math.max(
+ interfaceData.history.download?.length || 0,
+ interfaceData.history.upload?.length || 0
+ );
+
+ if (historyLength > 0) {
+ console.log('Creating chart for interface:', interfaceId);
+ requestAnimationFrame(() => {
+ setTimeout(() => {
+ if (!charts[interfaceId]) {
+ createChart(interfaceId, interfaceData);
+ }
+ }, 50);
+ });
+ return;
+ }
+ }
+
+ if (retries > 0) {
+ console.log(`Chart data not ready for ${interfaceId}, retrying... (${retries} attempts left)`);
+ setTimeout(() => attemptCreateChart(retries - 1, delay), delay);
+ }
+ };
+
+ attemptCreateChart();
+ }
+ }
+ };
+
+ // Event delegation for chart toggle buttons - attach once to document
+ document.addEventListener('click', (e) => {
+ const toggleButton = e.target.closest('.chart-toggle');
+ if (toggleButton) {
+ const interfaceId = toggleButton.dataset.interface;
+ if (interfaceId) {
+ handleChartToggle(interfaceId);
+ }
+ }
+ });
+
const formatBytes = (bytes, decimals = 2) => {
if (bytes === 0) return '0 B';
const k = 1024;
@@ -1669,96 +1726,7 @@
.map((iface) => createInterfaceCard(iface))
.join('');
- // Set up chart toggles - use event delegation or ensure fresh listeners
- document.querySelectorAll('.chart-toggle').forEach(button => {
- button.addEventListener('click', (e) => {
- const interfaceId = e.currentTarget.dataset.interface;
- const container = document.querySelector(`[data-chart-container="${interfaceId}"]`);
-
- if (container) {
- container.classList.toggle('open');
-
- // Create chart on first open
- if (container.classList.contains('open') && !charts[interfaceId]) {
- const attemptCreateChart = (retries = 5, delay = 200) => {
- const interfaceData = networkData?.interfaces?.find(i =>
- i.id.replace(/[^a-z0-9]/gi, '-') === interfaceId
- );
-
- const canvas = document.getElementById(`chart-${interfaceId}`);
-
- if (canvas && interfaceData && interfaceData.history) {
- const historyLength = Math.max(
- interfaceData.history.download?.length || 0,
- interfaceData.history.upload?.length || 0
- );
-
- if (historyLength > 0) {
- console.log('Creating chart for interface:', interfaceId, 'with data:', {
- id: interfaceData.id,
- name: interfaceData.name,
- historyLength: historyLength
- });
-
- // Use requestAnimationFrame to ensure DOM is ready
- requestAnimationFrame(() => {
- setTimeout(() => {
- if (!charts[interfaceId]) {
- createChart(interfaceId, interfaceData);
- }
- }, 50);
- });
- return;
- }
- }
-
- // Retry if data not ready yet
- if (retries > 0) {
- console.log(`Chart data not ready for ${interfaceId}, retrying... (${retries} attempts left)`);
- setTimeout(() => attemptCreateChart(retries - 1, delay), delay);
- } else {
- console.warn('Failed to create chart for', interfaceId, 'after multiple attempts. Available interfaces:', networkData?.interfaces?.map(i => i.id));
-
- // Try one more time with latest data from next poll
- const checkOnNextUpdate = () => {
- const latestData = networkData?.interfaces?.find(i =>
- i.id.replace(/[^a-z0-9]/gi, '-') === interfaceId
- );
- if (latestData && latestData.history && container.classList.contains('open')) {
- const historyLength = Math.max(
- latestData.history.download?.length || 0,
- latestData.history.upload?.length || 0
- );
- if (historyLength > 0 && !charts[interfaceId]) {
- console.log('Creating chart for', interfaceId, 'with data from next update');
- createChart(interfaceId, latestData);
- }
- }
- };
-
- // Check on next data update
- const originalUpdate = updateInterfaceData;
- const checkOnce = (interfaceData) => {
- if (interfaceData.id.replace(/[^a-z0-9]/gi, '-') === interfaceId && container.classList.contains('open')) {
- checkOnNextUpdate();
- }
- };
-
- // Set up a one-time check
- setTimeout(() => {
- if (container.classList.contains('open') && !charts[interfaceId]) {
- checkOnNextUpdate();
- }
- }, 500);
- }
- };
-
- // Start attempting to create chart
- attemptCreateChart();
- }
- }
- });
- });
+ // Chart toggles are handled by event delegation at document level
};
const createChart = (interfaceId, interfaceData) => {
diff --git a/extensions/Servers/Proxmox/views/vnc-popup.blade.php b/extensions/Servers/Proxmox/views/vnc-popup.blade.php
new file mode 100644
index 000000000..a8126bb6b
--- /dev/null
+++ b/extensions/Servers/Proxmox/views/vnc-popup.blade.php
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
NoVNC Console - {{ $server->name }}
+
+
+
+
+
+
diff --git a/extensions/Servers/Proxmox/views/xterm-popup.blade.php b/extensions/Servers/Proxmox/views/xterm-popup.blade.php
new file mode 100644
index 000000000..4f6f0a8d3
--- /dev/null
+++ b/extensions/Servers/Proxmox/views/xterm-popup.blade.php
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
Xterm.js Console - {{ $server->name }}
+
+
+
+
+
+