feat: web UI chat render, panels, presence + analytics

This commit is contained in:
hh
2026-05-31 19:41:37 +02:00
parent 75425d1bee
commit ed469ba8dd
83 changed files with 6034 additions and 136 deletions
+91 -18
View File
@@ -1,12 +1,19 @@
<script lang="ts">
import AccountSwitcher from "$lib/components/AccountSwitcher.svelte";
import { cubicOut } from "svelte/easing";
import { fly } from "svelte/transition";
import ChatList from "$lib/components/ChatList.svelte";
import FolderTabs from "$lib/components/FolderTabs.svelte";
import RightColumn from "$lib/components/RightColumn.svelte";
import SearchInput from "$lib/components/search/SearchInput.svelte";
import SearchResults from "$lib/components/search/SearchResults.svelte";
import Settings from "$lib/components/settings/Settings.svelte";
import Button from "$lib/components/ui/Button.svelte";
import Icon from "$lib/components/ui/Icon.svelte";
import { accounts } from "$lib/stores/accounts.svelte";
import { theme } from "$lib/stores/theme.svelte";
import { events } from "$lib/stores/events.svelte";
import { search } from "$lib/stores/search.svelte";
import { toasts } from "$lib/stores/toasts.svelte";
import { ui } from "$lib/stores/ui.svelte";
let { children } = $props();
@@ -15,30 +22,72 @@
accounts.load().catch(() => toasts.error("Failed to load accounts"));
}
});
$effect(() => {
events.open(accounts.selectedId);
});
</script>
<div id="Main">
<div id="Main" class:with-right={ui.rightPanel !== null}>
<div id="LeftColumn">
<header class="left-header">
<AccountSwitcher />
<Button
variant="translucent"
round
smaller
onclick={() => theme.toggle()}
aria-label="Toggle theme"
{#if ui.leftView === "settings"}
<div
class="left-view"
in:fly={{ x: 64, duration: 200, easing: cubicOut }}
>
<Icon name={theme.resolved === "dark" ? "sun" : "moon"} />
</Button>
</header>
<div class="folder-tabs">
<FolderTabs />
</div>
<ChatList />
<Settings />
</div>
{:else}
<div
class="left-view"
in:fly={{ x: -64, duration: 200, easing: cubicOut }}
>
<header class="left-header">
{#if search.active}
<Button
variant="translucent"
round
smaller
onclick={() => search.close()}
aria-label="Назад"
>
<Icon name="arrow-left" />
</Button>
{:else}
<Button
variant="translucent"
round
smaller
onclick={() => ui.openSettings()}
aria-label="Меню"
>
<Icon name="menu" />
</Button>
{/if}
<SearchInput />
</header>
{#if search.active && search.trimmed}
<SearchResults />
{:else}
<div class="folder-tabs">
<FolderTabs />
</div>
<ChatList />
{/if}
</div>
{/if}
</div>
<div id="MiddleColumn">
{@render children()}
</div>
{#if ui.rightPanel !== null}
<div
id="RightColumn"
transition:fly={{ x: 320, duration: 200, easing: cubicOut }}
>
<RightColumn />
</div>
{/if}
</div>
<style lang="scss">
@@ -49,6 +98,10 @@
overflow: hidden;
height: 100%;
&.with-right {
grid-template-columns: minmax(16rem, 26.5rem) 1fr minmax(20rem, 25rem);
}
}
#LeftColumn {
@@ -62,6 +115,15 @@
background-color: var(--color-background);
}
.left-view {
display: flex;
flex: 1;
flex-direction: column;
overflow: hidden;
min-height: 0;
}
.left-header {
display: flex;
align-items: center;
@@ -83,4 +145,15 @@
height: 100%;
background-color: var(--color-background-secondary);
}
#RightColumn {
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
border-left: 1px solid var(--color-borders);
background-color: var(--color-background);
}
</style>