- centralize Pico chat connection and session state in a shared store - move chat lifecycle control out of usePicoChat - hydrate and restore the active session across the app
72 lines
1.6 KiB
TypeScript
72 lines
1.6 KiB
TypeScript
import dayjs from "dayjs"
|
|
import { useAtomValue } from "jotai"
|
|
|
|
import {
|
|
newChatSession,
|
|
sendChatMessage,
|
|
switchChatSession,
|
|
} from "@/lib/pico-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)
|
|
}
|
|
|
|
// Helper to format message timestamps
|
|
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")
|
|
}
|
|
|
|
// Cross-day formatting
|
|
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,
|
|
}
|
|
}
|