Files
app/frontend/src/components/Chat/chatStore.ts
T

48 lines
1.5 KiB
TypeScript

import { useSyncExternalStore } from "react"
/** What the panel is talking to: the assistant, or a wren node in a flow. */
export type ChatTab = "build" | "control"
/**
* Whether the assistant is open, on which tab, and whether another panel is
* open beside it.
*
* Module state rather than context because the chat is mounted once in the
* sidebar and toggled from there, while the editor's own panels report in
* from a different tree: the two only have to agree on who takes which half
* of the lane.
*/
type State = { open: boolean; tab: ChatTab; panels: number }
let state: State = { open: false, tab: "build", panels: 0 }
const listeners = new Set<() => void>()
function set(next: Partial<State>) {
state = { ...state, ...next }
for (const listener of listeners) listener()
}
export const chatStore = {
/** Open it, on a given tab or on whichever was last shown. */
open: (tab?: ChatTab) => set(tab ? { open: true, tab } : { open: true }),
show: (tab: ChatTab) => set({ tab }),
close: () => set({ open: false }),
toggle: () => set({ open: !state.open }),
/** A floating panel is open; returns how to say it closed again. */
registerPanel: () => {
set({ panels: state.panels + 1 })
return () => set({ panels: state.panels - 1 })
},
}
export function useChat(): State {
return useSyncExternalStore(
(listener) => {
listeners.add(listener)
return () => listeners.delete(listener)
},
() => state,
() => state,
)
}