HistoryView.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
  2. import { vscode } from "../utils/vscode"
  3. import { HistoryItem } from "../../../src/shared/HistoryItem"
  4. type HistoryViewProps = {
  5. taskHistory: HistoryItem[]
  6. onDone: () => void
  7. }
  8. const HistoryView = ({ taskHistory, onDone }: HistoryViewProps) => {
  9. const handleHistorySelect = (id: string) => {
  10. vscode.postMessage({ type: "showTaskWithId", text: id })
  11. }
  12. const handleDeleteHistoryItem = (id: string) => {
  13. vscode.postMessage({ type: "deleteTaskWithId", text: id })
  14. }
  15. const handleExportMd = (id: string) => {
  16. vscode.postMessage({ type: "exportTaskWithId", text: id })
  17. }
  18. const formatDate = (timestamp: number) => {
  19. const date = new Date(timestamp)
  20. return date
  21. ?.toLocaleString("en-US", {
  22. month: "long",
  23. day: "numeric",
  24. hour: "numeric",
  25. minute: "2-digit",
  26. hour12: true,
  27. })
  28. .replace(", ", " ")
  29. .replace(" at", ",")
  30. .toUpperCase()
  31. }
  32. return (
  33. <>
  34. <style>
  35. {`
  36. .history-item:hover {
  37. background-color: var(--vscode-list-hoverBackground);
  38. }
  39. .delete-button {
  40. opacity: 0;
  41. pointer-events: none;
  42. }
  43. .history-item:hover .delete-button {
  44. opacity: 1;
  45. pointer-events: auto;
  46. }
  47. `}
  48. </style>
  49. <div
  50. style={{
  51. position: "fixed",
  52. top: 0,
  53. left: 0,
  54. right: 0,
  55. bottom: 0,
  56. display: "flex",
  57. flexDirection: "column",
  58. overflow: "hidden",
  59. }}>
  60. <div
  61. style={{
  62. display: "flex",
  63. justifyContent: "space-between",
  64. alignItems: "center",
  65. padding: "10px 17px 10px 20px",
  66. }}>
  67. <h3 style={{ color: "var(--vscode-foreground)", margin: 0 }}>History</h3>
  68. <VSCodeButton onClick={onDone}>Done</VSCodeButton>
  69. </div>
  70. <div style={{ flexGrow: 1, overflowY: "auto", margin: 0 }}>
  71. {taskHistory.length === 0 && (
  72. <div
  73. style={{
  74. display: "flex",
  75. flexDirection: "column",
  76. justifyContent: "center",
  77. alignItems: "center",
  78. height: "100%",
  79. fontStyle: "italic",
  80. color: "var(--vscode-descriptionForeground)",
  81. textAlign: "center",
  82. padding: "0px 10px",
  83. }}>
  84. <span
  85. className="codicon codicon-archive"
  86. style={{ fontSize: "50px", marginBottom: "15px" }}></span>
  87. <div>
  88. No history found,
  89. <br />
  90. start a new task to see it here...
  91. </div>
  92. </div>
  93. )}
  94. {taskHistory
  95. .filter((item) => item.ts && item.task && item.totalCost)
  96. .map((item, index) => (
  97. <div
  98. key={item.id}
  99. className="history-item"
  100. style={{
  101. cursor: "pointer",
  102. borderBottom:
  103. index < taskHistory.length - 1
  104. ? "1px solid var(--vscode-panel-border)"
  105. : "none",
  106. }}
  107. onClick={() => handleHistorySelect(item.id)}>
  108. <div
  109. style={{
  110. display: "flex",
  111. flexDirection: "column",
  112. gap: "8px",
  113. padding: "12px 20px",
  114. position: "relative",
  115. }}>
  116. <div
  117. style={{
  118. display: "flex",
  119. justifyContent: "space-between",
  120. alignItems: "center",
  121. }}>
  122. <span
  123. style={{
  124. color: "var(--vscode-descriptionForeground)",
  125. fontWeight: 500,
  126. fontSize: "0.85em",
  127. textTransform: "uppercase",
  128. }}>
  129. {formatDate(item.ts)}
  130. </span>
  131. <VSCodeButton
  132. appearance="icon"
  133. onClick={(e) => {
  134. e.stopPropagation()
  135. handleDeleteHistoryItem(item.id)
  136. }}
  137. className="delete-button">
  138. <span className="codicon codicon-trash"></span>
  139. </VSCodeButton>
  140. </div>
  141. <div
  142. style={{
  143. fontSize: "var(--vscode-font-size)",
  144. color: "var(--vscode-foreground)",
  145. display: "-webkit-box",
  146. WebkitLineClamp: 3,
  147. WebkitBoxOrient: "vertical",
  148. overflow: "hidden",
  149. whiteSpace: "pre-wrap",
  150. wordBreak: "break-word",
  151. overflowWrap: "anywhere",
  152. }}>
  153. {item.task}
  154. </div>
  155. <div style={{ display: "flex", flexDirection: "column", gap: "4px" }}>
  156. <div
  157. style={{
  158. display: "flex",
  159. alignItems: "center",
  160. gap: "4px",
  161. flexWrap: "wrap",
  162. }}>
  163. <span
  164. style={{
  165. fontWeight: 500,
  166. color: "var(--vscode-descriptionForeground)",
  167. }}>
  168. Tokens:
  169. </span>
  170. <span
  171. style={{
  172. display: "flex",
  173. alignItems: "center",
  174. gap: "3px",
  175. color: "var(--vscode-descriptionForeground)",
  176. }}>
  177. <i
  178. className="codicon codicon-arrow-up"
  179. style={{
  180. fontSize: "12px",
  181. fontWeight: "bold",
  182. marginBottom: "-2px",
  183. }}
  184. />
  185. {item.tokensIn?.toLocaleString()}
  186. </span>
  187. <span
  188. style={{
  189. display: "flex",
  190. alignItems: "center",
  191. gap: "3px",
  192. color: "var(--vscode-descriptionForeground)",
  193. }}>
  194. <i
  195. className="codicon codicon-arrow-down"
  196. style={{
  197. fontSize: "12px",
  198. fontWeight: "bold",
  199. marginBottom: "-2px",
  200. }}
  201. />
  202. {item.tokensOut?.toLocaleString()}
  203. </span>
  204. </div>
  205. {item.cacheWrites && item.cacheReads && (
  206. <div
  207. style={{
  208. display: "flex",
  209. alignItems: "center",
  210. gap: "4px",
  211. flexWrap: "wrap",
  212. }}>
  213. <span
  214. style={{
  215. fontWeight: 500,
  216. color: "var(--vscode-descriptionForeground)",
  217. }}>
  218. Cache:
  219. </span>
  220. <span
  221. style={{
  222. display: "flex",
  223. alignItems: "center",
  224. gap: "3px",
  225. color: "var(--vscode-descriptionForeground)",
  226. }}>
  227. <i
  228. className="codicon codicon-database"
  229. style={{
  230. fontSize: "12px",
  231. fontWeight: "bold",
  232. marginBottom: "-1px",
  233. }}
  234. />
  235. +{item.cacheWrites?.toLocaleString()}
  236. </span>
  237. <span
  238. style={{
  239. display: "flex",
  240. alignItems: "center",
  241. gap: "3px",
  242. color: "var(--vscode-descriptionForeground)",
  243. }}>
  244. <i
  245. className="codicon codicon-arrow-right"
  246. style={{
  247. fontSize: "12px",
  248. fontWeight: "bold",
  249. marginBottom: 0,
  250. }}
  251. />
  252. {item.cacheReads?.toLocaleString()}
  253. </span>
  254. </div>
  255. )}
  256. <div
  257. style={{
  258. display: "flex",
  259. justifyContent: "space-between",
  260. alignItems: "center",
  261. marginTop: -2,
  262. }}>
  263. <div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
  264. <span
  265. style={{
  266. fontWeight: 500,
  267. color: "var(--vscode-descriptionForeground)",
  268. }}>
  269. API Cost:
  270. </span>
  271. <span style={{ color: "var(--vscode-descriptionForeground)" }}>
  272. ${item.totalCost?.toFixed(4)}
  273. </span>
  274. </div>
  275. <VSCodeButton
  276. appearance="icon"
  277. onClick={(e) => {
  278. e.stopPropagation()
  279. handleExportMd(item.id)
  280. }}>
  281. <div style={{ fontSize: "11px", fontWeight: 500, opacity: 1 }}>
  282. EXPORT .MD
  283. </div>
  284. </VSCodeButton>
  285. </div>
  286. </div>
  287. </div>
  288. </div>
  289. ))}
  290. </div>
  291. </div>
  292. </>
  293. )
  294. }
  295. export default HistoryView