Rework design part 1

This commit is contained in:
2026-04-01 19:31:00 +02:00
parent fed270d28d
commit 65b9419869
15 changed files with 324 additions and 222 deletions

View File

@@ -1,5 +1,5 @@
<div
class="fixed top-0 left-16 right-0 h-10 bg-card border-b border-border flex items-center justify-between px-4 z-50 select-none"
class="fixed left-16 right-0 top-0 z-50 flex h-10 items-center justify-between border-b border-border bg-card px-4 select-none"
style="-webkit-app-region: drag"
>
<div
@@ -11,7 +11,7 @@
name="lucideHash"
class="w-5 h-5 text-muted-foreground"
/>
<span class="text-sm font-semibold text-foreground truncate">{{ roomName() }}</span>
<span class="truncate text-sm font-semibold text-foreground">{{ roomContextTitle() }}</span>
@if (showRoomCompatibilityNotice()) {
<span class="inline-flex items-center gap-1 rounded bg-destructive/15 px-2 py-0.5 text-xs text-destructive">
@@ -29,9 +29,9 @@
</span>
}
@if (roomDescription()) {
<span class="hidden md:inline text-sm text-muted-foreground border-l border-border pl-2 truncate">
{{ roomDescription() }}
@if (roomContextMeta()) {
<span class="hidden truncate border-l border-border/70 pl-2 text-xs text-muted-foreground md:inline">
{{ roomContextMeta() }}
</span>
}
} @else {
@@ -51,7 +51,7 @@
>
<button
type="button"
class="px-3 h-8 grid place-items-center hover:bg-secondary rounded text-sm text-foreground"
class="grid h-8 place-items-center rounded-md px-3 text-sm text-foreground transition-colors hover:bg-secondary"
[class.hidden]="isAuthed()"
(click)="goLogin()"
title="Login"
@@ -62,7 +62,7 @@
<button
type="button"
(click)="toggleMenu()"
class="ml-2 p-2 hover:bg-secondary rounded"
class="ml-2 rounded-md p-2 transition-colors hover:bg-secondary"
title="Menu"
>
<ng-icon
@@ -72,13 +72,13 @@
</button>
<!-- Anchored dropdown under the menu button -->
@if (showMenu()) {
<div class="absolute right-0 top-full mt-1 z-50 w-64 rounded-lg border border-border bg-card shadow-lg">
<div class="absolute right-0 top-full z-50 mt-2 w-64 rounded-md border border-border bg-popover p-1 shadow-lg">
@if (inRoom()) {
<button
type="button"
(click)="createInviteLink()"
[disabled]="creatingInvite()"
class="w-full text-left px-3 py-2 text-sm hover:bg-secondary transition-colors text-foreground disabled:cursor-not-allowed disabled:opacity-60"
class="w-full rounded-md px-3 py-2 text-left text-sm text-foreground transition-colors hover:bg-secondary disabled:cursor-not-allowed disabled:opacity-60"
>
@if (creatingInvite()) {
Creating Invite Link…
@@ -89,22 +89,22 @@
<button
type="button"
(click)="leaveServer()"
class="w-full text-left px-3 py-2 text-sm hover:bg-secondary transition-colors text-foreground"
class="w-full rounded-md px-3 py-2 text-left text-sm text-foreground transition-colors hover:bg-secondary"
>
Leave Server
</button>
}
<div
class="border-t border-border px-3 py-2 text-xs leading-5 text-muted-foreground"
class="px-3 py-2 text-xs leading-5 text-muted-foreground"
[class.hidden]="!inviteStatus()"
>
{{ inviteStatus() }}
</div>
<div class="border-t border-border"></div>
<div class="mx-2 my-1 h-px bg-border"></div>
<button
type="button"
(click)="logout()"
class="w-full text-left px-3 py-2 text-sm hover:bg-secondary transition-colors text-foreground"
class="w-full rounded-md px-3 py-2 text-left text-sm text-foreground transition-colors hover:bg-secondary"
>
Logout
</button>
@@ -113,7 +113,7 @@
@if (isElectron()) {
<button
type="button"
class="w-8 h-8 grid place-items-center hover:bg-secondary rounded"
class="grid h-8 w-8 place-items-center rounded-md transition-colors hover:bg-secondary"
title="Minimize"
(click)="minimize()"
>
@@ -124,7 +124,7 @@
</button>
<button
type="button"
class="w-8 h-8 grid place-items-center hover:bg-secondary rounded"
class="grid h-8 w-8 place-items-center rounded-md transition-colors hover:bg-secondary"
title="Maximize"
(click)="maximize()"
>
@@ -135,7 +135,7 @@
</button>
<button
type="button"
class="w-8 h-8 grid place-items-center hover:bg-destructive/10 rounded"
class="grid h-8 w-8 place-items-center rounded-md transition-colors hover:bg-destructive/10"
title="Close"
(click)="close()"
>

View File

@@ -21,6 +21,9 @@ import {
import { Router } from '@angular/router';
import {
selectCurrentRoom,
selectActiveChannelId,
selectTextChannels,
selectVoiceChannels,
selectIsSignalServerReconnecting,
selectSignalServerCompatibilityError
} from '../../store/rooms/rooms.selectors';
@@ -33,6 +36,7 @@ import { PlatformService } from '../../core/platform';
import { STORAGE_KEY_CURRENT_USER_ID } from '../../core/constants';
import { LeaveServerDialogComponent } from '../../shared';
import { Room } from '../../shared-kernel';
import { VoiceWorkspaceService } from '../../domains/voice-session';
@Component({
selector: 'app-title-bar',
@@ -63,6 +67,7 @@ export class TitleBarComponent {
private router = inject(Router);
private webrtc = inject(RealtimeSessionFacade);
private platform = inject(PlatformService);
private voiceWorkspace = inject(VoiceWorkspaceService);
private getWindowControlsApi() {
return this.electronBridge.getApi();
@@ -78,11 +83,62 @@ export class TitleBarComponent {
isReconnecting = computed(() => !this.webrtc.isConnected() && this.webrtc.hasEverConnected());
isAuthed = computed(() => !!this.currentUser());
currentRoom = this.store.selectSignal(selectCurrentRoom);
activeChannelId = this.store.selectSignal(selectActiveChannelId);
textChannels = this.store.selectSignal(selectTextChannels);
voiceChannels = this.store.selectSignal(selectVoiceChannels);
isVoiceWorkspaceExpanded = this.voiceWorkspace.isExpanded;
isSignalServerReconnecting = this.store.selectSignal(selectIsSignalServerReconnecting);
signalServerCompatibilityError = this.store.selectSignal(selectSignalServerCompatibilityError);
inRoom = computed(() => !!this.currentRoom());
roomName = computed(() => this.currentRoom()?.name || '');
roomDescription = computed(() => this.currentRoom()?.description || '');
activeTextChannelName = computed(() => {
const textChannels = this.textChannels();
if (textChannels.length === 0) {
return 'No text channels';
}
const id = this.activeChannelId();
const activeChannel = textChannels.find((channel) => channel.id === id) ?? textChannels[0];
return activeChannel ? activeChannel.name : id;
});
connectedVoiceChannelName = computed(() => {
const voiceChannelId = this.currentUser()?.voiceState?.roomId;
const voiceChannel = this.voiceChannels().find((channel) => channel.id === voiceChannelId);
return voiceChannel?.name || 'Voice Lounge';
});
roomContextTitle = computed(() => {
const room = this.currentRoom();
if (!room) {
return '';
}
if (this.isVoiceWorkspaceExpanded()) {
return `${room.name} / ${this.connectedVoiceChannelName()}`;
}
if (this.textChannels().length === 0) {
return room.name;
}
return `${room.name} / #${this.activeTextChannelName()}`;
});
roomContextMeta = computed(() => {
if (!this.currentRoom()) {
return '';
}
const parts = [`${this.textChannels().length} text`];
if (this.voiceChannels().length > 0) {
parts.push(`${this.voiceChannels().length} voice`);
}
return parts.join(' | ');
});
showRoomCompatibilityNotice = computed(() =>
this.inRoom() && !!this.signalServerCompatibilityError()
);