Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 34 additions & 7 deletions frontend/src/sidepanel/components/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLInputElement>;
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 (
<div className="relative flex items-center">
<SearchLineIcon className="pointer-events-none absolute left-3 h-4 w-4 text-text-tertiary" />
<div className={rootClassName} data-empty={isEmpty ? "true" : "false"}>
<SearchLineIcon className="vesti-search-input-icon" />
{variant === "threads-glass" ? (
<span className="vesti-search-input-placeholder" aria-hidden="true">
{placeholder}
</span>
) : null}
<input
type="text"
autoFocus={autoFocus}
value={value}
onChange={(e) => 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"
/>
</div>
);
}

65 changes: 32 additions & 33 deletions frontend/src/sidepanel/pages/TimelinePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -481,68 +482,66 @@ export function TimelinePage({
};

return (
<div className="flex h-full flex-col bg-bg-app">
<div className="flex h-full flex-col bg-bg-app">
{headerMode === "search" ? (
<header className="vesti-page-header gap-2">
<div className="threads-search-surface flex h-8 flex-1 items-center gap-2 rounded-lg px-3">
<SearchLineIcon className="h-3.5 w-3.5 shrink-0 text-text-secondary" />
<input
type="text"
autoFocus
value={query}
onChange={(event) => {
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"
/>
</div>
<header className="vesti-page-header threads-header threads-header-searching">
<SearchInput
autoFocus
value={query}
onChange={(nextQuery) => {
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"
/>
<button
type="button"
onClick={handleCancelSearch}
className="rounded-sm px-1 py-1 text-vesti-sm font-medium text-text-secondary transition-colors duration-150 hover:text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
className="threads-header-search-cancel"
>
Cancel
</button>
</header>
) : (
<header className="vesti-page-header justify-between">
<div className="flex min-w-0 items-center gap-3">
<header className="vesti-page-header threads-header">
<div className="threads-header-main">
<h1 className="vesti-page-title text-text-primary">Threads</h1>
<span className="inline-flex items-center gap-1.5 whitespace-nowrap text-vesti-xs font-medium text-success/90">
<span className="threads-header-capture-status" title={`${firstCapturedTodayCount} first captured today`}>
<span className="h-1.5 w-1.5 rounded-full bg-success" />
{firstCapturedTodayCount} first captured today
<span className="threads-header-capture-copy">
{firstCapturedTodayCount} first captured today
</span>
</span>
</div>
<div className="flex shrink-0 items-center gap-0.5">
<div className="threads-header-actions">
<button
type="button"
aria-label="Search conversations"
onClick={handleOpenSearch}
className="flex h-8 w-8 items-center justify-center rounded-md text-text-tertiary transition-colors duration-150 hover:bg-bg-secondary hover:text-text-secondary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus"
className="threads-header-icon-btn"
>
<SearchLineIcon className="h-3.5 w-3.5" />
</button>
<button
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"
: ""
}`}
>
<SlidersHorizontal className="h-3.5 w-3.5" strokeWidth={1.8} />
</button>

</div>
</header>
)}
Expand Down
Loading