picoclaw/web/frontend/src/hooks/use-pico-chat.ts
wenjie c513ad22d7
fix(web): refactor pico chat flow and fix proxied websocket URLs (#1639)
- move chat controller, state, protocol, history, and websocket logic into a dedicated chat feature module
- improve chat reconnection, session hydration, and send gating based on actual websocket state
- preserve gateway status during transient SSE disconnects and update stop state immediately
- generate wss websocket URLs behind HTTPS proxies and add backend tests for forwarded proto handling
2026-03-16 16:25:16 +08:00

70 lines
1.5 KiB
TypeScript

import dayjs from "dayjs"
import { useAtomValue } from "jotai"
import {
newChatSession,
sendChatMessage,
switchChatSession,
} from "@/features/chat/controller"
import { chatAtom } from "@/store/chat"
const UNIX_MS_THRESHOLD = 1e12
function normalizeUnixTimestamp(timestamp: number): number {
return timestamp < UNIX_MS_THRESHOLD ? timestamp * 1000 : timestamp
}
function parseTimestamp(dateRaw: number | string | Date) {
if (typeof dateRaw === "number") {
return dayjs(normalizeUnixTimestamp(dateRaw))
}
if (typeof dateRaw === "string") {
const trimmed = dateRaw.trim()
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
const numeric = Number(trimmed)
if (Number.isFinite(numeric)) {
return dayjs(normalizeUnixTimestamp(numeric))
}
}
return dayjs(trimmed)
}
return dayjs(dateRaw)
}
export function formatMessageTime(dateRaw: number | string | Date): string {
const date = parseTimestamp(dateRaw)
if (!date.isValid()) {
return ""
}
const now = dayjs()
const isToday = date.isSame(now, "day")
const isThisYear = date.isSame(now, "year")
if (isToday) {
return date.format("LT")
}
if (isThisYear) {
return date.format("MMM D LT")
}
return date.format("ll LT")
}
export function usePicoChat() {
const { messages, connectionState, isTyping, activeSessionId } =
useAtomValue(chatAtom)
return {
messages,
connectionState,
isTyping,
activeSessionId,
sendMessage: sendChatMessage,
switchSession: switchChatSession,
newChat: newChatSession,
}
}