mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
Logs are back, baby
This commit is contained in:
parent
ac57f77806
commit
f7e5a09d48
2 changed files with 261 additions and 84 deletions
44
web/src/client/assets/logs.html
Normal file
44
web/src/client/assets/logs.html
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover, user-scalable=no">
|
||||||
|
<title>VibeTunnel - System Logs</title>
|
||||||
|
<meta name="description" content="View and manage VibeTunnel system logs">
|
||||||
|
|
||||||
|
<!-- Mobile optimizations -->
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
|
<meta name="theme-color" content="#1e1e1e">
|
||||||
|
|
||||||
|
<!-- Favicon -->
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png">
|
||||||
|
|
||||||
|
<!-- Styles -->
|
||||||
|
<link href="bundle/styles.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
log-viewer {
|
||||||
|
display: block;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="m-0 p-0" style="background: black;">
|
||||||
|
<log-viewer></log-viewer>
|
||||||
|
|
||||||
|
<script type="module" src="bundle/client-bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -298,105 +298,238 @@ export class LogViewer extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
${scrollbarStyles}
|
${scrollbarStyles}
|
||||||
<div class="flex flex-col h-full bg-dark-bg text-dark-text font-mono">
|
<div class="flex flex-col h-full bg-dark-bg text-dark-text font-mono">
|
||||||
<!-- Header -->
|
<!-- Header - single row on desktop, two rows on mobile -->
|
||||||
<div class="flex items-center gap-3 p-4 bg-dark-bg-secondary border-b border-dark-border">
|
<div class="bg-dark-bg-secondary border-b border-dark-border p-3 sm:p-4">
|
||||||
<!-- Back button -->
|
<!-- Mobile layout (two rows) -->
|
||||||
<button
|
<div class="sm:hidden">
|
||||||
class="px-3 py-1.5 bg-dark-bg border border-dark-border rounded text-sm text-dark-text hover:border-accent-green hover:text-accent-green transition-colors flex items-center gap-2 flex-shrink-0"
|
<!-- Top row with back button and title -->
|
||||||
@click=${() => (window.location.href = '/')}
|
<div class="flex items-center gap-2 mb-3">
|
||||||
>
|
<!-- Back button -->
|
||||||
<svg
|
<button
|
||||||
width="16"
|
class="p-2 bg-dark-bg border border-dark-border rounded text-sm text-dark-text hover:border-accent-green hover:text-accent-green transition-colors flex items-center gap-1 flex-shrink-0"
|
||||||
height="16"
|
@click=${() => (window.location.href = '/')}
|
||||||
viewBox="0 0 24 24"
|
>
|
||||||
fill="none"
|
<svg
|
||||||
stroke="currentColor"
|
width="16"
|
||||||
stroke-width="2"
|
height="16"
|
||||||
>
|
viewBox="0 0 24 24"
|
||||||
<path d="M15 18l-6-6 6-6" />
|
fill="none"
|
||||||
</svg>
|
stroke="currentColor"
|
||||||
Back
|
stroke-width="2"
|
||||||
</button>
|
>
|
||||||
|
<path d="M15 18l-6-6 6-6" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<h1 class="text-lg font-bold text-accent-green flex items-center gap-2 flex-shrink-0">
|
<h1
|
||||||
<terminal-icon size="24"></terminal-icon>
|
class="text-base font-bold text-accent-green flex items-center gap-2 flex-shrink-0"
|
||||||
<span>System Logs</span>
|
>
|
||||||
</h1>
|
<terminal-icon size="20"></terminal-icon>
|
||||||
|
<span>System Logs</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
<div class="flex-1 flex flex-wrap gap-2 items-center justify-end">
|
<!-- Auto-scroll toggle (mobile position) -->
|
||||||
<!-- Search input -->
|
<div class="ml-auto">
|
||||||
<input
|
<button
|
||||||
type="text"
|
class="p-2 text-xs uppercase font-bold rounded transition-colors ${this.autoScroll
|
||||||
class="px-3 py-1.5 bg-dark-bg border border-dark-border rounded text-sm text-dark-text placeholder-dark-text-muted focus:outline-none focus:border-accent-green transition-colors flex-1 sm:flex-initial sm:w-64 md:w-80"
|
? 'bg-accent-green text-dark-bg'
|
||||||
placeholder="Filter logs..."
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
.value=${this.filter}
|
@click=${() => {
|
||||||
@input=${(e: Event) => {
|
this.autoScroll = !this.autoScroll;
|
||||||
this.filter = (e.target as HTMLInputElement).value;
|
}}
|
||||||
}}
|
title="Auto Scroll"
|
||||||
/>
|
>
|
||||||
|
<svg
|
||||||
<!-- Level filters -->
|
width="16"
|
||||||
<div class="flex gap-1">
|
height="16"
|
||||||
${levels.map(
|
viewBox="0 0 24 24"
|
||||||
(level) => html`
|
fill="none"
|
||||||
<button
|
stroke="currentColor"
|
||||||
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this.levelFilter.has(
|
stroke-width="2"
|
||||||
level
|
|
||||||
)
|
|
||||||
? level === 'error'
|
|
||||||
? 'bg-status-error text-dark-bg'
|
|
||||||
: level === 'warn'
|
|
||||||
? 'bg-status-warning text-dark-bg'
|
|
||||||
: level === 'debug'
|
|
||||||
? 'bg-dark-text-muted text-dark-bg'
|
|
||||||
: 'bg-dark-text text-dark-bg'
|
|
||||||
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
|
||||||
@click=${() => this.toggleLevel(level)}
|
|
||||||
>
|
>
|
||||||
${level}
|
<path d="M12 5v14M19 12l-7 7-7-7" />
|
||||||
</button>
|
</svg>
|
||||||
`
|
</button>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Client/Server toggles -->
|
<!-- Filters row -->
|
||||||
<div class="flex gap-1">
|
<div class="flex flex-wrap gap-2">
|
||||||
<button
|
<!-- Search input -->
|
||||||
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
<input
|
||||||
.showClient
|
type="text"
|
||||||
? 'bg-orange-500 text-dark-bg'
|
class="px-3 py-1.5 bg-dark-bg border border-dark-border rounded text-sm text-dark-text placeholder-dark-text-muted focus:outline-none focus:border-accent-green transition-colors w-full"
|
||||||
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
placeholder="Filter logs..."
|
||||||
@click=${() => {
|
.value=${this.filter}
|
||||||
this.showClient = !this.showClient;
|
@input=${(e: Event) => {
|
||||||
|
this.filter = (e.target as HTMLInputElement).value;
|
||||||
}}
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Filters container -->
|
||||||
|
<div class="flex gap-2 items-center">
|
||||||
|
<!-- Level filters -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
${levels.map(
|
||||||
|
(level) => html`
|
||||||
|
<button
|
||||||
|
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${this.levelFilter.has(
|
||||||
|
level
|
||||||
|
)
|
||||||
|
? level === 'error'
|
||||||
|
? 'bg-status-error text-dark-bg'
|
||||||
|
: level === 'warn'
|
||||||
|
? 'bg-status-warning text-dark-bg'
|
||||||
|
: level === 'debug'
|
||||||
|
? 'bg-dark-text-muted text-dark-bg'
|
||||||
|
: 'bg-dark-text text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => this.toggleLevel(level)}
|
||||||
|
title="${level} logs"
|
||||||
|
>
|
||||||
|
${level === 'error'
|
||||||
|
? 'ERR'
|
||||||
|
: level === 'warn'
|
||||||
|
? 'WRN'
|
||||||
|
: level === 'debug'
|
||||||
|
? 'DBG'
|
||||||
|
: 'LOG'}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Client/Server toggles -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<button
|
||||||
|
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
||||||
|
.showClient
|
||||||
|
? 'bg-orange-500 text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => {
|
||||||
|
this.showClient = !this.showClient;
|
||||||
|
}}
|
||||||
|
title="Client logs"
|
||||||
|
>
|
||||||
|
C
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="px-1.5 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
||||||
|
.showServer
|
||||||
|
? 'bg-accent-green text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => {
|
||||||
|
this.showServer = !this.showServer;
|
||||||
|
}}
|
||||||
|
title="Server logs"
|
||||||
|
>
|
||||||
|
S
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop layout (single row) -->
|
||||||
|
<div class="hidden sm:flex items-center gap-3">
|
||||||
|
<!-- Back button -->
|
||||||
|
<button
|
||||||
|
class="px-3 py-1.5 bg-dark-bg border border-dark-border rounded text-sm text-dark-text hover:border-accent-green hover:text-accent-green transition-colors flex items-center gap-2 flex-shrink-0"
|
||||||
|
@click=${() => (window.location.href = '/')}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
>
|
>
|
||||||
CLIENT
|
<path d="M15 18l-6-6 6-6" />
|
||||||
</button>
|
</svg>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<h1 class="text-lg font-bold text-accent-green flex items-center gap-2 flex-shrink-0">
|
||||||
|
<terminal-icon size="24"></terminal-icon>
|
||||||
|
<span>System Logs</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="flex-1 flex flex-wrap gap-2 items-center justify-end">
|
||||||
|
<!-- Search input -->
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="px-3 py-1.5 bg-dark-bg border border-dark-border rounded text-sm text-dark-text placeholder-dark-text-muted focus:outline-none focus:border-accent-green transition-colors flex-1 sm:flex-initial sm:w-64 md:w-80"
|
||||||
|
placeholder="Filter logs..."
|
||||||
|
.value=${this.filter}
|
||||||
|
@input=${(e: Event) => {
|
||||||
|
this.filter = (e.target as HTMLInputElement).value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Level filters -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
${levels.map(
|
||||||
|
(level) => html`
|
||||||
|
<button
|
||||||
|
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this.levelFilter.has(
|
||||||
|
level
|
||||||
|
)
|
||||||
|
? level === 'error'
|
||||||
|
? 'bg-status-error text-dark-bg'
|
||||||
|
: level === 'warn'
|
||||||
|
? 'bg-status-warning text-dark-bg'
|
||||||
|
: level === 'debug'
|
||||||
|
? 'bg-dark-text-muted text-dark-bg'
|
||||||
|
: 'bg-dark-text text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => this.toggleLevel(level)}
|
||||||
|
>
|
||||||
|
${level}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Client/Server toggles -->
|
||||||
|
<div class="flex gap-1">
|
||||||
|
<button
|
||||||
|
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
||||||
|
.showClient
|
||||||
|
? 'bg-orange-500 text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => {
|
||||||
|
this.showClient = !this.showClient;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
CLIENT
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
||||||
|
.showServer
|
||||||
|
? 'bg-accent-green text-dark-bg'
|
||||||
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
|
@click=${() => {
|
||||||
|
this.showServer = !this.showServer;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
SERVER
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auto-scroll toggle -->
|
||||||
<button
|
<button
|
||||||
class="px-2 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
class="px-3 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
||||||
.showServer
|
.autoScroll
|
||||||
? 'bg-accent-green text-dark-bg'
|
? 'bg-accent-green text-dark-bg'
|
||||||
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.showServer = !this.showServer;
|
this.autoScroll = !this.autoScroll;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
SERVER
|
AUTO SCROLL
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Auto-scroll toggle -->
|
|
||||||
<button
|
|
||||||
class="px-3 py-1 text-xs uppercase font-bold rounded transition-colors ${this
|
|
||||||
.autoScroll
|
|
||||||
? 'bg-accent-green text-dark-bg'
|
|
||||||
: 'bg-dark-bg-tertiary text-dark-text-muted border border-dark-border'}"
|
|
||||||
@click=${() => {
|
|
||||||
this.autoScroll = !this.autoScroll;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
AUTO SCROLL
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue