Add debugging console
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
<div class="max-w-3xl space-y-6">
|
||||
<section class="rounded-xl border border-border bg-card/40 p-5">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="rounded-xl bg-primary/10 p-2 text-primary">
|
||||
<ng-icon
|
||||
name="lucideBug"
|
||||
class="h-5 w-5"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-foreground">App-wide debugging</h4>
|
||||
<p class="mt-1 text-sm text-muted-foreground">
|
||||
Capture UI events, navigation activity, console output, and global runtime errors in a live debug console.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="relative inline-flex cursor-pointer items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="peer sr-only"
|
||||
[checked]="enabled()"
|
||||
(change)="onEnabledChange($event)"
|
||||
/>
|
||||
<div
|
||||
class="h-5 w-10 rounded-full bg-secondary after:absolute after:left-[2px] after:top-0.5 after:h-4 after:w-4 after:rounded-full after:bg-white after:transition-all after:content-[''] peer-checked:bg-primary peer-checked:after:translate-x-full"
|
||||
></div>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="grid gap-3 sm:grid-cols-3">
|
||||
<div class="rounded-xl border border-border bg-secondary/20 p-4">
|
||||
<div class="flex items-center gap-2 text-muted-foreground">
|
||||
<ng-icon
|
||||
name="lucideClock3"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<span class="text-xs font-medium uppercase tracking-wide">Captured events</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold text-foreground">{{ entryCount() }}</p>
|
||||
<p class="mt-1 text-xs text-muted-foreground">Last update: {{ lastUpdatedLabel() }}</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-border bg-secondary/20 p-4">
|
||||
<div class="flex items-center gap-2 text-muted-foreground">
|
||||
<ng-icon
|
||||
name="lucideCircleAlert"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<span class="text-xs font-medium uppercase tracking-wide">Errors</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold text-destructive">{{ errorCount() }}</p>
|
||||
<p class="mt-1 text-xs text-muted-foreground">Unhandled runtime failures and rejected promises.</p>
|
||||
</div>
|
||||
|
||||
<div class="rounded-xl border border-border bg-secondary/20 p-4">
|
||||
<div class="flex items-center gap-2 text-muted-foreground">
|
||||
<ng-icon
|
||||
name="lucideTriangleAlert"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
<span class="text-xs font-medium uppercase tracking-wide">Warnings</span>
|
||||
</div>
|
||||
<p class="mt-3 text-2xl font-semibold text-yellow-400">{{ warningCount() }}</p>
|
||||
<p class="mt-1 text-xs text-muted-foreground">Navigation cancellations, offline events, and other warnings.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="rounded-xl border border-border bg-card/40 p-5">
|
||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<h5 class="text-sm font-semibold text-foreground">Floating debug console</h5>
|
||||
<p class="mt-1 text-sm text-muted-foreground">
|
||||
When debugging is enabled, a bug icon appears in the app so you can open the docked console without blocking the rest of the UI.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
(click)="openConsole()"
|
||||
[disabled]="!enabled()"
|
||||
class="rounded-lg bg-primary px-3 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{{ isConsoleOpen() ? 'Console open' : 'Open console' }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
(click)="clearLogs()"
|
||||
[disabled]="entryCount() === 0"
|
||||
class="inline-flex items-center gap-1.5 rounded-lg bg-secondary px-3 py-2 text-sm font-medium text-foreground transition-colors hover:bg-secondary/80 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<ng-icon
|
||||
name="lucideTrash2"
|
||||
class="h-4 w-4"
|
||||
/>
|
||||
Clear logs
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
Component,
|
||||
computed,
|
||||
inject
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import {
|
||||
lucideBug,
|
||||
lucideCircleAlert,
|
||||
lucideClock3,
|
||||
lucideTrash2,
|
||||
lucideTriangleAlert
|
||||
} from '@ng-icons/lucide';
|
||||
|
||||
import { DebuggingService } from '../../../../core/services/debugging.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-debugging-settings',
|
||||
standalone: true,
|
||||
imports: [CommonModule, NgIcon],
|
||||
viewProviders: [
|
||||
provideIcons({
|
||||
lucideBug,
|
||||
lucideCircleAlert,
|
||||
lucideClock3,
|
||||
lucideTrash2,
|
||||
lucideTriangleAlert
|
||||
})
|
||||
],
|
||||
templateUrl: './debugging-settings.component.html'
|
||||
})
|
||||
export class DebuggingSettingsComponent {
|
||||
readonly debugging = inject(DebuggingService);
|
||||
|
||||
readonly enabled = this.debugging.enabled;
|
||||
readonly isConsoleOpen = this.debugging.isConsoleOpen;
|
||||
readonly entryCount = computed(() => {
|
||||
return this.debugging.entries().reduce((sum, entry) => sum + entry.count, 0);
|
||||
});
|
||||
readonly errorCount = computed(() => {
|
||||
return this.debugging.entries().reduce((sum, entry) => {
|
||||
return sum + (entry.level === 'error' ? entry.count : 0);
|
||||
}, 0);
|
||||
});
|
||||
readonly warningCount = computed(() => {
|
||||
return this.debugging.entries().reduce((sum, entry) => {
|
||||
return sum + (entry.level === 'warn' ? entry.count : 0);
|
||||
}, 0);
|
||||
});
|
||||
readonly lastUpdatedLabel = computed(() => {
|
||||
const lastEntry = this.debugging.entries().at(-1);
|
||||
|
||||
return lastEntry ? lastEntry.timeLabel : 'No logs yet';
|
||||
});
|
||||
|
||||
onEnabledChange(event: Event): void {
|
||||
const input = event.target as HTMLInputElement;
|
||||
|
||||
this.debugging.setEnabled(input.checked);
|
||||
}
|
||||
|
||||
openConsole(): void {
|
||||
this.debugging.openConsole();
|
||||
}
|
||||
|
||||
clearLogs(): void {
|
||||
this.debugging.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/member-ordering */
|
||||
import {
|
||||
Component,
|
||||
effect,
|
||||
inject,
|
||||
input,
|
||||
signal,
|
||||
computed
|
||||
signal
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
@@ -60,18 +60,21 @@ export class ServerSettingsComponent {
|
||||
private saveTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
/** Reload form fields whenever the server input changes. */
|
||||
serverData = computed(() => {
|
||||
const room = this.server();
|
||||
readonly serverData = this.server;
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const room = this.server();
|
||||
|
||||
if (!room)
|
||||
return;
|
||||
|
||||
if (room) {
|
||||
this.roomName = room.name;
|
||||
this.roomDescription = room.description || '';
|
||||
this.isPrivate.set(room.isPrivate);
|
||||
this.maxUsers = room.maxUsers || 0;
|
||||
}
|
||||
|
||||
return room;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
togglePrivate(): void {
|
||||
this.isPrivate.update((currentValue) => !currentValue);
|
||||
|
||||
@@ -122,6 +122,9 @@
|
||||
@case ('voice') {
|
||||
Voice & Audio
|
||||
}
|
||||
@case ('debugging') {
|
||||
Debugging
|
||||
}
|
||||
@case ('server') {
|
||||
Server Settings
|
||||
}
|
||||
@@ -157,6 +160,9 @@
|
||||
@case ('voice') {
|
||||
<app-voice-settings />
|
||||
}
|
||||
@case ('debugging') {
|
||||
<app-debugging-settings />
|
||||
}
|
||||
@case ('server') {
|
||||
<app-server-settings
|
||||
[server]="selectedServer()"
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Store } from '@ngrx/store';
|
||||
import { NgIcon, provideIcons } from '@ng-icons/core';
|
||||
import {
|
||||
lucideX,
|
||||
lucideBug,
|
||||
lucideGlobe,
|
||||
lucideAudioLines,
|
||||
lucideSettings,
|
||||
@@ -33,6 +34,7 @@ import { ServerSettingsComponent } from './server-settings/server-settings.compo
|
||||
import { MembersSettingsComponent } from './members-settings/members-settings.component';
|
||||
import { BansSettingsComponent } from './bans-settings/bans-settings.component';
|
||||
import { PermissionsSettingsComponent } from './permissions-settings/permissions-settings.component';
|
||||
import { DebuggingSettingsComponent } from './debugging-settings/debugging-settings.component';
|
||||
import { THIRD_PARTY_LICENSES, type ThirdPartyLicense } from './third-party-licenses';
|
||||
|
||||
@Component({
|
||||
@@ -44,6 +46,7 @@ import { THIRD_PARTY_LICENSES, type ThirdPartyLicense } from './third-party-lice
|
||||
NgIcon,
|
||||
NetworkSettingsComponent,
|
||||
VoiceSettingsComponent,
|
||||
DebuggingSettingsComponent,
|
||||
ServerSettingsComponent,
|
||||
MembersSettingsComponent,
|
||||
BansSettingsComponent,
|
||||
@@ -52,6 +55,7 @@ import { THIRD_PARTY_LICENSES, type ThirdPartyLicense } from './third-party-lice
|
||||
viewProviders: [
|
||||
provideIcons({
|
||||
lucideX,
|
||||
lucideBug,
|
||||
lucideGlobe,
|
||||
lucideAudioLines,
|
||||
lucideSettings,
|
||||
@@ -82,7 +86,10 @@ export class SettingsModalComponent {
|
||||
icon: 'lucideGlobe' },
|
||||
{ id: 'voice',
|
||||
label: 'Voice & Audio',
|
||||
icon: 'lucideAudioLines' }
|
||||
icon: 'lucideAudioLines' },
|
||||
{ id: 'debugging',
|
||||
label: 'Debugging',
|
||||
icon: 'lucideBug' }
|
||||
];
|
||||
readonly serverPages: { id: SettingsPage; label: string; icon: string }[] = [
|
||||
{ id: 'server',
|
||||
|
||||
Reference in New Issue
Block a user