import { ArrowLeftIcon, TrashIcon } from "@heroicons/react/24/outline"; import { PersistedSessionInfo, SessionInfo } from "../shims/typings"; import MiniSearch from "minisearch"; import React, { Fragment, useEffect, useState } from "react"; import { useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; import styled from "styled-components"; import { defaultBorderRadius, lightGray, vscBackground, vscBadgeBackground, vscForeground, vscInputBackground, } from "../components"; import HeaderButtonWithText from "../components/HeaderButtonWithText"; import useHistory from "../hooks/useHistory"; import { useNavigationListener } from "../hooks/useNavigationListener"; import { newSession } from "../redux/slices/stateSlice"; import { getFontSize } from "../util"; const SearchBar = styled.input` padding: 4px 8px; border-radius: ${defaultBorderRadius}; border: 0.5px solid #888; outline: none; width: 90vw; max-width: 500px; margin: 8px auto; display: block; background-color: ${vscInputBackground}; color: ${vscForeground}; &:focus { border: 0.5px solid ${vscBadgeBackground}; outline: none; } `; const Tr = styled.tr` &:hover { background-color: ${vscInputBackground}; } overflow-wrap: anywhere; border-bottom: 1px solid ${vscInputBackground}; border-top: 1px solid ${vscInputBackground}; `; const parseDate = (date: string): Date => { let dateObj = new Date(date); if (isNaN(dateObj.getTime())) { dateObj = new Date(parseInt(date)); } return dateObj; }; const SectionHeader = styled.tr` padding: 4px; padding-left: 16px; padding-right: 16px; background-color: ${vscInputBackground}; width: 100%; font-weight: bold; text-align: center; align-items: center; margin: 0; position: sticky; height: 1.5em; `; const TdDiv = styled.div` cursor: pointer; flex-grow: 1; padding-left: 1rem; padding-right: 1rem; padding-top: 0.5rem; padding-bottom: 0.5rem; `; function TableRow({ session, date, onDelete, }: { session: SessionInfo; date: Date; onDelete: (sessionId: string) => void; }) { const dispatch = useDispatch(); const navigate = useNavigate(); const apiUrl = window.serverUrl; const workspacePaths = window.workspacePaths || [""]; const [hovered, setHovered] = useState(false); const { saveSession, deleteSession, loadSession } = useHistory(dispatch); return ( setHovered(true)} onMouseLeave={() => setHovered(false)} >
{ // Save current session saveSession(); const json: PersistedSessionInfo = await loadSession( session.sessionId ); dispatch(newSession(json)); navigate("/"); }} >
{JSON.stringify(session.title).slice(1, -1)}
{date.toLocaleString("en-US", { year: "2-digit", month: "2-digit", day: "2-digit", hour: "numeric", minute: "2-digit", hour12: true, })} {" | "} {lastPartOfPath(session.workspaceDirectory || "")}/
{hovered && ( { deleteSession(session.sessionId); onDelete(session.sessionId); }} > )}
); } function lastPartOfPath(path: string): string { const sep = path.includes("/") ? "/" : "\\"; return path.split(sep).pop() || path; } function History() { useNavigationListener(); const navigate = useNavigate(); const [sessions, setSessions] = useState([]); const [filteredAndSortedSessions, setFilteredAndSortedSessions] = useState< SessionInfo[] >([]); const apiUrl = window.serverUrl; const workspacePaths = window.workspacePaths || []; const deleteSessionInUI = async (sessionId: string) => { setSessions((prev) => prev.filter((session) => session.sessionId !== sessionId) ); }; const [filteringByWorkspace, setFilteringByWorkspace] = useState(false); const stickyHistoryHeaderRef = React.useRef(null); const [headerHeight, setHeaderHeight] = useState(0); const dispatch = useDispatch(); const { getHistory } = useHistory(dispatch); const [minisearch, setMinisearch] = useState< MiniSearch<{ title: string; sessionId: string }> >( new MiniSearch({ fields: ["title"], storeFields: ["title", "sessionId", "id"], }) ); const [searchTerm, setSearchTerm] = useState(""); useEffect(() => { const fetchSessions = async () => { const sessions = await getHistory(); setSessions(sessions); minisearch.addAll( sessions.map((session) => ({ title: session.title, sessionId: session.sessionId, id: session.sessionId, })) ); }; fetchSessions(); }, []); useEffect(() => { const sessionIds = minisearch .search(searchTerm, { fuzzy: 0.1, }) .map((result) => result.id); setFilteredAndSortedSessions( sessions .filter((session) => { if ( !filteringByWorkspace || typeof workspacePaths === "undefined" || typeof session.workspaceDirectory === "undefined" ) { return true; } return workspacePaths.includes(session.workspaceDirectory); }) // ChunkFilter.ts by search term .filter((session) => { return searchTerm === "" || sessionIds.includes(session.sessionId); }) .sort( (a, b) => parseDate(b.dateCreated).getTime() - parseDate(a.dateCreated).getTime() ) ); }, [filteringByWorkspace, sessions, searchTerm, minisearch]); useEffect(() => { setHeaderHeight(stickyHistoryHeaderRef.current?.clientHeight || 100); }, [stickyHistoryHeaderRef.current]); const yesterday = new Date(Date.now() - 1000 * 60 * 60 * 24); const lastWeek = new Date(Date.now() - 1000 * 60 * 60 * 24 * 7); const lastMonth = new Date(Date.now() - 1000 * 60 * 60 * 24 * 30); const earlier = new Date(0); return (
navigate("/")} className="inline-block ml-4 cursor-pointer" />

History

{/* {workspacePaths && workspacePaths.length > 0 && ( setFilteringByWorkspace((prev) => !prev)} title={`Show only sessions from ${lastPartOfPath( workspacePaths[workspacePaths.length - 1] )}/`} /> )} */}
setSearchTerm(e.target.value)} /> {filteredAndSortedSessions.length === 0 && (
No past sessions found. To start a new session, either click the "+" button or use the keyboard shortcut: Option + Command + N
)} {filteredAndSortedSessions.map((session, index) => { const prevDate = index > 0 ? parseDate(filteredAndSortedSessions[index - 1].dateCreated) : earlier; const date = parseDate(session.dateCreated); return ( {index === 0 && date > yesterday && ( Today )} {date < yesterday && date > lastWeek && prevDate > yesterday && ( This Week )} {date < lastWeek && date > lastMonth && prevDate > lastWeek && ( This Month )} deleteSessionInUI(session.sessionId)} > ); })}

All session data is saved in ~/.continue/sessions
); } export default History;