persist prompt drafts per session
This commit is contained in:
@@ -2,13 +2,12 @@ import { createSignal, Show, onMount, For, onCleanup, createEffect, on, untrack
|
|||||||
import UnifiedPicker from "./unified-picker"
|
import UnifiedPicker from "./unified-picker"
|
||||||
import { addToHistory, getHistory } from "../stores/message-history"
|
import { addToHistory, getHistory } from "../stores/message-history"
|
||||||
import { getAttachments, addAttachment, clearAttachments, removeAttachment } from "../stores/attachments"
|
import { getAttachments, addAttachment, clearAttachments, removeAttachment } from "../stores/attachments"
|
||||||
import { getPromptValue, setPromptValue, clearPromptValue } from "../stores/prompt-state"
|
|
||||||
import { createFileAttachment, createTextAttachment, createAgentAttachment } from "../types/attachment"
|
import { createFileAttachment, createTextAttachment, createAgentAttachment } from "../types/attachment"
|
||||||
import type { Attachment } from "../types/attachment"
|
import type { Attachment } from "../types/attachment"
|
||||||
import Kbd from "./kbd"
|
import Kbd from "./kbd"
|
||||||
import HintRow from "./hint-row"
|
import HintRow from "./hint-row"
|
||||||
import { getActiveInstance } from "../stores/instances"
|
import { getActiveInstance } from "../stores/instances"
|
||||||
import { agents } from "../stores/sessions"
|
import { agents, getSessionDraftPrompt, setSessionDraftPrompt, clearSessionDraftPrompt } from "../stores/sessions"
|
||||||
|
|
||||||
interface PromptInputProps {
|
interface PromptInputProps {
|
||||||
instanceId: string
|
instanceId: string
|
||||||
@@ -57,11 +56,11 @@ export default function PromptInput(props: PromptInputProps) {
|
|||||||
|
|
||||||
const setPrompt = (value: string) => {
|
const setPrompt = (value: string) => {
|
||||||
setPromptInternal(value)
|
setPromptInternal(value)
|
||||||
setPromptValue(props.instanceId, props.sessionId, value)
|
setSessionDraftPrompt(props.instanceId, props.sessionId, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearPrompt = () => {
|
const clearPrompt = () => {
|
||||||
clearPromptValue(props.instanceId, props.sessionId)
|
clearSessionDraftPrompt(props.instanceId, props.sessionId)
|
||||||
setPromptInternal("")
|
setPromptInternal("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,14 +115,14 @@ export default function PromptInput(props: PromptInputProps) {
|
|||||||
const sessionId = props.sessionId
|
const sessionId = props.sessionId
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
setPromptValue(instanceId, sessionId, prompt())
|
setSessionDraftPrompt(instanceId, sessionId, prompt())
|
||||||
})
|
})
|
||||||
|
|
||||||
const storedPrompt = getPromptValue(instanceId, sessionId)
|
const storedPrompt = getSessionDraftPrompt(instanceId, sessionId)
|
||||||
const currentAttachments = untrack(() => getAttachments(instanceId, sessionId))
|
const currentAttachments = untrack(() => getAttachments(instanceId, sessionId))
|
||||||
|
|
||||||
setPromptInternal(storedPrompt)
|
setPromptInternal(storedPrompt)
|
||||||
setPromptValue(instanceId, sessionId, storedPrompt)
|
setSessionDraftPrompt(instanceId, sessionId, storedPrompt)
|
||||||
setHistoryIndex(-1)
|
setHistoryIndex(-1)
|
||||||
setIgnoredAtPositions(new Set<number>())
|
setIgnoredAtPositions(new Set<number>())
|
||||||
setShowPicker(false)
|
setShowPicker(false)
|
||||||
@@ -134,9 +133,8 @@ export default function PromptInput(props: PromptInputProps) {
|
|||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
adjustTextareaHeight(textareaRef)
|
adjustTextareaHeight(textareaRef)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
{ defer: true },
|
)
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
function handleRemoveAttachment(attachmentId: string) {
|
function handleRemoveAttachment(attachmentId: string) {
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import { createSignal } from "solid-js"
|
|||||||
import type { Instance, LogEntry } from "../types/instance"
|
import type { Instance, LogEntry } from "../types/instance"
|
||||||
import { sdkManager } from "../lib/sdk-manager"
|
import { sdkManager } from "../lib/sdk-manager"
|
||||||
import { sseManager } from "../lib/sse-manager"
|
import { sseManager } from "../lib/sse-manager"
|
||||||
import { fetchSessions, fetchAgents, fetchProviders, removeSessionIndexes } from "./sessions"
|
import {
|
||||||
|
fetchSessions,
|
||||||
|
fetchAgents,
|
||||||
|
fetchProviders,
|
||||||
|
removeSessionIndexes,
|
||||||
|
clearInstanceDraftPrompts,
|
||||||
|
} from "./sessions"
|
||||||
import { preferences, updateLastUsedBinary } from "./preferences"
|
import { preferences, updateLastUsedBinary } from "./preferences"
|
||||||
|
|
||||||
const [instances, setInstances] = createSignal<Map<string, Instance>>(new Map())
|
const [instances, setInstances] = createSignal<Map<string, Instance>>(new Map())
|
||||||
@@ -69,8 +75,9 @@ function removeInstance(id: string) {
|
|||||||
setActiveInstanceId(null)
|
setActiveInstanceId(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up session indexes for removed instance
|
// Clean up session indexes and drafts for removed instance
|
||||||
removeSessionIndexes(id)
|
removeSessionIndexes(id)
|
||||||
|
clearInstanceDraftPrompts(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createInstance(folder: string, binaryPath?: string): Promise<string> {
|
async function createInstance(folder: string, binaryPath?: string): Promise<string> {
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
import { createSignal } from "solid-js"
|
|
||||||
|
|
||||||
function getSessionKey(instanceId: string, sessionId: string): string {
|
|
||||||
return `${instanceId}:${sessionId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const [prompts, setPrompts] = createSignal<Map<string, string>>(new Map())
|
|
||||||
|
|
||||||
export function getPromptValue(instanceId: string, sessionId: string): string {
|
|
||||||
const key = getSessionKey(instanceId, sessionId)
|
|
||||||
return prompts().get(key) || ""
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPromptValue(instanceId: string, sessionId: string, value: string) {
|
|
||||||
const key = getSessionKey(instanceId, sessionId)
|
|
||||||
setPrompts((prev) => {
|
|
||||||
const next = new Map(prev)
|
|
||||||
if (value.length === 0) {
|
|
||||||
next.delete(key)
|
|
||||||
} else {
|
|
||||||
next.set(key, value)
|
|
||||||
}
|
|
||||||
return next
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clearPromptValue(instanceId: string, sessionId: string) {
|
|
||||||
const key = getSessionKey(instanceId, sessionId)
|
|
||||||
setPrompts((prev) => {
|
|
||||||
const next = new Map(prev)
|
|
||||||
next.delete(key)
|
|
||||||
return next
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -25,6 +25,74 @@ const [activeSessionId, setActiveSessionId] = createSignal<Map<string, string>>(
|
|||||||
const [activeParentSessionId, setActiveParentSessionId] = createSignal<Map<string, string>>(new Map())
|
const [activeParentSessionId, setActiveParentSessionId] = createSignal<Map<string, string>>(new Map())
|
||||||
const [agents, setAgents] = createSignal<Map<string, Agent[]>>(new Map())
|
const [agents, setAgents] = createSignal<Map<string, Agent[]>>(new Map())
|
||||||
const [providers, setProviders] = createSignal<Map<string, Provider[]>>(new Map())
|
const [providers, setProviders] = createSignal<Map<string, Provider[]>>(new Map())
|
||||||
|
const [sessionDraftPrompts, setSessionDraftPrompts] = createSignal<Map<string, string>>(new Map())
|
||||||
|
|
||||||
|
function getDraftKey(instanceId: string, sessionId: string): string {
|
||||||
|
return `${instanceId}:${sessionId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSessionDraftPrompt(instanceId: string, sessionId: string): string {
|
||||||
|
if (!instanceId || !sessionId) return ""
|
||||||
|
const key = getDraftKey(instanceId, sessionId)
|
||||||
|
return sessionDraftPrompts().get(key) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSessionDraftPrompt(instanceId: string, sessionId: string, value: string) {
|
||||||
|
const key = getDraftKey(instanceId, sessionId)
|
||||||
|
setSessionDraftPrompts((prev) => {
|
||||||
|
const next = new Map(prev)
|
||||||
|
if (!value) {
|
||||||
|
next.delete(key)
|
||||||
|
} else {
|
||||||
|
next.set(key, value)
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSessionDraftPrompt(instanceId: string, sessionId: string) {
|
||||||
|
const key = getDraftKey(instanceId, sessionId)
|
||||||
|
setSessionDraftPrompts((prev) => {
|
||||||
|
if (!prev.has(key)) return prev
|
||||||
|
const next = new Map(prev)
|
||||||
|
next.delete(key)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearInstanceDraftPrompts(instanceId: string) {
|
||||||
|
if (!instanceId) return
|
||||||
|
setSessionDraftPrompts((prev) => {
|
||||||
|
let changed = false
|
||||||
|
const next = new Map(prev)
|
||||||
|
const prefix = `${instanceId}:`
|
||||||
|
for (const key of Array.from(next.keys())) {
|
||||||
|
if (key.startsWith(prefix)) {
|
||||||
|
next.delete(key)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed ? next : prev
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function pruneDraftPrompts(instanceId: string, validSessionIds: Set<string>) {
|
||||||
|
setSessionDraftPrompts((prev) => {
|
||||||
|
let changed = false
|
||||||
|
const next = new Map(prev)
|
||||||
|
const prefix = `${instanceId}:`
|
||||||
|
for (const key of Array.from(next.keys())) {
|
||||||
|
if (key.startsWith(prefix)) {
|
||||||
|
const sessionId = key.slice(prefix.length)
|
||||||
|
if (!validSessionIds.has(sessionId)) {
|
||||||
|
next.delete(key)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed ? next : prev
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const [loading, setLoading] = createSignal({
|
const [loading, setLoading] = createSignal({
|
||||||
fetchingSessions: new Map<string, boolean>(),
|
fetchingSessions: new Map<string, boolean>(),
|
||||||
@@ -323,6 +391,8 @@ async function fetchSessions(instanceId: string): Promise<void> {
|
|||||||
next.set(instanceId, sessionMap)
|
next.set(instanceId, sessionMap)
|
||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pruneDraftPrompts(instanceId, new Set(sessionMap.keys()))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch sessions:", error)
|
console.error("Failed to fetch sessions:", error)
|
||||||
throw error
|
throw error
|
||||||
@@ -736,6 +806,8 @@ async function deleteSession(instanceId: string, sessionId: string): Promise<voi
|
|||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
|
||||||
|
clearSessionDraftPrompt(instanceId, sessionId)
|
||||||
|
|
||||||
// Remove session info entry
|
// Remove session info entry
|
||||||
setSessionInfoByInstance((prev) => {
|
setSessionInfoByInstance((prev) => {
|
||||||
const next = new Map(prev)
|
const next = new Map(prev)
|
||||||
@@ -1788,4 +1860,8 @@ export {
|
|||||||
updateSessionModel,
|
updateSessionModel,
|
||||||
getDefaultModel,
|
getDefaultModel,
|
||||||
removeSessionIndexes,
|
removeSessionIndexes,
|
||||||
|
getSessionDraftPrompt,
|
||||||
|
setSessionDraftPrompt,
|
||||||
|
clearSessionDraftPrompt,
|
||||||
|
clearInstanceDraftPrompts,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user