desktop: components/start-menu — Windows 11-style centered start menu with app grid, search, animated slide-up

This commit is contained in:
Butterfly Dev 2026-04-07 03:31:11 +00:00
parent 490317b3ea
commit 8ad129ed17
3 changed files with 186 additions and 0 deletions

View File

@ -0,0 +1,24 @@
<div class="start-menu-backdrop" (click)="close.emit()"></div>
<div class="start-menu">
<div class="start-header">
<span class="start-logo">🦋</span>
<span class="start-title">Butterfly</span>
</div>
<div class="start-search">
<input type="text" placeholder="Type to search..." class="search-input" />
</div>
<div class="app-grid">
@for (app of apps; track app.id) {
<button class="app-tile" (click)="onLaunch(app.id)">
<span class="app-tile-icon">{{ app.icon }}</span>
<span class="app-tile-title">{{ app.title }}</span>
</button>
}
</div>
<div class="start-footer">
<span class="version">Butterfly Desktop v0.1</span>
</div>
</div>

View File

@ -0,0 +1,134 @@
:host {
display: contents;
}
.start-menu-backdrop {
position: fixed;
inset: 0;
z-index: 8000;
}
.start-menu {
position: fixed;
bottom: 56px;
left: 50%;
transform: translateX(-50%);
width: 520px;
max-height: 580px;
background: #2d2d2d;
border: 1px solid #444;
border-radius: 12px;
box-shadow: 0 8px 48px rgba(0, 0, 0, 0.5), 0 2px 12px rgba(0, 0, 0, 0.3);
z-index: 8500;
display: flex;
flex-direction: column;
overflow: hidden;
animation: slideUp 0.2s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateX(-50%) translateY(20px);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
}
.start-header {
display: flex;
align-items: center;
gap: 10px;
padding: 20px 24px 12px;
}
.start-logo {
font-size: 28px;
}
.start-title {
font-size: 20px;
font-weight: 600;
color: #e0e0e0;
}
.start-search {
padding: 0 20px 12px;
}
.search-input {
width: 100%;
height: 36px;
padding: 0 12px;
border: 1px solid #555;
border-radius: 6px;
background: #383838;
color: #e0e0e0;
font-size: 13px;
outline: none;
box-sizing: border-box;
&::placeholder {
color: #888;
}
&:focus {
border-color: #0078d4;
box-shadow: 0 0 0 1px #0078d4;
}
}
.app-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
padding: 4px 20px 16px;
flex: 1;
}
.app-tile {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 16px 8px;
border: none;
background: transparent;
border-radius: 8px;
cursor: pointer;
transition: background 0.15s;
color: #e0e0e0;
&:hover {
background: rgba(255, 255, 255, 0.06);
}
&:active {
background: rgba(255, 255, 255, 0.03);
}
}
.app-tile-icon {
font-size: 32px;
}
.app-tile-title {
font-size: 12px;
text-align: center;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.start-footer {
padding: 8px 24px 14px;
border-top: 1px solid #3a3a3a;
}
.version {
font-size: 11px;
color: #666;
}

View File

@ -0,0 +1,28 @@
import { Component, output } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-start-menu',
standalone: true,
imports: [CommonModule],
templateUrl: './start-menu.component.html',
styleUrl: './start-menu.component.scss',
})
export class StartMenuComponent {
readonly launchApp = output<string>();
readonly close = output<void>();
apps = [
{ id: 'file-explorer', icon: '📁', title: 'File Explorer', description: 'Browse files and folders' },
{ id: 'terminal', icon: '⬛', title: 'Terminal', description: 'Command-line interface' },
{ id: 'text-editor', icon: '📝', title: 'Text Editor', description: 'Edit text and code files' },
{ id: 'browser', icon: '🌐', title: 'Web Browser', description: 'Browse the web' },
{ id: 'settings', icon: '⚙️', title: 'Settings', description: 'System configuration' },
{ id: 'remote-display', icon: '🖥️', title: 'Remote Desktop', description: 'Connect to a VM session' },
];
onLaunch(id: string): void {
this.launchApp.emit(id);
this.close.emit();
}
}