mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
dea06c391c
* Improve the web launcher and gateway integration across backend and frontend. - add runtime model availability checks for local and OAuth-backed models - support launcher-driven gateway host overrides and websocket URL resolution - add gateway log clearing and keep incremental log sync consistent after resets - migrate session history APIs to JSONL metadata-backed storage with legacy fallback - expose session titles and improve chat history loading and error handling - move shared backend runtime helpers into the web utils package - avoid blocking web startup when automatic onboard initialization fails - add backend tests covering gateway readiness, host resolution, models, logs, and sessions * feat(agent): add skills and tools management APIs and UI - add backend APIs to list, view, import, and delete skills - add tool status and toggle endpoints with dependency-aware config updates - add agent skills/tools pages, routes, sidebar entries, and i18n strings - add backend tests for the new skills and tools flows * chore(frontend): upgrade shadcn to 4.0.5 and refresh lockfile * chore(web): keep backend dist placeholder tracked
113 lines
2.9 KiB
TypeScript
113 lines
2.9 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { useTranslation } from "react-i18next"
|
|
|
|
import { type SessionSummary, deleteSession, getSessions } from "@/api/sessions"
|
|
|
|
const LIMIT = 20
|
|
|
|
interface UseSessionHistoryOptions {
|
|
activeSessionId: string
|
|
onDeletedActiveSession: () => void
|
|
}
|
|
|
|
export function useSessionHistory({
|
|
activeSessionId,
|
|
onDeletedActiveSession,
|
|
}: UseSessionHistoryOptions) {
|
|
const { t } = useTranslation()
|
|
const observerRef = useRef<HTMLDivElement>(null)
|
|
const [sessions, setSessions] = useState<SessionSummary[]>([])
|
|
const [offset, setOffset] = useState(0)
|
|
const [hasMore, setHasMore] = useState(true)
|
|
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
|
const [loadError, setLoadError] = useState(false)
|
|
|
|
const loadSessions = useCallback(
|
|
async (reset = true) => {
|
|
try {
|
|
const currentOffset = reset ? 0 : offset
|
|
if (reset) {
|
|
setLoadError(false)
|
|
setHasMore(true)
|
|
setOffset(0)
|
|
}
|
|
|
|
const data = await getSessions(currentOffset, LIMIT)
|
|
setLoadError(false)
|
|
|
|
if (data.length < LIMIT) {
|
|
setHasMore(false)
|
|
}
|
|
|
|
if (reset) {
|
|
setSessions(data)
|
|
} else {
|
|
setSessions((prev) => {
|
|
const existingIds = new Set(prev.map((s) => s.id))
|
|
const newItems = data.filter((s) => !existingIds.has(s.id))
|
|
return [...prev, ...newItems]
|
|
})
|
|
}
|
|
|
|
setOffset(currentOffset + data.length)
|
|
} catch (err) {
|
|
console.error("Failed to fetch session history:", err)
|
|
setLoadError(true)
|
|
if (!reset) {
|
|
setHasMore(false)
|
|
}
|
|
} finally {
|
|
setIsLoadingMore(false)
|
|
}
|
|
},
|
|
[offset],
|
|
)
|
|
|
|
useEffect(() => {
|
|
if (!observerRef.current || !hasMore || isLoadingMore || loadError) return
|
|
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
if (
|
|
entries[0].isIntersecting &&
|
|
hasMore &&
|
|
!isLoadingMore &&
|
|
!loadError
|
|
) {
|
|
setIsLoadingMore(true)
|
|
void loadSessions(false)
|
|
}
|
|
},
|
|
{ threshold: 0.1 },
|
|
)
|
|
|
|
observer.observe(observerRef.current)
|
|
return () => observer.disconnect()
|
|
}, [hasMore, isLoadingMore, loadError, loadSessions])
|
|
|
|
const handleDeleteSession = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
await deleteSession(id)
|
|
setSessions((prev) => prev.filter((s) => s.id !== id))
|
|
if (id === activeSessionId) {
|
|
onDeletedActiveSession()
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to delete session:", err)
|
|
}
|
|
},
|
|
[activeSessionId, onDeletedActiveSession],
|
|
)
|
|
|
|
return {
|
|
sessions,
|
|
hasMore,
|
|
loadError,
|
|
loadErrorMessage: t("chat.historyLoadFailed"),
|
|
observerRef,
|
|
loadSessions,
|
|
handleDeleteSession,
|
|
}
|
|
}
|