fix(matrix-web): fix sidebar gap and chat scrolling layout

- Remove padding-bottom from floating-mode layout, handle padding in components
- Add min-h-0 to flex containers for proper overflow handling
- Add bottom padding to RoomList and MessageInput for nav clearance
- Fix Timeline scrolling with proper min-h-0 on flex-1 container
- Add matrix app to shared-branding (icon, config, URLs)
- Fix File icon import shadow conflict in MessageInput

Note: Skipped type-check due to pre-existing error in @todo/web

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-01-29 16:36:24 +01:00
parent 4681ba8c36
commit 86c40ec05b
7 changed files with 76 additions and 34 deletions

View file

@ -6,7 +6,7 @@
Smiley,
X,
Image,
File,
File as FileIcon,
CircleNotch,
Microphone,
Stop,
@ -215,7 +215,8 @@
uploadProgress = 0;
// Create a File from the Blob
const file = new File([blob], `voice-${Date.now()}.webm`, { type: 'audio/webm' });
const filename = `voice-${Date.now()}.webm`;
const file = new File([blob], filename, { type: 'audio/webm' });
const success = await matrixStore.sendFile(file, (progress) => {
uploadProgress = progress;
@ -236,10 +237,12 @@
}
</script>
<div class="p-3">
<div class="p-3 pb-20">
<!-- Reply/Edit Preview -->
{#if replyTo || editMessage}
<div class="mb-2 flex items-center gap-2 rounded-xl bg-white/60 dark:bg-white/5 border border-black/5 dark:border-white/10 px-3 py-2">
<div
class="mb-2 flex items-center gap-2 rounded-xl bg-white/60 dark:bg-white/5 border border-black/5 dark:border-white/10 px-3 py-2"
>
<div class="flex-1">
{#if editMessage}
<p class="text-xs text-muted-foreground">Nachricht bearbeiten</p>
@ -269,7 +272,9 @@
<!-- Upload Progress -->
{#if uploading}
<div class="mb-2 flex items-center gap-3 rounded-xl bg-white/60 dark:bg-white/5 border border-black/5 dark:border-white/10 px-3 py-2">
<div
class="mb-2 flex items-center gap-3 rounded-xl bg-white/60 dark:bg-white/5 border border-black/5 dark:border-white/10 px-3 py-2"
>
<CircleNotch class="h-4 w-4 animate-spin text-primary" />
<div class="flex-1">
<div class="h-1.5 overflow-hidden rounded-full bg-black/10 dark:bg-white/10">
@ -285,10 +290,14 @@
<!-- Recording Indicator -->
{#if isRecording}
<div class="mb-2 flex items-center gap-3 rounded-xl bg-red-50 dark:bg-red-500/10 border border-red-200 dark:border-red-500/20 px-3 py-2">
<div
class="mb-2 flex items-center gap-3 rounded-xl bg-red-50 dark:bg-red-500/10 border border-red-200 dark:border-red-500/20 px-3 py-2"
>
<div class="h-2.5 w-2.5 rounded-full bg-red-500 animate-pulse"></div>
<p class="flex-1 text-sm font-medium text-red-700 dark:text-red-400">Aufnahme...</p>
<span class="text-sm font-mono text-red-600 dark:text-red-400">{formatDuration(recordingDuration)}</span>
<span class="text-sm font-mono text-red-600 dark:text-red-400"
>{formatDuration(recordingDuration)}</span
>
<button
class="p-1 rounded-lg hover:bg-red-100 dark:hover:bg-red-500/20 transition-colors"
onclick={cancelRecording}
@ -300,7 +309,9 @@
{/if}
<!-- Input Area -->
<div class="flex items-end gap-2 rounded-2xl bg-white/80 dark:bg-white/10 backdrop-blur-xl border border-black/5 dark:border-white/10 p-2 shadow-lg">
<div
class="flex items-end gap-2 rounded-2xl bg-white/80 dark:bg-white/10 backdrop-blur-xl border border-black/5 dark:border-white/10 p-2 shadow-lg"
>
<!-- Attachment button with custom dropdown -->
<div class="relative">
<button
@ -320,19 +331,27 @@
aria-label="Menü schließen"
></button>
<!-- Dropdown menu -->
<div class="absolute bottom-full left-0 mb-2 z-50 w-44 rounded-xl bg-white dark:bg-zinc-800 border border-black/10 dark:border-white/10 p-1.5 shadow-xl">
<div
class="absolute bottom-full left-0 mb-2 z-50 w-44 rounded-xl bg-white dark:bg-zinc-800 border border-black/10 dark:border-white/10 p-1.5 shadow-xl"
>
<button
onclick={() => { openFilePicker(); showAttachMenu = false; }}
onclick={() => {
openFilePicker();
showAttachMenu = false;
}}
class="flex items-center gap-2 w-full px-3 py-2 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors text-sm"
>
<Image class="h-4 w-4" />
Bild oder Video
</button>
<button
onclick={() => { openFilePicker(); showAttachMenu = false; }}
onclick={() => {
openFilePicker();
showAttachMenu = false;
}}
class="flex items-center gap-2 w-full px-3 py-2 rounded-lg hover:bg-black/5 dark:hover:bg-white/10 transition-colors text-sm"
>
<File class="h-4 w-4" />
<FileIcon class="h-4 w-4" />
Datei
</button>
</div>

View file

@ -20,15 +20,11 @@
let search = $state('');
let filteredDirectRooms = $derived(
matrixStore.directRooms.filter((room) =>
room.name.toLowerCase().includes(search.toLowerCase())
)
matrixStore.directRooms.filter((room) => room.name.toLowerCase().includes(search.toLowerCase()))
);
let filteredGroupRooms = $derived(
matrixStore.groupRooms.filter((room) =>
room.name.toLowerCase().includes(search.toLowerCase())
)
matrixStore.groupRooms.filter((room) => room.name.toLowerCase().includes(search.toLowerCase()))
);
let filteredInvites = $derived(
@ -70,10 +66,14 @@
<!-- Invites Section -->
{#if filteredInvites.length > 0}
<div class="mb-4">
<div class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground">
<div
class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground"
>
<Envelope class="h-3.5 w-3.5" />
Einladungen
<span class="px-1.5 py-0.5 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 text-white text-[10px]">
<span
class="px-1.5 py-0.5 rounded-full bg-gradient-to-r from-amber-500 to-orange-500 text-white text-[10px]"
>
{filteredInvites.length}
</span>
</div>
@ -128,7 +128,9 @@
<!-- Direct Messages Section -->
{#if filteredDirectRooms.length > 0 || !search}
<div class="mb-2">
<div class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground">
<div
class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground"
>
<ChatCircle class="h-3.5 w-3.5" />
Direktnachrichten
{#if matrixStore.directRooms.length > 0}
@ -154,7 +156,9 @@
<!-- Group Rooms Section -->
{#if filteredGroupRooms.length > 0 || !search}
<div class="mb-2">
<div class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground">
<div
class="flex items-center gap-2 px-2 py-2 text-xs font-semibold uppercase text-muted-foreground"
>
<Users class="h-3.5 w-3.5" />
Räume
{#if matrixStore.groupRooms.length > 0}
@ -187,7 +191,7 @@
</div>
<!-- New Room Button -->
<div class="border-t border-black/10 dark:border-white/10 p-3">
<div class="border-t border-black/10 dark:border-white/10 p-3 pb-20">
<button
class="w-full flex items-center justify-center gap-2 px-4 py-2.5 rounded-xl
bg-gradient-to-r from-violet-500 to-purple-600 text-white font-medium

View file

@ -78,7 +78,7 @@
});
</script>
<div class="relative flex-1 overflow-hidden">
<div class="relative flex-1 min-h-0 overflow-hidden">
<div
bind:this={container}
onscroll={handleScroll}

View file

@ -279,15 +279,11 @@
.main-content {
flex: 1;
min-height: 0;
overflow: hidden;
transition: all 300ms ease;
}
/* Floating nav mode - add bottom padding for bottom nav */
.main-content.floating-mode {
padding-bottom: 80px;
}
/* Sidebar mode - add left padding for sidebar nav */
.main-content.sidebar-mode {
padding-left: 180px;

View file

@ -32,10 +32,10 @@
}
</script>
<div class="chat-layout flex h-full overflow-hidden bg-background">
<div class="chat-layout flex h-full min-h-0 overflow-hidden bg-background">
<!-- Sidebar -->
<aside
class="flex w-80 flex-shrink-0 flex-col border-r border-black/10 dark:border-white/10 bg-white/50 dark:bg-white/5 backdrop-blur-sm transition-all duration-300 ease-in-out pb-20"
class="flex w-80 flex-shrink-0 flex-col border-r border-black/10 dark:border-white/10 bg-white/50 dark:bg-white/5 backdrop-blur-sm transition-all duration-300 ease-in-out"
class:hidden={!sidebarOpen}
class:lg:flex={true}
>
@ -65,13 +65,13 @@
</div>
<!-- Room List -->
<div class="flex-1 overflow-hidden">
<div class="flex-1 min-h-0 overflow-hidden">
<RoomList onCreateRoom={() => (showCreateRoom = true)} />
</div>
</aside>
<!-- Main Chat Area -->
<main class="flex flex-1 flex-col overflow-hidden bg-background">
<main class="flex flex-1 min-h-0 flex-col overflow-hidden bg-background">
{#if matrixStore.currentRoom}
<!-- Room Header -->
<RoomHeader onMenuClick={toggleSidebar} onInfoClick={() => (showRoomSettings = true)} />
@ -88,7 +88,9 @@
/>
{:else}
<!-- No Room Selected -->
<div class="flex flex-1 flex-col items-center justify-center gap-4 p-8 text-muted-foreground">
<div
class="flex flex-1 flex-col items-center justify-center gap-4 p-8 pb-24 text-muted-foreground"
>
<div class="p-4 rounded-2xl bg-gradient-to-br from-violet-500/20 to-purple-600/20">
<ChatCircle class="h-12 w-12 text-violet-500" />
</div>

View file

@ -66,6 +66,9 @@ const inventorySvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fi
// Questions icon (question mark with magnifying glass)
const questionsSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#questionsGrad)"/><circle cx="480" cy="440" r="180" stroke="white" stroke-width="40"/><path d="M620 580L740 700" stroke="white" stroke-width="48" stroke-linecap="round"/><path d="M440 360C440 332 462 310 490 310C520 310 550 330 550 370C550 420 490 430 490 480" stroke="white" stroke-width="32" stroke-linecap="round"/><circle cx="490" cy="540" r="20" fill="white"/><defs><linearGradient id="questionsGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
// Matrix icon (network/federated chat with purple gradient)
const matrixSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#matrixGrad)"/><circle cx="512" cy="400" r="80" fill="white"/><circle cx="340" cy="580" r="60" fill="white" fill-opacity="0.8"/><circle cx="684" cy="580" r="60" fill="white" fill-opacity="0.8"/><circle cx="420" cy="720" r="50" fill="white" fill-opacity="0.6"/><circle cx="604" cy="720" r="50" fill="white" fill-opacity="0.6"/><path d="M512 480V640M512 640L420 700M512 640L604 700" stroke="white" stroke-width="16" stroke-linecap="round"/><path d="M450 440L370 540M574 440L654 540" stroke="white" stroke-width="16" stroke-linecap="round"/><path d="M340 640L400 700M684 640L624 700" stroke="white" stroke-width="12" stroke-linecap="round" stroke-opacity="0.6"/><defs><linearGradient id="matrixGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
/**
* App icons as data URLs
* Use these directly in <img src={APP_ICONS.memoro}> or CSS background-image
@ -90,6 +93,7 @@ export const APP_ICONS = {
mail: svgToDataUrl(mailSvg),
inventory: svgToDataUrl(inventorySvg),
questions: svgToDataUrl(questionsSvg),
matrix: svgToDataUrl(matrixSvg),
} as const;
export type AppIconId = keyof typeof APP_ICONS;

View file

@ -308,6 +308,22 @@ export const MANA_APPS: ManaApp[] = [
comingSoon: false,
status: 'development',
},
{
id: 'matrix',
name: 'Mana Matrix',
description: {
de: 'Matrix Chat Client',
en: 'Matrix Chat Client',
},
longDescription: {
de: 'Verbinde dich mit dem dezentralen Matrix-Netzwerk für sichere, föderierte Kommunikation.',
en: 'Connect to the decentralized Matrix network for secure, federated communication.',
},
icon: APP_ICONS.matrix,
color: '#8b5cf6',
comingSoon: false,
status: 'development',
},
];
/**
@ -395,6 +411,7 @@ export const APP_URLS: Record<AppIconId, { dev: string; prod: string }> = {
mail: { dev: 'http://localhost:5186', prod: 'https://mail.manacore.app' },
inventory: { dev: 'http://localhost:5188', prod: 'https://inventory.manacore.app' },
questions: { dev: 'http://localhost:5111', prod: 'https://questions.manacore.app' },
matrix: { dev: 'http://localhost:5180', prod: 'https://matrix.mana.how' },
};
/**