diff --git a/frontend/src/sidepanel/components/SearchInput.tsx b/frontend/src/sidepanel/components/SearchInput.tsx index 356a528..6febec1 100644 --- a/frontend/src/sidepanel/components/SearchInput.tsx +++ b/frontend/src/sidepanel/components/SearchInput.tsx @@ -1,27 +1,54 @@ -import { SearchLineIcon } from "./ThreadSearchIcons"; +import type { KeyboardEventHandler } from "react"; +import { SearchLineIcon } from "./ThreadSearchIcons"; interface SearchInputProps { value: string; onChange: (value: string) => void; placeholder?: string; + autoFocus?: boolean; + onKeyDown?: KeyboardEventHandler; + ariaLabel?: string; + className?: string; + variant?: "default" | "threads-glass"; } export function SearchInput({ value, onChange, placeholder = "Search conversations", + autoFocus = false, + onKeyDown, + ariaLabel, + className, + variant = "default", }: SearchInputProps) { + const isEmpty = value.trim().length === 0; + const rootClassName = [ + "vesti-search-input", + variant === "threads-glass" ? "vesti-search-input-glass" : "vesti-search-input-default", + className ?? "", + ] + .filter(Boolean) + .join(" "); + return ( -
- +
+ + {variant === "threads-glass" ? ( + + ) : null} onChange(e.target.value)} - placeholder={placeholder} - className="h-9 w-full rounded-sm border border-border-default bg-bg-primary pl-9 pr-3 text-vesti-md font-sans text-text-primary placeholder:text-text-tertiary transition-[border-color,box-shadow] [transition-duration:120ms] ease-in-out focus-visible:border-border-focus focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-primary-light" + onChange={(event) => onChange(event.target.value)} + onKeyDown={onKeyDown} + aria-label={ariaLabel ?? placeholder} + placeholder={variant === "threads-glass" ? "" : placeholder} + className="vesti-search-input-field" />
); } - diff --git a/frontend/src/sidepanel/pages/TimelinePage.tsx b/frontend/src/sidepanel/pages/TimelinePage.tsx index 26ee004..b7c5d07 100644 --- a/frontend/src/sidepanel/pages/TimelinePage.tsx +++ b/frontend/src/sidepanel/pages/TimelinePage.tsx @@ -12,6 +12,7 @@ import { } from "~lib/services/storageService"; import { PLATFORM_TONE } from "../components/platformTone"; import { ThreadsFilterDisclosure } from "../components/ThreadsFilterDisclosure"; +import { SearchInput } from "../components/SearchInput"; import { ConversationList } from "../containers/ConversationList"; import { SearchLineIcon } from "../components/ThreadSearchIcons"; import { @@ -481,52 +482,51 @@ export function TimelinePage({ }; return ( -
+
{headerMode === "search" ? ( -
-
- - { - const nextQuery = event.target.value; - dispatch({ type: "QUERY_CHANGED", query: nextQuery }); - }} - onKeyDown={(event) => { - if (event.key === "Escape") { - event.preventDefault(); - handleCancelSearch(); - } - }} - placeholder="Search conversations" - className="h-full w-full bg-transparent text-vesti-sm text-text-primary outline-none placeholder:text-text-tertiary" - /> -
+
+ { + dispatch({ type: "QUERY_CHANGED", query: nextQuery }); + }} + onKeyDown={(event) => { + if (event.key === "Escape") { + event.preventDefault(); + handleCancelSearch(); + } + }} + placeholder="Search conversations" + ariaLabel="Search conversations" + variant="threads-glass" + className="threads-header-search-input" + />
) : ( -
-
+
+

Threads

- + - {firstCapturedTodayCount} first captured today + + {firstCapturedTodayCount} first captured today +
-
+
@@ -534,15 +534,14 @@ export function TimelinePage({ type="button" aria-label="Filter conversations" onClick={handleToggleFilter} - className={`flex h-8 w-8 items-center justify-center rounded-md transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${ + className={`threads-header-icon-btn ${ headerMode === "filter" - ? "bg-bg-secondary text-text-primary" - : "text-text-tertiary hover:bg-bg-secondary hover:text-text-secondary" + ? "threads-header-icon-btn-active" + : "" }`} > -
)} diff --git a/frontend/src/style.css b/frontend/src/style.css index 9bf9e21..060f7ee 100644 --- a/frontend/src/style.css +++ b/frontend/src/style.css @@ -326,8 +326,237 @@ height: 2rem; flex-shrink: 0; align-items: center; - padding-left: 1rem; - padding-right: 1rem; + gap: 0.75rem; + padding-left: 0.75rem; + padding-right: 0.75rem; + } + + .threads-header { + justify-content: space-between; + gap: 0.375rem; + } + + .threads-header-main { + min-width: 0; + flex: 1 1 auto; + display: flex; + align-items: center; + gap: 0.375rem; + } + + .threads-header-capture-status { + flex: 1 1 auto; + min-width: 0; + max-width: 100%; + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-size: 10.5px; + font-weight: 600; + line-height: 1.2; + color: hsl(var(--success) / 0.92); + } + + .threads-header-capture-copy { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .threads-header-actions { + flex: 0 0 auto; + display: flex; + align-items: center; + gap: 0; + margin-left: auto; + padding-left: 0; + } + + .threads-header-icon-btn { + width: 1.625rem; + height: 1.625rem; + display: inline-flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + border-radius: 999px; + color: hsl(var(--text-tertiary)); + transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease; + } + + .threads-header-icon-btn:hover { + background-color: hsl(var(--bg-secondary) / 0.82); + color: hsl(var(--text-secondary)); + } + + .threads-header-icon-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 2px hsl(var(--accent-primary-light)); + } + + .threads-header-icon-btn-active { + background-color: hsl(var(--bg-secondary) / 0.88); + color: hsl(var(--text-primary)); + } + + .threads-header-searching { + height: auto; + min-height: 2.875rem; + justify-content: flex-start; + align-items: center; + gap: 0.5rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; + } + + .threads-header-search-input { + width: 100%; + min-width: 0; + flex: 1 1 auto; + max-width: 14.5rem; + } + + .threads-header-search-cancel { + flex: 0 0 auto; + padding: 0 0.125rem; + font-size: 11.5px; + font-weight: 500; + line-height: 1; + color: hsl(var(--text-tertiary) / 0.9); + transition: color 0.15s ease; + } + + .threads-header-search-cancel:hover { + color: hsl(var(--text-secondary)); + } + + .threads-header-search-cancel:focus-visible { + outline: none; + box-shadow: 0 0 0 2px hsl(var(--accent-primary-light)); + border-radius: 8px; + } + + .vesti-search-input { + position: relative; + display: flex; + align-items: center; + min-width: 0; + } + + .vesti-search-input-icon { + pointer-events: none; + position: absolute; + left: 0.875rem; + width: 1rem; + height: 1rem; + color: hsl(var(--text-tertiary)); + } + + .vesti-search-input-field { + width: 100%; + font-family: var(--font-ui); + font-size: 12.5px; + color: hsl(var(--text-primary)); + outline: none; + } + + .vesti-search-input-field::placeholder { + color: hsl(var(--text-tertiary)); + opacity: 1; + } + + .vesti-search-input-placeholder { + position: absolute; + top: 50%; + left: 2.5rem; + right: 0.9rem; + transform: translateY(-50%); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: center; + pointer-events: none; + font-family: var(--font-ui); + font-size: 12.5px; + line-height: 1; + color: hsl(var(--text-tertiary) / 0.92); + } + + .vesti-search-input[data-empty="false"] .vesti-search-input-placeholder { + display: none; + } + + .vesti-search-input-default .vesti-search-input-field { + height: 2.25rem; + border: 1px solid hsl(var(--border-default)); + border-radius: 0.375rem; + background-color: hsl(var(--bg-primary)); + padding-left: 2.25rem; + padding-right: 0.75rem; + transition: border-color 0.12s ease, box-shadow 0.12s ease; + } + + .vesti-search-input-default:focus-within .vesti-search-input-field { + border-color: hsl(var(--border-focus)); + box-shadow: 0 0 0 2px hsl(var(--accent-primary-light)); + } + + .vesti-search-input-glass { + border: 1px solid rgb(255 255 255 / 0.28); + border-radius: 999px; + background: + linear-gradient(180deg, rgb(255 255 255 / 0.7), rgb(255 255 255 / 0.58)), + hsl(var(--bg-primary) / 0.52); + backdrop-filter: blur(14px) saturate(1.04); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 0.36), + 0 4px 14px rgb(12 16 24 / 0.06); + } + + .vesti-search-input-glass .vesti-search-input-icon { + left: 0.875rem; + color: hsl(var(--text-secondary) / 0.92); + } + + .vesti-search-input-glass .vesti-search-input-field { + height: 2.25rem; + border: 0; + background: transparent; + padding-left: 2.375rem; + padding-right: 0.8125rem; + line-height: 1; + text-align: left; + } + + .vesti-search-input-glass[data-empty="true"] .vesti-search-input-field { + padding-right: 0.875rem; + } + + .vesti-search-input-glass:focus-within { + border-color: rgb(255 255 255 / 0.42); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 0.42), + 0 0 0 2px hsl(var(--accent-primary-light)), + 0 6px 18px rgb(12 16 24 / 0.08); + } + + .dark .vesti-search-input-glass { + border-color: rgb(255 255 255 / 0.14); + background: + linear-gradient(180deg, rgb(255 255 255 / 0.06), rgb(255 255 255 / 0.025)), + hsl(var(--bg-secondary) / 0.72); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 0.1), + 0 8px 20px rgb(0 0 0 / 0.18); + } + + .dark .vesti-search-input-glass:focus-within { + border-color: rgb(255 255 255 / 0.2); + box-shadow: + inset 0 1px 0 rgb(255 255 255 / 0.13), + 0 0 0 2px hsl(var(--accent-primary-light)), + 0 8px 22px rgb(0 0 0 / 0.2); } .vesti-artifact {