- Add SessionManager class to handle PTY sessions with WebSocket connections. - Implement methods for creating, retrieving, and destroying sessions. - Handle PTY output and WebSocket messages for terminal interaction. - Ensure graceful session destruction and cleanup. feat: initialize web application with Next.js and Tailwind CSS - Create initial Next.js application structure with TypeScript support. - Set up Tailwind CSS for styling with custom theme configurations. - Add ESLint configuration for code quality and consistency. feat: implement chat API and UI components - Create chat API route to handle chat requests and responses. - Develop chat layout with sidebar, header, chat window, and input components. - Integrate Zustand for state management of conversations and messages. - Add utility functions for formatting dates and managing class names. chore: add environment variables and configuration files - Create .env.example for environment variable setup. - Add configuration files for PostCSS, Tailwind CSS, and TypeScript. - Set up package.json with necessary dependencies and scripts for development.
124 lines
3.5 KiB
TypeScript
124 lines
3.5 KiB
TypeScript
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
import { nanoid } from "nanoid";
|
|
import type { Conversation, Message, AppSettings } from "./types";
|
|
import { DEFAULT_MODEL } from "./constants";
|
|
|
|
interface ChatState {
|
|
conversations: Conversation[];
|
|
activeConversationId: string | null;
|
|
settings: AppSettings;
|
|
|
|
// Actions
|
|
createConversation: () => string;
|
|
setActiveConversation: (id: string) => void;
|
|
deleteConversation: (id: string) => void;
|
|
addMessage: (conversationId: string, message: Omit<Message, "id" | "createdAt">) => string;
|
|
updateMessage: (conversationId: string, messageId: string, updates: Partial<Message>) => void;
|
|
updateSettings: (settings: Partial<AppSettings>) => void;
|
|
getActiveConversation: () => Conversation | null;
|
|
}
|
|
|
|
export const useChatStore = create<ChatState>()(
|
|
persist(
|
|
(set, get) => ({
|
|
conversations: [],
|
|
activeConversationId: null,
|
|
settings: {
|
|
theme: "dark",
|
|
model: DEFAULT_MODEL,
|
|
apiUrl: process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:3001",
|
|
streamingEnabled: true,
|
|
},
|
|
|
|
createConversation: () => {
|
|
const id = nanoid();
|
|
const now = Date.now();
|
|
const conversation: Conversation = {
|
|
id,
|
|
title: "New conversation",
|
|
messages: [],
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
model: get().settings.model,
|
|
};
|
|
set((state) => ({
|
|
conversations: [conversation, ...state.conversations],
|
|
activeConversationId: id,
|
|
}));
|
|
return id;
|
|
},
|
|
|
|
setActiveConversation: (id) => {
|
|
set({ activeConversationId: id });
|
|
},
|
|
|
|
deleteConversation: (id) => {
|
|
set((state) => {
|
|
const remaining = state.conversations.filter((c) => c.id !== id);
|
|
const nextActive =
|
|
state.activeConversationId === id
|
|
? (remaining[0]?.id ?? null)
|
|
: state.activeConversationId;
|
|
return { conversations: remaining, activeConversationId: nextActive };
|
|
});
|
|
},
|
|
|
|
addMessage: (conversationId, message) => {
|
|
const id = nanoid();
|
|
const now = Date.now();
|
|
set((state) => ({
|
|
conversations: state.conversations.map((c) =>
|
|
c.id === conversationId
|
|
? {
|
|
...c,
|
|
messages: [...c.messages, { ...message, id, createdAt: now }],
|
|
updatedAt: now,
|
|
}
|
|
: c
|
|
),
|
|
}));
|
|
return id;
|
|
},
|
|
|
|
updateMessage: (conversationId, messageId, updates) => {
|
|
set((state) => ({
|
|
conversations: state.conversations.map((c) =>
|
|
c.id === conversationId
|
|
? {
|
|
...c,
|
|
messages: c.messages.map((m) =>
|
|
m.id === messageId ? { ...m, ...updates } : m
|
|
),
|
|
updatedAt: Date.now(),
|
|
}
|
|
: c
|
|
),
|
|
}));
|
|
},
|
|
|
|
updateSettings: (settings) => {
|
|
set((state) => ({
|
|
settings: { ...state.settings, ...settings },
|
|
}));
|
|
},
|
|
|
|
getActiveConversation: () => {
|
|
const state = get();
|
|
return (
|
|
state.conversations.find((c) => c.id === state.activeConversationId) ??
|
|
null
|
|
);
|
|
},
|
|
}),
|
|
{
|
|
name: "claude-code-chat",
|
|
partialize: (state) => ({
|
|
conversations: state.conversations,
|
|
activeConversationId: state.activeConversationId,
|
|
settings: state.settings,
|
|
}),
|
|
}
|
|
)
|
|
);
|