feat: Theme studio v2

This commit is contained in:
2026-04-27 03:02:13 +02:00
parent 11c2588e45
commit 1b91eacb5b
52 changed files with 2792 additions and 844 deletions

View File

@@ -1,6 +1,9 @@
<!-- eslint-disable @angular-eslint/template/button-has-type, @angular-eslint/template/cyclomatic-complexity -->
<aside class="flex h-full min-h-0 flex-col bg-card">
<div class="border-b border-border px-3 py-3">
<div
appThemeNode="roomPanelHeader"
class="border-b border-border px-3 py-3"
>
@if (panelMode() === 'channels') {
<div class="flex items-center gap-3">
<div class="grid h-9 w-9 place-items-center rounded-md bg-secondary text-sm font-semibold text-foreground">
@@ -33,7 +36,10 @@
@if (panelMode() === 'channels') {
<div class="flex-1 overflow-auto">
<!-- Text Channels -->
<section class="px-2 py-3">
<section
appThemeNode="roomTextChannelsSection"
class="px-2 py-3"
>
<div class="mb-2 flex items-center justify-between px-1">
<h4 class="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">Text Channels</h4>
@if (canManageChannels()) {
@@ -52,6 +58,7 @@
<div class="space-y-1">
@for (ch of textChannels(); track ch.id) {
<button
appThemeNode="roomTextChannelItem"
class="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors"
[class.bg-secondary]="activeChannelId() === ch.id"
[class.text-foreground]="activeChannelId() === ch.id"
@@ -94,7 +101,10 @@
</section>
<!-- Voice Channels -->
<section class="border-t border-border px-2 py-3">
<section
appThemeNode="roomVoiceChannelsSection"
class="border-t border-border px-2 py-3"
>
<div class="mb-2 flex items-center justify-between px-1">
<h4 class="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">Voice Channels</h4>
@if (canManageChannels()) {
@@ -125,6 +135,7 @@
(drop)="onVoiceChannelDrop($event, ch.id)"
>
<button
appThemeNode="roomVoiceChannelItem"
class="flex w-full items-center justify-between rounded-md px-2 py-1.5 text-left text-sm transition-colors hover:bg-secondary/60"
(click)="joinVoice(ch.id)"
(contextmenu)="openChannelContextMenu($event, ch)"
@@ -174,6 +185,7 @@
>
@for (u of voiceUsersInRoom(ch.id); track u.id) {
<div
appThemeNode="roomVoiceUserItem"
class="flex items-center gap-2 rounded-md px-2 py-1.5 transition-colors hover:bg-secondary/50"
[class.cursor-pointer]="canDragVoiceUser(u)"
[class.opacity-60]="draggedVoiceUserId() === (u.id || u.oderId)"

View File

@@ -37,6 +37,7 @@ import { MessagesActions } from '../../../store/messages/messages.actions';
import { RealtimeSessionFacade } from '../../../core/realtime';
import { ScreenShareFacade } from '../../../domains/screen-share';
import { NotificationsFacade } from '../../../domains/notifications';
import { ThemeNodeDirective } from '../../../domains/theme';
import {
VoiceActivityService,
VoiceConnectionFacade,
@@ -82,7 +83,8 @@ type PanelMode = 'channels' | 'users';
ContextMenuComponent,
UserVolumeMenuComponent,
UserAvatarComponent,
ConfirmDialogComponent
ConfirmDialogComponent,
ThemeNodeDirective
],
viewProviders: [
provideIcons({
@@ -142,10 +144,8 @@ export class RoomsSidePanelComponent {
const memberIdentifiers = this.roomMemberIdentifiers();
const roomId = this.currentRoom()?.id;
return this.onlineUsers().filter((user) =>
!this.isCurrentUserIdentity(user)
&& this.matchesIdentifiers(memberIdentifiers, user)
&& this.isUserPresentInRoom(user, roomId)
return this.onlineUsers().filter(
(user) => !this.isCurrentUserIdentity(user) && this.matchesIdentifiers(memberIdentifiers, user) && this.isUserPresentInRoom(user, roomId)
);
});
offlineRoomMembers = computed(() => {
@@ -661,9 +661,7 @@ export class RoomsSidePanelComponent {
}
private handleVoiceJoinFailure(error: unknown): void {
const message = error instanceof Error
? error.message
: 'Failed to join voice channel.';
const message = error instanceof Error ? error.message : 'Failed to join voice channel.';
this.voiceConnection.reportConnectionError(message);
}