From 8388d8c8c4a743b643be3bf2e2136064f88efdbd Mon Sep 17 00:00:00 2001 From: Smyile <84925446+xsmyile@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:04:02 +0200 Subject: [PATCH 1/2] fix(ui): suppress transitions during theme switch Toolbar hover color transitions also animated on the theme toggle, smearing colors over ~150ms. Disable transitions around the .dark flip via a guard class. --- ui/src/index.css | 5 +++++ ui/src/stores/theme.ts | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ui/src/index.css b/ui/src/index.css index 8cfbb61..fb2cfc7 100644 --- a/ui/src/index.css +++ b/ui/src/index.css @@ -12,6 +12,11 @@ font-family: var(--font-brand); } +.theme-switching, +.theme-switching * { + transition: none !important; +} + .mesh-glow { position: fixed; top: 0; diff --git a/ui/src/stores/theme.ts b/ui/src/stores/theme.ts index 4dde554..e6424ce 100644 --- a/ui/src/stores/theme.ts +++ b/ui/src/stores/theme.ts @@ -21,7 +21,11 @@ function resolve(t: Theme): "dark" | "light" { } function applyTheme(t: Theme) { - document.documentElement.classList.toggle("dark", resolve(t) === "dark"); + const root = document.documentElement; + root.classList.add("theme-switching"); + root.classList.toggle("dark", resolve(t) === "dark"); + void root.offsetHeight; + root.classList.remove("theme-switching"); } applyTheme(theme()); From 40edd14c9de4e6b90ff51c741e993b2172387e44 Mon Sep 17 00:00:00 2001 From: Smyile <84925446+xsmyile@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:04:02 +0200 Subject: [PATCH 2/2] feat(ui): add filter input to tag dropdown past 8 tags --- ui/src/components/FilterBar.tsx | 117 ++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/ui/src/components/FilterBar.tsx b/ui/src/components/FilterBar.tsx index 96a05cb..6822d6b 100644 --- a/ui/src/components/FilterBar.tsx +++ b/ui/src/components/FilterBar.tsx @@ -15,6 +15,8 @@ import { total, } from "../stores/messages"; +const TAG_SEARCH_THRESHOLD = 8; + function Chip(props: { label: string; active: boolean; @@ -42,9 +44,16 @@ function Chip(props: { function TagDropdown() { const [open, setOpen] = createSignal(false); const [pos, setPos] = createSignal({ top: 0, left: 0 }); + const [tagQuery, setTagQuery] = createSignal(""); let triggerRef: HTMLButtonElement | undefined; let dropdownRef: HTMLDivElement | undefined; + const visibleTags = () => { + const q = tagQuery().trim().toLowerCase(); + const tags = allTags(); + return q ? tags.filter((t) => t.toLowerCase().includes(q)) : tags; + }; + function updatePosition() { if (!triggerRef) return; const rect = triggerRef.getBoundingClientRect(); @@ -74,6 +83,7 @@ function TagDropdown() { open, (isOpen) => { if (isOpen) { + setTagQuery(""); updatePosition(); window.addEventListener("scroll", updatePosition, true); window.addEventListener("resize", updatePosition); @@ -157,51 +167,72 @@ function TagDropdown() { } > + TAG_SEARCH_THRESHOLD}> +
+ queueMicrotask(() => el.focus())} + value={tagQuery()} + onInput={(e) => setTagQuery(e.currentTarget.value)} + placeholder="Filter tags..." + class="w-full rounded-md border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800/50 px-2 py-1 text-xs text-zinc-700 dark:text-zinc-300 placeholder-zinc-400 dark:placeholder-zinc-500 outline-none focus:border-zinc-400 dark:focus:border-zinc-600" + /> +
+
- - {(tag) => { - const isSelected = () => filters().tags.includes(tag); - return ( - - ); - }} - +
+ + + + + +
+ + {tag} + + + ); + }} + +
0}>