Buckets:
| import path from "path" | |
| import { onMount } from "solid-js" | |
| import { createStore, produce, unwrap } from "solid-js/store" | |
| import type { AgentPart, FilePart, TextPart } from "@opencode-ai/sdk/v2" | |
| import { createSimpleContext } from "../context/helper" | |
| import { useTuiPaths } from "../context/runtime" | |
| import { appendText, readText, writeText } from "../util/persistence" | |
| export type PromptInfo = { | |
| input: string | |
| mode?: "normal" | "shell" | |
| parts: ( | |
| | Omit<FilePart, "id" | "messageID" | "sessionID"> | |
| | Omit<AgentPart, "id" | "messageID" | "sessionID"> | |
| | (Omit<TextPart, "id" | "messageID" | "sessionID"> & { | |
| source?: { | |
| text: { | |
| start: number | |
| end: number | |
| value: string | |
| } | |
| } | |
| }) | |
| )[] | |
| } | |
| export const MAX_HISTORY_ENTRIES = 50 | |
| export function parsePromptHistory(text: string) { | |
| return text | |
| .split("\n") | |
| .filter(Boolean) | |
| .map((line) => { | |
| try { | |
| return JSON.parse(line) as PromptInfo | |
| } catch { | |
| return undefined | |
| } | |
| }) | |
| .filter((line): line is PromptInfo => line !== undefined) | |
| .slice(-MAX_HISTORY_ENTRIES) | |
| } | |
| export function isDuplicateEntry(previous: PromptInfo | undefined, next: PromptInfo): boolean { | |
| if (!previous) return false | |
| return JSON.stringify(previous) === JSON.stringify(next) | |
| } | |
| export const { use: usePromptHistory, provider: PromptHistoryProvider } = createSimpleContext({ | |
| name: "PromptHistory", | |
| init: () => { | |
| const paths = useTuiPaths() | |
| const historyPath = path.join(paths.state, "prompt-history.jsonl") | |
| onMount(async () => { | |
| const lines = parsePromptHistory(await readText(historyPath).catch(() => "")) | |
| setStore("history", lines) | |
| // Rewrite valid retained entries to self-heal corruption and enforce the limit. | |
| if (lines.length > 0) | |
| writeText(historyPath, lines.map((line) => JSON.stringify(line)).join("\n") + "\n").catch(() => {}) | |
| }) | |
| const [store, setStore] = createStore({ | |
| index: 0, | |
| history: [] as PromptInfo[], | |
| }) | |
| return { | |
| move(direction: 1 | -1, input: string) { | |
| if (!store.history.length) return undefined | |
| const current = store.history.at(store.index) | |
| if (!current) return undefined | |
| if (current.input !== input && input.length) return | |
| setStore( | |
| produce((draft) => { | |
| const next = store.index + direction | |
| if (Math.abs(next) > store.history.length) return | |
| if (next > 0) return | |
| draft.index = next | |
| }), | |
| ) | |
| if (store.index === 0) return { input: "", parts: [] } | |
| return store.history.at(store.index) | |
| }, | |
| append(item: PromptInfo) { | |
| const entry = structuredClone(unwrap(item)) | |
| if (isDuplicateEntry(store.history.at(-1), entry)) { | |
| setStore("index", 0) | |
| return | |
| } | |
| let trimmed = false | |
| setStore( | |
| produce((draft) => { | |
| draft.history.push(entry) | |
| if (draft.history.length > MAX_HISTORY_ENTRIES) { | |
| draft.history = draft.history.slice(-MAX_HISTORY_ENTRIES) | |
| trimmed = true | |
| } | |
| draft.index = 0 | |
| }), | |
| ) | |
| if (trimmed) { | |
| writeText(historyPath, store.history.map((line) => JSON.stringify(line)).join("\n") + "\n").catch(() => {}) | |
| return | |
| } | |
| appendText(historyPath, JSON.stringify(entry) + "\n").catch(() => {}) | |
| }, | |
| } | |
| }, | |
| }) | |
Xet Storage Details
- Size:
- 3.51 kB
- Xet hash:
- efaa1aace6c03a7a91add87a5710f9af34757de2288f78e5a56bb4e577ef4a0d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.