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;