From e7cfcd409fc951fec2225da9579e6d3f3781010d Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 19 Jun 2026 10:45:29 +0200 Subject: [PATCH 01/64] chore: `npm audit fix --force` --- tools/ui/package-lock.json | 14 +++++++------- tools/ui/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/ui/package-lock.json b/tools/ui/package-lock.json index 9d0cdfea6cb..9dce3a0c9d4 100644 --- a/tools/ui/package-lock.json +++ b/tools/ui/package-lock.json @@ -35,7 +35,7 @@ "bits-ui": "2.18.1", "clsx": "2.1.1", "dexie": "4.4.3", - "dompurify": "3.4.5", + "dompurify": "3.4.11", "eslint": "9.39.4", "eslint-config-prettier": "10.1.8", "eslint-plugin-storybook": "10.4.2", @@ -8653,9 +8653,9 @@ "peer": true }, "node_modules/dompurify": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.5.tgz", - "integrity": "sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", "dev": true, "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { @@ -10226,9 +10226,9 @@ } }, "node_modules/hono": { - "version": "4.12.23", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", - "integrity": "sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==", + "version": "4.12.26", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.26.tgz", + "integrity": "sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==", "dev": true, "license": "MIT", "engines": { diff --git a/tools/ui/package.json b/tools/ui/package.json index 4803922889a..bcb4165d101 100644 --- a/tools/ui/package.json +++ b/tools/ui/package.json @@ -54,7 +54,7 @@ "bits-ui": "2.18.1", "clsx": "2.1.1", "dexie": "4.4.3", - "dompurify": "3.4.5", + "dompurify": "3.4.11", "eslint": "9.39.4", "eslint-config-prettier": "10.1.8", "eslint-plugin-storybook": "10.4.2", From b1c51390ef420bfe7a7ac98edad17af1e6e961d5 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 19 Jun 2026 12:45:22 +0200 Subject: [PATCH 02/64] feat: Update sidebar toggle to use Logo --- .../components/app/actions/ActionIcon.svelte | 17 +++++++------- .../src/lib/components/app/misc/Logo.svelte | 17 ++++++++++++++ tools/ui/src/lib/components/app/misc/index.ts | 8 +++++++ .../SidebarNavigation.svelte | 7 +++--- .../SidebarNavigationActions.svelte | 2 +- .../ui/sidebar/sidebar-trigger.svelte | 22 +++++++++++++------ tools/ui/static/favicon-dark.svg | 14 ------------ 7 files changed, 54 insertions(+), 33 deletions(-) create mode 100644 tools/ui/src/lib/components/app/misc/Logo.svelte delete mode 100644 tools/ui/static/favicon-dark.svg diff --git a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte index f156df66998..d471447f956 100644 --- a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte +++ b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte @@ -10,7 +10,7 @@ disabled?: boolean; icon: Component; iconSize?: string; - onclick: (e?: MouseEvent) => void; + onclick?: (e?: MouseEvent) => void; size?: ButtonSize; stopPropagationOnClick?: boolean; tooltip: string; @@ -37,24 +37,25 @@ {#snippet child({ props })} - + {/snippet} diff --git a/tools/ui/src/lib/components/app/misc/Logo.svelte b/tools/ui/src/lib/components/app/misc/Logo.svelte new file mode 100644 index 00000000000..719337c64cf --- /dev/null +++ b/tools/ui/src/lib/components/app/misc/Logo.svelte @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/tools/ui/src/lib/components/app/misc/index.ts b/tools/ui/src/lib/components/app/misc/index.ts index 64b76fb711d..b550ae66a53 100644 --- a/tools/ui/src/lib/components/app/misc/index.ts +++ b/tools/ui/src/lib/components/app/misc/index.ts @@ -51,3 +51,11 @@ export { default as KeyboardShortcutInfo } from './KeyboardShortcutInfo.svelte'; * Preview button is shown only for HTML code blocks. */ export { default as CodeBlockActions } from './CodeBlockActions.svelte'; + +/** + * **Logo** - Application brand mark + * + * Inline SVG of the application logo. Accepts styling via the standard + * `class` and `style` props and inherits color via `currentColor`. + */ +export { default as Logo } from './Logo.svelte'; diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index 1fa7722f240..7dd56bcfa66 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -3,7 +3,7 @@ import { page } from '$app/state'; import { Trash2, Pencil, Pin, X } from '@lucide/svelte'; import { Button } from '$lib/components/ui/button'; - import { DialogConfirmation } from '$lib/components/app'; + import { DialogConfirmation, Logo } from '$lib/components/app'; import SidebarNavigationActions from './SidebarNavigationActions.svelte'; import SidebarNavigationConversationItem from './SidebarNavigationConversationItem.svelte'; import { Checkbox } from '$lib/components/ui/checkbox'; @@ -182,9 +182,10 @@
-

+ + diff --git a/tools/ui/static/favicon-dark.svg b/tools/ui/static/favicon-dark.svg deleted file mode 100644 index 22cab8d25a9..00000000000 --- a/tools/ui/static/favicon-dark.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - From de72864e4a6e108d49b48653e6a7e9164d8520a1 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 19 Jun 2026 14:07:02 +0200 Subject: [PATCH 03/64] refactor: Clean up favicon SVG --- tools/ui/static/favicon.svg | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/tools/ui/static/favicon.svg b/tools/ui/static/favicon.svg index 52664fc8b40..f8c2a7c0e5d 100644 --- a/tools/ui/static/favicon.svg +++ b/tools/ui/static/favicon.svg @@ -1,14 +1,7 @@ - - - - - - - - - - - - + + + + + From 152b9bcfb86f8e604294f8ff781d831b0906b637 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 19 Jun 2026 14:46:45 +0200 Subject: [PATCH 04/64] feat: Refactor logo component and implement theme-aware favicon generation --- tools/ui/.gitignore | 3 +- tools/ui/pwa-assets-dark.config.ts | 4 ++ tools/ui/pwa-assets.config.ts | 5 +- tools/ui/scripts/favicon-colorize.ts | 56 +++++++++++++++++++ .../favicon.svg => src/lib/assets/logo.svg} | 2 +- .../src/lib/components/app/misc/Logo.svelte | 21 +++---- .../SidebarNavigation.svelte | 2 +- tools/ui/src/lib/constants/pwa.ts | 7 +++ tools/ui/tests/unit/favicon-colorize.test.ts | 52 +++++++++++++++++ 9 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 tools/ui/scripts/favicon-colorize.ts rename tools/ui/{static/favicon.svg => src/lib/assets/logo.svg} (93%) create mode 100644 tools/ui/tests/unit/favicon-colorize.test.ts diff --git a/tools/ui/.gitignore b/tools/ui/.gitignore index ddcfe2e60f9..0bb8c9b3c21 100644 --- a/tools/ui/.gitignore +++ b/tools/ui/.gitignore @@ -28,10 +28,9 @@ vite.config.ts.timestamp-* # PWA Artifacts apple-splash-*.png apple-touch-icon-*.png -favicon.ico -favicon-dark.ico maskable-icon-*.png pwa-*.png +static/favicon* # Storybook *storybook.log diff --git a/tools/ui/pwa-assets-dark.config.ts b/tools/ui/pwa-assets-dark.config.ts index e9793bca9ea..89a34035334 100644 --- a/tools/ui/pwa-assets-dark.config.ts +++ b/tools/ui/pwa-assets-dark.config.ts @@ -1,4 +1,8 @@ import { defineConfig } from '@vite-pwa/assets-generator/config'; +import { FAVICON_COLORS } from './src/lib/constants/pwa'; +import { writeThemeFavicons } from './scripts/favicon-colorize'; + +writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK); export default defineConfig({ headLinkOptions: { diff --git a/tools/ui/pwa-assets.config.ts b/tools/ui/pwa-assets.config.ts index 54928eeb4a8..316679fefed 100644 --- a/tools/ui/pwa-assets.config.ts +++ b/tools/ui/pwa-assets.config.ts @@ -5,8 +5,11 @@ import { } from '@vite-pwa/assets-generator/config'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; -import { THEME_COLORS, PWA_GENERATOR_DEVICES, PWA_ASSET_GENERATOR } from './src/lib/constants/pwa'; +import { THEME_COLORS, PWA_GENERATOR_DEVICES, PWA_ASSET_GENERATOR, FAVICON_COLORS } from './src/lib/constants/pwa'; import { SplashOrientation } from './src/lib/enums/splash.enums'; +import { writeThemeFavicons } from './scripts/favicon-colorize'; + +writeThemeFavicons(FAVICON_COLORS.LIGHT, FAVICON_COLORS.DARK); export default defineConfig({ headLinkOptions: { diff --git a/tools/ui/scripts/favicon-colorize.ts b/tools/ui/scripts/favicon-colorize.ts new file mode 100644 index 00000000000..e7015207d4a --- /dev/null +++ b/tools/ui/scripts/favicon-colorize.ts @@ -0,0 +1,56 @@ +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const PROJECT_ROOT = resolve(HERE, '..'); + +const DEFAULT_LOGO = resolve(PROJECT_ROOT, 'src/lib/assets/logo.svg'); +const DEFAULT_OUT_DIR = resolve(PROJECT_ROOT, 'static'); +const DEFAULT_OUT_LIGHT = resolve(DEFAULT_OUT_DIR, 'favicon.svg'); +const DEFAULT_OUT_DARK = resolve(DEFAULT_OUT_DIR, 'favicon-dark.svg'); + +const CURRENT_COLOR = 'currentColor'; + +export interface ColorizedFavicon { + light: string; + dark: string; +} + +export interface WriteThemeFaviconsOptions { + sourcePath?: string; + lightOutPath?: string; + darkOutPath?: string; +} + +/** + * Replace every `currentColor` occurrence in the SVG with the given color. + * Pure: no filesystem access, so it is straightforward to unit-test. + */ +export function colorizeFaviconSvg(svg: string, lightColor: string, darkColor: string): ColorizedFavicon { + return { + light: svg.replaceAll(CURRENT_COLOR, lightColor), + dark: svg.replaceAll(CURRENT_COLOR, darkColor) + }; +} + +/** + * Read `src/lib/assets/logo.svg`, colorize it for both themes, and write + * the results to the static directory so the PWA asset generator can consume + * them. Paths can be overridden for tests. + */ +export function writeThemeFavicons( + lightColor: string, + darkColor: string, + { + sourcePath = DEFAULT_LOGO, + lightOutPath = DEFAULT_OUT_LIGHT, + darkOutPath = DEFAULT_OUT_DARK + }: WriteThemeFaviconsOptions = {} +): void { + const source = readFileSync(sourcePath, 'utf-8'); + const { light, dark } = colorizeFaviconSvg(source, lightColor, darkColor); + mkdirSync(dirname(lightOutPath), { recursive: true }); + writeFileSync(lightOutPath, light); + writeFileSync(darkOutPath, dark); +} diff --git a/tools/ui/static/favicon.svg b/tools/ui/src/lib/assets/logo.svg similarity index 93% rename from tools/ui/static/favicon.svg rename to tools/ui/src/lib/assets/logo.svg index f8c2a7c0e5d..05424790afe 100644 --- a/tools/ui/static/favicon.svg +++ b/tools/ui/src/lib/assets/logo.svg @@ -1,5 +1,5 @@ - + diff --git a/tools/ui/src/lib/components/app/misc/Logo.svelte b/tools/ui/src/lib/components/app/misc/Logo.svelte index 719337c64cf..d067eb375ae 100644 --- a/tools/ui/src/lib/components/app/misc/Logo.svelte +++ b/tools/ui/src/lib/components/app/misc/Logo.svelte @@ -1,17 +1,18 @@ - - - - - - - + {@html logoMark} +

+ + diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index 7dd56bcfa66..855ac4263b4 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -185,7 +185,7 @@ - + - {/snippet} - + + {/if} + +{/snippet} - -

{tooltip}

-
- +{#if tooltip} + + + + {#snippet child({ props })} + {@render button(props)} + {/snippet} + + + +

{tooltip}

+
+
+{:else} + {@render button({ href })} +{/if} diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index e733a64a97a..6c47b0f67fe 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -389,7 +389,7 @@
- import { goto } from '$app/navigation'; - import { page } from '$app/state'; - import { ActionIcon } from '$lib/components/app'; - import { - ICON_STRIP_TRANSITION_DURATION, - ICON_STRIP_TRANSITION_DELAY_MULTIPLIER, - SIDEBAR_ACTIONS_ITEMS - } from '$lib/constants'; - import { TooltipSide } from '$lib/enums'; - import { fade } from 'svelte/transition'; - import { circIn } from 'svelte/easing'; - import { onMount } from 'svelte'; - import { useKeyboardShortcuts } from '$lib/hooks/use-keyboard-shortcuts.svelte'; - - interface Props { - sidebarOpen: boolean; - onSearchClick: () => void; - } - - let { sidebarOpen = false, onSearchClick }: Props = $props(); - - const { handleKeydown } = useKeyboardShortcuts({ activateSearchMode: () => onSearchClick() }); - - let initialized = $state(false); - let showIcons = $derived(!sidebarOpen); - - showIcons = false; - - onMount(() => { - showIcons = !sidebarOpen; - - setTimeout(() => { - initialized = true; - }, ICON_STRIP_TRANSITION_DELAY_MULTIPLIER * SIDEBAR_ACTIONS_ITEMS.length); - }); - - - - - - diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index 324ef655bd5..d11ae0c8d6b 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -1,161 +1,116 @@ -
- - -
- - - - - - -
- - -
+{#snippet itemIcon(IconComponent: Component)} + +{/snippet} - -
-
+ - { - showDeleteDialog = false; - selectedConversation = null; - }} + diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte index 8c75e08ac6b..94f226eafba 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte @@ -1,39 +1,50 @@ @@ -41,56 +52,58 @@ {/snippet} -
- {#if isSearchModeActive} - e.key === 'Escape' && handleSearchModeDeactivate()} - placeholder="Search conversations..." - {isCancelAlwaysVisible} - /> - {:else} - {#each SIDEBAR_ACTIONS_ITEMS as item (item.route)} - {#if !item.route} - - {:else} + +
- {/if} - {/each} - {/if} +
+ + {/each}
diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte index ea3ff843db2..6bbe7606c93 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte @@ -2,7 +2,6 @@ import { Pin } from '@lucide/svelte'; import { buildConversationTree } from '$lib/stores/conversations.svelte'; import SidebarNavigationConversationItem from './SidebarNavigationConversationItem.svelte'; - import * as Sidebar from '$lib/components/ui/sidebar'; interface Props { filteredConversations: DatabaseConversation[]; @@ -48,51 +47,52 @@ {#if !isSearchModeActive && pinnedConversations.length > 0} - - -
- - Pinned -
-
- - - {#each pinnedConversations as { conversation, depth } (conversation.id)} - - - - {/each} - - -
+
+
+ + Pinned +
+
+ +
    + {#each pinnedConversations as { conversation, depth } (conversation.id)} +
  • + +
  • + {/each} +
{/if} - +
{#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive} - +
{isSearchModeActive ? 'Search results' : 'Recent conversations'} - +
{/if} - - +
+
    {#each visibleItems as { conversation, depth } (conversation.id)} - +
  • - +
  • {/each} {#if visibleItems.length === 0} @@ -119,6 +119,6 @@

{/if} -
-
- + +
+
diff --git a/tools/ui/src/lib/components/app/navigation/index.ts b/tools/ui/src/lib/components/app/navigation/index.ts index d4ca914594c..66dea9311ea 100644 --- a/tools/ui/src/lib/components/app/navigation/index.ts +++ b/tools/ui/src/lib/components/app/navigation/index.ts @@ -63,15 +63,6 @@ export { default as DropdownMenuSearchable } from './DropdownMenuSearchable.svel * ``` */ export { default as DropdownMenuActions } from './DropdownMenuActions.svelte'; - -/** - * **DesktopIconStrip** - Fixed icon strip for desktop sidebar - * - * Vertical icon strip shown on desktop when the sidebar is collapsed. - * Contains navigation shortcuts for new chat, search, MCP, import/export, and settings. - */ -export { default as DesktopIconStrip } from './DesktopIconStrip.svelte'; - /** * **SidebarNavigation** - Sidebar with actions menu and conversation list * @@ -120,7 +111,7 @@ export { default as SidebarNavigation } from './SidebarNavigation/SidebarNavigat * and delete all conversations button. Manages dialog states for settings and * delete confirmation. */ -export { default as SidebarNavigationActions } from './SidebarNavigation/SidebarNavigationActions.svelte'; +// export { default as SidebarNavigationActions } from './SidebarNavigation/SidebarNavigationActions.svelte'; /** * Single conversation item in sidebar. Displays conversation title (truncated), @@ -130,6 +121,30 @@ export { default as SidebarNavigationActions } from './SidebarNavigation/Sidebar */ export { default as SidebarNavigationConversationItem } from './SidebarNavigation/SidebarNavigationConversationItem.svelte'; +/** + * **SidebarNavigationConversationList** - Grouped conversation list + * + * Pure-presentational list of conversations. Splits items into a Pinned + * section (when not in search mode) and a Recent Conversations / Search + * Results section with the unpinned items. Item selection, edit, delete, + * and stop-generation are delegated to the caller via callbacks. + * + * @example + * ```svelte + * + * ``` + */ +export { default as SidebarNavigationConversationList } from './SidebarNavigation/SidebarNavigationConversationList.svelte'; + /** * Search input for filtering conversations in sidebar. Filters conversation * list by title as user types. Shows clear button when query is not empty. diff --git a/tools/ui/src/lib/components/app/settings/SettingsMcpServers.svelte b/tools/ui/src/lib/components/app/settings/SettingsMcpServers.svelte index 5c801fa2c27..f3e1d97c0a4 100644 --- a/tools/ui/src/lib/components/app/settings/SettingsMcpServers.svelte +++ b/tools/ui/src/lib/components/app/settings/SettingsMcpServers.svelte @@ -55,7 +55,7 @@
-
+

MCP Servers

diff --git a/tools/ui/src/lib/components/ui/sidebar/constants.ts b/tools/ui/src/lib/components/ui/sidebar/constants.ts deleted file mode 100644 index 8c320c2ca8f..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const SIDEBAR_COOKIE_NAME = 'sidebar:state'; -export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; -export const SIDEBAR_MIN_WIDTH = '18rem'; -export const SIDEBAR_MAX_WIDTH = '32rem'; -export const SIDEBAR_WIDTH_MOBILE = '18rem'; -export const SIDEBAR_WIDTH_ICON = '3rem'; -export const SIDEBAR_KEYBOARD_SHORTCUT = 'b'; diff --git a/tools/ui/src/lib/components/ui/sidebar/context.svelte.ts b/tools/ui/src/lib/components/ui/sidebar/context.svelte.ts deleted file mode 100644 index 2fa5cc25dcb..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/context.svelte.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { isMobile } from '$lib/stores/viewport.svelte.js'; -import { getContext, setContext } from 'svelte'; -import { SIDEBAR_KEYBOARD_SHORTCUT, SIDEBAR_MIN_WIDTH } from './constants.js'; - -type Getter = () => T; - -export type SidebarStateProps = { - /** - * A getter function that returns the current open state of the sidebar. - * We use a getter function here to support `bind:open` on the `Sidebar.Provider` - * component. - */ - open: Getter; - - /** - * A function that sets the open state of the sidebar. To support `bind:open`, we need - * a source of truth for changing the open state to ensure it will be synced throughout - * the sub-components and any `bind:` references. - */ - setOpen: (open: boolean) => void; -}; - -class SidebarState { - readonly props: SidebarStateProps; - open = $derived.by(() => this.props.open()); - openMobile = $state(false); - sidebarWidth = $state(SIDEBAR_MIN_WIDTH); - isResizing = $state(false); - setOpen: SidebarStateProps['setOpen']; - state = $derived.by(() => (this.open ? 'expanded' : 'collapsed')); - - constructor(props: SidebarStateProps) { - this.setOpen = props.setOpen; - this.props = props; - } - - // Convenience getter for checking if the sidebar is mobile - // without this, we would need to use `sidebar.isMobile.current` everywhere - get isMobile() { - return isMobile.current; - } - - // Event handler to apply to the `` - handleShortcutKeydown = (e: KeyboardEvent) => { - if (e.key === SIDEBAR_KEYBOARD_SHORTCUT && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - this.toggle(); - } - }; - - setOpenMobile = (value: boolean) => { - this.openMobile = value; - }; - - toggle = () => { - this.setOpen(!this.open); - }; -} - -const SYMBOL_KEY = 'scn-sidebar'; - -/** - * Instantiates a new `SidebarState` instance and sets it in the context. - * - * @param props The constructor props for the `SidebarState` class. - * @returns The `SidebarState` instance. - */ -export function setSidebar(props: SidebarStateProps): SidebarState { - return setContext(Symbol.for(SYMBOL_KEY), new SidebarState(props)); -} - -/** - * Retrieves the `SidebarState` instance from the context. This is a class instance, - * so you cannot destructure it. - * @returns The `SidebarState` instance. - */ -export function useSidebar(): SidebarState { - return getContext(Symbol.for(SYMBOL_KEY)); -} diff --git a/tools/ui/src/lib/components/ui/sidebar/index.ts b/tools/ui/src/lib/components/ui/sidebar/index.ts deleted file mode 100644 index 280e64079e2..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/index.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { useSidebar } from './context.svelte.js'; -import Content from './sidebar-content.svelte'; -import Footer from './sidebar-footer.svelte'; -import GroupAction from './sidebar-group-action.svelte'; -import GroupContent from './sidebar-group-content.svelte'; -import GroupLabel from './sidebar-group-label.svelte'; -import Group from './sidebar-group.svelte'; -import Header from './sidebar-header.svelte'; -import Input from './sidebar-input.svelte'; -import Inset from './sidebar-inset.svelte'; -import MenuAction from './sidebar-menu-action.svelte'; -import MenuBadge from './sidebar-menu-badge.svelte'; -import MenuButton from './sidebar-menu-button.svelte'; -import MenuItem from './sidebar-menu-item.svelte'; -import MenuSkeleton from './sidebar-menu-skeleton.svelte'; -import MenuSubButton from './sidebar-menu-sub-button.svelte'; -import MenuSubItem from './sidebar-menu-sub-item.svelte'; -import MenuSub from './sidebar-menu-sub.svelte'; -import Menu from './sidebar-menu.svelte'; -import Provider from './sidebar-provider.svelte'; -import Rail from './sidebar-rail.svelte'; -import Separator from './sidebar-separator.svelte'; -import Trigger from './sidebar-trigger.svelte'; -import Root from './sidebar.svelte'; - -export { - Content, - Footer, - Group, - GroupAction, - GroupContent, - GroupLabel, - Header, - Input, - Inset, - Menu, - MenuAction, - MenuBadge, - MenuButton, - MenuItem, - MenuSkeleton, - MenuSub, - MenuSubButton, - MenuSubItem, - Provider, - Rail, - Root, - Separator, - // - Root as Sidebar, - Content as SidebarContent, - Footer as SidebarFooter, - Group as SidebarGroup, - GroupAction as SidebarGroupAction, - GroupContent as SidebarGroupContent, - GroupLabel as SidebarGroupLabel, - Header as SidebarHeader, - Input as SidebarInput, - Inset as SidebarInset, - Menu as SidebarMenu, - MenuAction as SidebarMenuAction, - MenuBadge as SidebarMenuBadge, - MenuButton as SidebarMenuButton, - MenuItem as SidebarMenuItem, - MenuSkeleton as SidebarMenuSkeleton, - MenuSub as SidebarMenuSub, - MenuSubButton as SidebarMenuSubButton, - MenuSubItem as SidebarMenuSubItem, - Provider as SidebarProvider, - Rail as SidebarRail, - Separator as SidebarSeparator, - Trigger as SidebarTrigger, - Trigger, - useSidebar -}; diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-content.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-content.svelte deleted file mode 100644 index 0e5f75e3604..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-content.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-footer.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-footer.svelte deleted file mode 100644 index 69914dfa288..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-footer.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-action.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-group-action.svelte deleted file mode 100644 index 027a711cad5..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-action.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - -{#if child} - {@render child({ props: mergedProps })} -{:else} - -{/if} diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-content.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-group-content.svelte deleted file mode 100644 index 9e018fb593f..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-content.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-label.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-group-label.svelte deleted file mode 100644 index 79f47d730f2..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-group-label.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - -{#if child} - {@render child({ props: mergedProps })} -{:else} -
- {@render children?.()} -
-{/if} diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-group.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-group.svelte deleted file mode 100644 index eed5acec81a..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-group.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-header.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-header.svelte deleted file mode 100644 index 0651550d720..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-header.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-input.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-input.svelte deleted file mode 100644 index fa5747350dd..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-input.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-inset.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-inset.svelte deleted file mode 100644 index f55d2f48619..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-inset.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte deleted file mode 100644 index ded1ffd78a8..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-action.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -{#if child} - {@render child({ props: mergedProps })} -{:else} - -{/if} diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte deleted file mode 100644 index f4525a14207..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-badge.svelte +++ /dev/null @@ -1,29 +0,0 @@ - - -
- {@render children?.()} -
diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte deleted file mode 100644 index f9efd67e992..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-button.svelte +++ /dev/null @@ -1,106 +0,0 @@ - - - - -{#snippet Button({ props }: { props?: Record })} - {@const mergedProps = mergeProps(buttonProps, props)} - {#if child} - {@render child({ props: mergedProps })} - {:else} - - {/if} -{/snippet} - -{#if !tooltipContent} - {@render Button({})} -{:else} - - - {#snippet child({ props })} - {@render Button({ props })} - {/snippet} - - - - -{/if} diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte deleted file mode 100644 index 5adbedd4d27..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-item.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
  • - {@render children?.()} -
  • diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte deleted file mode 100644 index 2b2acd6ad52..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-skeleton.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - -
    - {#if showIcon} - - {/if} - - {@render children?.()} -
    diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte deleted file mode 100644 index dabfe0fa05f..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-button.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -{#if child} - {@render child({ props: mergedProps })} -{:else} - - {@render children?.()} - -{/if} diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte deleted file mode 100644 index cca870eb986..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub-item.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
  • - {@render children?.()} -
  • diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte deleted file mode 100644 index 5458cedf9cb..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu-sub.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -
      - {@render children?.()} -
    diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-menu.svelte deleted file mode 100644 index fee96ed59b5..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-menu.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - -
      - {@render children?.()} -
    diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-provider.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-provider.svelte deleted file mode 100644 index 9fd270403ea..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-provider.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - - - -
    - {@render children?.()} -
    diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-rail.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-rail.svelte deleted file mode 100644 index cde93070978..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-rail.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-separator.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-separator.svelte deleted file mode 100644 index 8fc206580f6..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-separator.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar-trigger.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar-trigger.svelte deleted file mode 100644 index ca83131367b..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar-trigger.svelte +++ /dev/null @@ -1,51 +0,0 @@ - - - diff --git a/tools/ui/src/lib/components/ui/sidebar/sidebar.svelte b/tools/ui/src/lib/components/ui/sidebar/sidebar.svelte deleted file mode 100644 index d6da1aec78e..00000000000 --- a/tools/ui/src/lib/components/ui/sidebar/sidebar.svelte +++ /dev/null @@ -1,150 +0,0 @@ - - -{#if collapsible === 'none'} -
    - {@render children?.()} -
    -{:else} -
    - -
    - -
    -
    - {@render children?.()} -
    - - {#if side === 'left'} - - - {:else} - - - {/if} -
    -
    -{/if} diff --git a/tools/ui/src/lib/constants/ui.ts b/tools/ui/src/lib/constants/ui.ts index 6f344029f3c..b4500377039 100644 --- a/tools/ui/src/lib/constants/ui.ts +++ b/tools/ui/src/lib/constants/ui.ts @@ -1,4 +1,4 @@ -import { Settings, Search, SquarePen } from '@lucide/svelte'; +import { Search, SquarePen } from '@lucide/svelte'; import McpLogo from '$lib/components/app/mcp/McpLogo.svelte'; import type { Component } from 'svelte'; import { ROUTES } from './routes'; @@ -27,10 +27,4 @@ export const SIDEBAR_ACTIONS_ITEMS: DesktopIconStripItem[] = [ route: ROUTES.MCP_SERVERS, activeRouteId: '/mcp-servers' }, - { - icon: Settings, - tooltip: 'Settings', - route: ROUTES.SETTINGS, - activeRoutePrefix: '/settings' - } ]; diff --git a/tools/ui/src/routes/+layout.svelte b/tools/ui/src/routes/+layout.svelte index 1269692a78c..ab415dd66ab 100644 --- a/tools/ui/src/routes/+layout.svelte +++ b/tools/ui/src/routes/+layout.svelte @@ -6,18 +6,15 @@ import { page } from '$app/state'; import { untrack } from 'svelte'; import { onMount } from 'svelte'; - import { fade } from 'svelte/transition'; import { - DesktopIconStrip, + SidebarNavigation, DialogConversationTitleUpdate, - SidebarNavigation } from '$lib/components/app'; import { PwaMetaTags, PwaRefreshAlert } from '$lib/components/pwa'; import { pwaAssetsHead } from 'virtual:pwa-assets/head'; import { conversationsStore } from '$lib/stores/conversations.svelte'; - import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import * as Tooltip from '$lib/components/ui/tooltip'; import { isRouterMode, serverStore } from '$lib/stores/server.svelte'; import { config, settingsStore } from '$lib/stores/settings.svelte'; @@ -31,7 +28,6 @@ import { FAVICON_PATHS, FAVICON_SELECTORS } from '$lib/constants/pwa'; import { useKeyboardShortcuts } from '$lib/hooks/use-keyboard-shortcuts.svelte'; import { usePwa } from '$lib/hooks/use-pwa.svelte'; - import { useSettingsNavigation } from '$lib/hooks/use-settings-navigation.svelte'; import { conversations } from '$lib/stores/conversations.svelte'; import { isMobile } from '$lib/stores/viewport.svelte'; import { theme } from '$lib/stores/theme.svelte'; @@ -42,8 +38,6 @@ let { children } = $props(); let alwaysShowSidebarOnDesktop = $derived(config().alwaysShowSidebarOnDesktop); let isDesktop = $derived(!isMobile.current); - let sidebarOpen = $state(false); - let mounted = $state(false); let innerHeight = $state(); let innerWidth = $state(browser ? window.innerWidth : 0); @@ -61,7 +55,6 @@ let titleUpdateNewTitle = $state(''); let titleUpdateResolve: ((value: boolean) => void) | null = null; - const panelNav = useSettingsNavigation(); // Keep the hook object intact: destructuring needRefreshByStorage reads the getter once and freezes it const pwa = usePwa(); const { needRefresh, updateServiceWorker } = pwa; @@ -166,7 +159,6 @@ onMount(() => { updateFavicon(); - mounted = true; }); $effect(() => { @@ -177,7 +169,6 @@ $effect(() => { if (alwaysShowSidebarOnDesktop && isDesktop) { - sidebarOpen = true; return; } @@ -300,19 +291,21 @@ - -
    - {#if showBuildVersion && buildInfoStore.value} - {buildInfoStore.value} - {/if} - -
    + + { + if (chatSidebar?.activateSearchMode) { + chatSidebar.activateSearchMode(); + } + }} + /> + +
    + {@render children?.()} +
    + @@ -324,44 +317,17 @@ onConfirm={handleTitleUpdateConfirm} onCancel={handleTitleUpdateCancel} /> - - -
    - - - {#if !(alwaysShowSidebarOnDesktop && isDesktop) && !(panelNav.isSettingsRoute && !isDesktop)} - {#if mounted} -
    - -
    - {/if} - {/if} - - {#if isDesktop && !alwaysShowSidebarOnDesktop} - { - if (chatSidebar?.activateSearchMode) { - chatSidebar.activateSearchMode(); - } - - sidebarOpen = true; - }} - /> - {/if} - - - {@render children?.()} - -
    -
    - + +
    + {#if showBuildVersion && buildInfoStore.value} + {buildInfoStore.value} + {/if} + + +
    diff --git a/tools/ui/tests/client/components/TestWrapper.svelte b/tools/ui/tests/client/components/TestWrapper.svelte index aeb7ff74c4a..1380ec851bd 100644 --- a/tools/ui/tests/client/components/TestWrapper.svelte +++ b/tools/ui/tests/client/components/TestWrapper.svelte @@ -1,9 +1,6 @@ - - - + From 3d41709350eb5342bdfd187098773b0b8bb4cd21 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Fri, 19 Jun 2026 19:59:06 +0200 Subject: [PATCH 09/64] feat: WIP --- tools/ui/src/app.css | 1 - .../components/app/actions/ActionIcon.svelte | 62 ++++++------ .../app/chat/ChatScreen/ChatScreen.svelte | 94 +++++++++--------- .../ChatScreenActionScrollDown.svelte | 2 +- .../src/lib/components/app/misc/Logo.svelte | 15 ++- .../SidebarNavigation.svelte | 97 +++---------------- .../SidebarNavigationActions.svelte | 44 +++++---- .../SidebarNavigationConversationList.svelte | 15 +-- .../lib/components/app/navigation/index.ts | 1 + tools/ui/src/lib/constants/ui.ts | 3 +- tools/ui/src/routes/+layout.svelte | 26 +++-- .../stories/SidebarNavigation.stories.svelte | 28 ++---- 12 files changed, 156 insertions(+), 232 deletions(-) diff --git a/tools/ui/src/app.css b/tools/ui/src/app.css index 9254a96df74..78fe952da13 100644 --- a/tools/ui/src/app.css +++ b/tools/ui/src/app.css @@ -141,7 +141,6 @@ @apply bg-background text-foreground; scrollbar-width: thin; scrollbar-gutter: stable; - overflow: hidden; /* Added due to Mermaid rendering somehow causing the double scrollbar */ } /* Global scrollbar styling - visible only on hover */ diff --git a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte index fffc6187b79..6619e4b76f1 100644 --- a/tools/ui/src/lib/components/app/actions/ActionIcon.svelte +++ b/tools/ui/src/lib/components/app/actions/ActionIcon.svelte @@ -35,42 +35,42 @@ }: Props = $props(); -{#snippet button(props)} - + + {/if} + {/snippet} {#if tooltip} - - - - {#snippet child({ props })} - {@render button(props)} - {/snippet} - + + + + {#snippet child({ props })} + {@render button(props)} + {/snippet} + - -

    {tooltip}

    -
    -
    + +

    {tooltip}

    +
    +
    {:else} - {@render button({ href })} + {@render button({ href })} {/if} diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index 6c47b0f67fe..896401b27da 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -46,7 +46,7 @@ const autoScroll = createAutoScrollController(); let disableAutoScroll = $derived(Boolean(config().disableAutoScroll)); - let chatScrollContainer: HTMLDivElement | undefined = $state(); + let chatScrollContainer: HTMLElement | undefined = $state(); let dragCounter = $state(0); let isDragOver = $state(false); let showFileErrorDialog = $state(false); @@ -369,6 +369,7 @@ }); $effect(() => { + chatScrollContainer = document.documentElement; autoScroll.setContainer(chatScrollContainer); }); @@ -381,66 +382,61 @@ {/if} - + {#if isServerLoading} {:else}
    -
    - {#if !isEmpty} - { - autoScroll.enable(); - if (!autoScroll.userScrolledUp) { - autoScroll.scrollToBottom(); - } - }} + {#if !isEmpty} + { + autoScroll.enable(); + if (!autoScroll.userScrolledUp) { + autoScroll.scrollToBottom(); + } + }} + /> + {/if} + +
    + + + + + + + + +
    + chatStore.stopGeneration()} + onSystemPromptAdd={handleSystemPromptAdd} + bind:uploadedFiles /> - {/if} - -
    - - - - - - - - -
    - chatStore.stopGeneration()} - onSystemPromptAdd={handleSystemPromptAdd} - bind:uploadedFiles - /> -
    diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte index a22c491adac..2a3233c0555 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte @@ -3,7 +3,7 @@ import { Button } from '$lib/components/ui/button'; interface Props { - container: HTMLDivElement | undefined; + container: HTMLElement | undefined; hasProcessingInfoVisible: boolean; } diff --git a/tools/ui/src/lib/components/app/misc/Logo.svelte b/tools/ui/src/lib/components/app/misc/Logo.svelte index d067eb375ae..ff2b7f2f072 100644 --- a/tools/ui/src/lib/components/app/misc/Logo.svelte +++ b/tools/ui/src/lib/components/app/misc/Logo.svelte @@ -1,18 +1,15 @@ -
    +
    {@html logoMark}
    diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte index d11ae0c8d6b..43db251bd2c 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigation.svelte @@ -1,26 +1,20 @@ -{#snippet itemIcon(IconComponent: Component)} - -{/snippet} - diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte index 94f226eafba..7fa7bd4ba1b 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationActions.svelte @@ -15,12 +15,12 @@ import type { Component } from 'svelte'; interface Props { - isExpandedMode: boolean; + class: string; + isExpandedMode: boolean; onSearchClick?: () => void; } - let { isExpandedMode = false, onSearchClick = () => {} }: Props = $props(); - + let { class: className, isExpandedMode = false, onSearchClick = () => {} }: Props = $props(); let initialized = $state(false); let showIcons = $state(false); @@ -37,9 +37,11 @@ if (item.activeRouteId) { return page.route.id === item.activeRouteId; } + if (item.activeRoutePrefix) { return !!page.route.id?.startsWith(item.activeRoutePrefix); } + return false; } @@ -52,37 +54,41 @@ {/snippet} -
    - +
    {#each SIDEBAR_ACTIONS_ITEMS as item, i (item.tooltip)} {@const isActive = isItemActive(item)} {@const itemOnClick = getItemOnClick(item)} {@const itemTransition = { duration: ICON_STRIP_TRANSITION_DURATION, delay: !initialized - ? ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + - i * ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + ? ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + i * ICON_STRIP_TRANSITION_DELAY_MULTIPLIER : 0, easing: circIn }} - + {#if showIcons}
    - + {/if} {/each}
    diff --git a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte index 6bbe7606c93..512994cf1bc 100644 --- a/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte +++ b/tools/ui/src/lib/components/app/navigation/SidebarNavigation/SidebarNavigationConversationList.svelte @@ -4,6 +4,7 @@ import SidebarNavigationConversationItem from './SidebarNavigationConversationItem.svelte'; interface Props { + class: string; filteredConversations: DatabaseConversation[]; currentChatId: string | undefined; isSearchModeActive: boolean; @@ -15,6 +16,7 @@ } let { + class: className, filteredConversations, currentChatId, isSearchModeActive, @@ -47,16 +49,17 @@ {#if !isSearchModeActive && pinnedConversations.length > 0} -
    -
    +
    +
    + Pinned
    -
      +
        {#each pinnedConversations as { conversation, depth } (conversation.id)}
      • {/if} -
        +
        {#if (filteredConversations.length > 0 && isSearchModeActive) || !isSearchModeActive}
        { if (alwaysShowSidebarOnDesktop && isDesktop) { - return; } }); @@ -294,16 +290,16 @@ - { - if (chatSidebar?.activateSearchMode) { - chatSidebar.activateSearchMode(); - } - }} - /> - -
        - {@render children?.()} +
        + { + if (chatSidebar?.activateSearchMode) { + chatSidebar.activateSearchMode(); + } + }} + /> + +
        {@render children?.()}
        diff --git a/tools/ui/tests/stories/SidebarNavigation.stories.svelte b/tools/ui/tests/stories/SidebarNavigation.stories.svelte index aae42f2a053..1206ef64c0e 100644 --- a/tools/ui/tests/stories/SidebarNavigation.stories.svelte +++ b/tools/ui/tests/stories/SidebarNavigation.stories.svelte @@ -14,10 +14,6 @@ @@ -54,62 +81,88 @@ {/snippet} -
        - {#each SIDEBAR_ACTIONS_ITEMS as item, i (item.tooltip)} - {@const isActive = isItemActive(item)} - {@const itemOnClick = getItemOnClick(item)} - {@const itemTransition = { - duration: ICON_STRIP_TRANSITION_DURATION, - delay: !initialized - ? ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + i * ICON_STRIP_TRANSITION_DELAY_MULTIPLIER - : 0, - easing: circIn - }} - - {#if showIcons} -
        - -
        - -
        - -
        - {/if} - {/each} -
        + +
        + {/if} + {/each} +
        +{:else} +
        + {#each SIDEBAR_ACTIONS_ITEMS as item, i (item.tooltip)} + {@const isActive = isItemActive(item)} + {@const itemOnClick = item.route ? () => goto(item.route!) : onSearchClick} + {@const itemTransition = { + duration: ICON_STRIP_TRANSITION_DURATION, + delay: !initialized + ? ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + i * ICON_STRIP_TRANSITION_DELAY_MULTIPLIER + : 0, + easing: circIn + }} + + {#if showIcons} +
        + +
        + {/if} + {/each} +
        +{/if} diff --git a/tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte b/tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte index 62e73d8579a..41105baa5e6 100644 --- a/tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte +++ b/tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChat.svelte @@ -126,10 +126,7 @@ }); -
        +
        -