Merge branch 'worktree-neo-surveillance-redesign'

This commit is contained in:
Developer
2026-07-28 14:32:00 +07:00
25 changed files with 891 additions and 291 deletions
@@ -1,31 +1,53 @@
"use client";
import { Loader2 } from "lucide-react";
import { MessageCard } from "./message-card";
import { Button } from "@/components/ui/button";
import type { MessageRecord } from "@/lib/types";
interface MessageListProps {
messages: MessageRecord[];
selectedId?: string | null;
selectedId: string | null;
onSelect: (id: string) => void;
onReanalyze?: (id: string) => void;
hasMore?: boolean;
onLoadMore?: () => void;
isLoadingMore?: boolean;
}
export function MessageList({ messages, selectedId, onSelect }: MessageListProps) {
export function MessageList({
messages,
selectedId: _selectedId,
onSelect,
onReanalyze,
hasMore,
onLoadMore,
isLoadingMore,
}: MessageListProps) {
return (
<div className="space-y-1.5 overflow-y-auto max-h-[calc(100vh-200px)] pr-1">
{messages.length === 0 ? (
<div className="flex items-center justify-center py-12 text-text-secondary/40 text-sm">
No messages
<>
{messages.map((msg) => (
<MessageCard
key={msg.id}
message={msg}
onClick={onSelect}
onReanalyze={(id) => onReanalyze?.(id)}
/>
))}
{hasMore && (
<div className="flex justify-center py-4">
<Button
variant="outline"
size="sm"
onClick={onLoadMore}
disabled={isLoadingMore}
className="text-xs glass"
>
{isLoadingMore && <Loader2 className="size-3 animate-spin mr-1" />}
Load more
</Button>
</div>
) : (
messages.map((msg) => (
<MessageCard
key={msg.id}
message={msg}
selected={selectedId === msg.id}
onClick={onSelect}
/>
))
)}
</div>
</>
);
}
@@ -16,11 +16,11 @@ export function SearchOverlay({ open, onClose, onSelect }: SearchOverlayProps) {
const [query, setQuery] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
const { data: results } = useQuery<{ results: MessageRecord[] }>({
const { data: results } = useQuery<MessageRecord[]>({
queryKey: ["messages-search", query],
queryFn: async () => {
const res = await messagesApi.search(query, 20);
return res;
return res.results;
},
enabled: query.length >= 2,
});
@@ -37,7 +37,7 @@ export function SearchOverlay({ open, onClose, onSelect }: SearchOverlayProps) {
const handleKey = (e: KeyboardEvent) => {
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
e.preventDefault();
onClose();
onClose(); // this is called when Cmd+K is pressed globally — toggle
}
if (e.key === "Escape") onClose();
};
@@ -69,12 +69,12 @@ export function SearchOverlay({ open, onClose, onSelect }: SearchOverlayProps) {
{/* Results */}
<div className="max-h-80 overflow-y-auto p-2 space-y-1">
{!results || results.results.length === 0 ? (
{!results || results.length === 0 ? (
<div className="py-8 text-center text-xs text-text-secondary/40">
{query.length < 2 ? "Type at least 2 characters" : "No results found"}
</div>
) : (
results.results.map((msg: MessageRecord) => (
results.map((msg) => (
<button
key={msg.id}
type="button"