desktop: components/apps/settings — settings panel with display/audio/network/about sections

This commit is contained in:
Butterfly Dev 2026-04-07 03:37:09 +00:00
parent 0048eabb60
commit 6beb93b16f
3 changed files with 220 additions and 0 deletions

View File

@ -0,0 +1,62 @@
<div class="settings">
<!-- Sidebar -->
<div class="settings-sidebar">
<div class="settings-title">Settings</div>
@for (section of sections; track section.id) {
<button
class="settings-nav-item"
[class.active]="activeSection() === section.id"
(click)="activeSection.set(section.id)"
>
<span class="nav-icon">{{ section.icon }}</span>
<span class="nav-label">{{ section.title }}</span>
</button>
}
</div>
<!-- Content -->
<div class="settings-content">
@switch (activeSection()) {
@case ('display') {
<div class="settings-section">
<h3>Display Settings</h3>
<div class="setting-row">
<label>Frame Buffer Size</label>
<input type="number" [ngModel]="frameBufferSize()" min="10" max="300" />
</div>
<div class="setting-desc">Number of frames to buffer for late-joining viewers (default: 60).</div>
</div>
}
@case ('audio') {
<div class="settings-section">
<h3>Audio Settings</h3>
<div class="setting-desc">Audio settings will be available once the VM agent is connected.</div>
</div>
}
@case ('network') {
<div class="settings-section">
<h3>Network Settings</h3>
<div class="setting-row">
<label>Server URL</label>
<input type="text" [ngModel]="serverUrl()" />
</div>
<div class="setting-row">
<label>Idle Timeout (sec)</label>
<input type="number" [ngModel]="idleTimeout()" min="30" max="3600" />
</div>
</div>
}
@case ('about') {
<div class="settings-section">
<h3>About Butterfly</h3>
<div class="about-info">
<div class="about-row"><span>Version</span><span>0.1.0</span></div>
<div class="about-row"><span>Backend</span><span>Rust / Actix-Web 4</span></div>
<div class="about-row"><span>Frontend</span><span>Angular 21</span></div>
<div class="about-row"><span>Protocol</span><span>WebSocket + REST</span></div>
</div>
</div>
}
}
</div>
</div>

View File

@ -0,0 +1,127 @@
:host {
display: block;
width: 100%;
height: 100%;
}
.settings {
width: 100%;
height: 100%;
display: flex;
background: #1e1e1e;
}
.settings-sidebar {
width: 200px;
background: #252526;
border-right: 1px solid #404040;
padding: 12px 0;
flex-shrink: 0;
overflow-y: auto;
}
.settings-title {
padding: 0 16px 12px;
font-size: 16px;
font-weight: 600;
color: #e0e0e0;
}
.settings-nav-item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 8px 16px;
border: none;
background: transparent;
color: #ccc;
font-size: 13px;
cursor: pointer;
text-align: left;
&:hover {
background: rgba(255, 255, 255, 0.04);
}
&.active {
background: rgba(0, 120, 212, 0.15);
color: #fff;
border-left: 2px solid #0078d4;
}
}
.nav-icon {
font-size: 16px;
}
.settings-content {
flex: 1;
padding: 24px;
overflow-y: auto;
}
.settings-section {
max-width: 500px;
h3 {
margin: 0 0 16px;
color: #e0e0e0;
font-size: 18px;
font-weight: 500;
}
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;
label {
color: #ccc;
font-size: 13px;
}
input {
width: 120px;
height: 30px;
padding: 0 8px;
border: 1px solid #555;
border-radius: 4px;
background: #383838;
color: #e0e0e0;
font-size: 13px;
outline: none;
&:focus {
border-color: #0078d4;
}
}
}
.setting-desc {
font-size: 12px;
color: #888;
margin-top: 4px;
}
.about-info {
margin-top: 12px;
}
.about-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #333;
font-size: 13px;
span:first-child {
color: #999;
}
span:last-child {
color: #e0e0e0;
}
}

View File

@ -0,0 +1,31 @@
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
interface SettingSection {
id: string;
title: string;
icon: string;
}
@Component({
selector: 'app-settings',
standalone: true,
imports: [CommonModule],
templateUrl: './settings.component.html',
styleUrl: './settings.component.scss',
})
export class SettingsComponent {
readonly activeSection = signal('display');
sections: SettingSection[] = [
{ id: 'display', title: 'Display', icon: '🖥️' },
{ id: 'audio', title: 'Audio', icon: '🔊' },
{ id: 'network', title: 'Network', icon: '🌐' },
{ id: 'about', title: 'About', icon: '' },
];
readonly serverUrl = signal(window.location.origin);
readonly maxSessions = signal('100');
readonly frameBufferSize = signal('60');
readonly idleTimeout = signal('300');
}