diff --git a/package.json b/package.json index 67d05d1..8c1916f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "talk-vite", "private": true, - "version": "1.2.7", + "version": "1.2.8", "type": "module", "scripts": { "dev": "vite", diff --git a/src/api/sse/server-ability.ts b/src/api/sse/server-ability.ts index fb673a2..c00844b 100644 --- a/src/api/sse/server-ability.ts +++ b/src/api/sse/server-ability.ts @@ -1,5 +1,6 @@ // ServerAbility guide clients in adjusting all parameters. export type ServerAbility = { + demo: boolean llm: ServerLLM tts: ServerTTS stt: ServerSTT @@ -59,6 +60,7 @@ export type TaggedItem = { export const defaultServerAbility = (): ServerAbility => { return { + demo: false, llm: { available: false, chatGPT: { diff --git a/src/api/sse/sse.tsx b/src/api/sse/sse.tsx index 459c481..b491b03 100644 --- a/src/api/sse/sse.tsx +++ b/src/api/sse/sse.tsx @@ -5,12 +5,13 @@ import {appState, findChatProxy, findMessage, findMessage2} from "../../state/ap import {ServerAbility} from "./server-ability.ts" import {newError, newThinking, onAudio, onEOF, onError, onThinking, onTyping} from "../../data-structure/message.tsx" import { + EventKeepAlive, EventMessageAudio, EventMessageError, EventMessageTextEOF, EventMessageTextTyping, EventMessageThinking, - EventSystemAbility, EventKeepAlive, + EventSystemAbility, SSEMsgAudio, SSEMsgError, SSEMsgMeta, @@ -20,6 +21,7 @@ import {base64ToBlob, generateAudioId, randomHash32Char} from "../../util/util.t import {audioDb} from "../../state/db.ts" import {audioPlayerMimeType, SSEEndpoint} from "../../config.ts" import {adjustOption} from "../../data-structure/client-option.tsx" +import {createDemoChatIfNecessary} from "../../data/chat.ts"; export const SSE = () => { @@ -53,6 +55,7 @@ export const SSE = () => { } // eslint-disable-next-line valtio/state-snapshot-rule appState.ability = sa + createDemoChatIfNecessary() }) eventSource.addEventListener(EventMessageThinking, (event: MessageEvent) => { diff --git a/src/data-structure/message.tsx b/src/data-structure/message.tsx index 5c55756..e1b278e 100644 --- a/src/data-structure/message.tsx +++ b/src/data-structure/message.tsx @@ -28,7 +28,6 @@ export type Message = { errorMessage?: string createdAt: number lastUpdatedAt: number - } export const newThinking = (id: string, ticketId: string, role: Role): Message => ({ @@ -62,6 +61,27 @@ export const newSending = (): Message => ({ lastUpdatedAt: Date.now(), }) +export const newSent = (text: string): Message => ({ + id: randomHash16Char(), + ticketId: randomHash16Char(), + role: "user", + status: "sent", + text: text, + createdAt: Date.now(), + lastUpdatedAt: Date.now(), +}) + +export const newReceived = (text: string): Message => ({ + id: randomHash16Char(), + ticketId: randomHash16Char(), + role: "assistant", + status: "received", + text: text, + createdAt: Date.now(), + lastUpdatedAt: Date.now(), +}) + + export const onThinking = (message: Message): void => { switch (message.status) { case "thinking": diff --git a/src/data/chat.ts b/src/data/chat.ts new file mode 100644 index 0000000..dca6ac5 --- /dev/null +++ b/src/data/chat.ts @@ -0,0 +1,16 @@ +import {newReceived, newSent} from "../data-structure/message.tsx"; +import {appState, createChat} from "../state/app-state.ts"; + +export const createDemoChatIfNecessary = () => { + if (appState.ability.demo && appState.chats.length === 0 && !appState.pref.dismissDemo) { + createChat("Demo", [ + newSent("Hello!"), + newReceived(`Hello! How may I assist you today? + +Feel free to ask me anything. Please note, I’ll reply with **pseudo** text and voice. + +For genuine AI responses, you may want to set up your own instance following the instructions at +[proxoar/talk](https://github.com/proxoar/talk).`), + ]) + } +} \ No newline at end of file diff --git a/src/home/panel/chat-list/chat-list.tsx b/src/home/panel/chat-list/chat-list.tsx index 364791e..5a46b36 100644 --- a/src/home/panel/chat-list/chat-list.tsx +++ b/src/home/panel/chat-list/chat-list.tsx @@ -1,11 +1,9 @@ import React, {memo, useCallback, useEffect, useRef, useState} from "react" import {useSnapshot} from "valtio/react" -import {proxy, subscribe} from "valtio" -import _ from "lodash" +import {subscribe} from "valtio" import {PiPlusLight} from "react-icons/pi" import {CiSearch} from "react-icons/ci" -import {appState, Chat} from "../../../state/app-state.ts" -import {randomHash16Char} from "../../../util/util.tsx" +import {appState, Chat, createChat} from "../../../state/app-state.ts" import {DndProvider} from "react-dnd" import {HTML5Backend} from "react-dnd-html5-backend" import {DraggableChat} from "./draggable-chat.tsx" @@ -45,17 +43,9 @@ const ChatList_ = () => { const newChat = useCallback((e: React.MouseEvent) => { e.stopPropagation() - const optionClone = _.cloneDeep(appState.option) - const chat = proxy({ - id: randomHash16Char(), - name: "New Chat", - promptId: "", - messages: [], - option: optionClone, - inputText: "" - }) - appState.chats.push(chat) - appState.currentChatId = chat.id + createChat("New Chat", []) + // at this point, we suppose the user has see the demo chat + appState.pref.dismissDemo = true }, []) // delete other chats should not trigger auto scrolling diff --git a/src/state/app-state.ts b/src/state/app-state.ts index f52c078..111d733 100644 --- a/src/state/app-state.ts +++ b/src/state/app-state.ts @@ -1,9 +1,9 @@ import {proxy, snapshot, subscribe} from 'valtio' -import {talkDB, appStateKey, deleteBlobs} from "./db.ts" +import {appStateKey, deleteBlobs, talkDB} from "./db.ts" import {Message, onMarkDeleted} from "../data-structure/message.tsx" import {ClientOption, defaultOption} from "../data-structure/client-option.tsx" import {defaultServerAbility, ServerAbility} from "../api/sse/server-ability.ts" -import {generateHash} from "../util/util.tsx" +import {generateHash, randomHash16Char} from "../util/util.tsx" import {migrateAppState} from "./migration.ts" import * as packageJson from '../../package.json' import _ from "lodash"; @@ -31,7 +31,9 @@ export type Wallpaper = { } export type UserPreference = { butterflyOnAttachedMessage: boolean - wallpaper: Wallpaper + wallpaper: Wallpaper, + // stop creating demo chat or not + dismissDemo: boolean, } export interface AppState { @@ -68,7 +70,8 @@ export const appState = proxy({ butterflyOnAttachedMessage: true, wallpaper: { index: 0, - } + }, + dismissDemo: false, } }) @@ -87,7 +90,8 @@ export const defaultAppState = (): AppState => ({ butterflyOnAttachedMessage: true, wallpaper: { index: 0, - } + }, + dismissDemo: false, } }) @@ -192,19 +196,6 @@ export const currentChatProxy = (): Chat | undefined => { return undefined } -export const currentChatSnap = (): Chat | undefined => { - if (appState.currentChatId === "") { - return undefined - } - for (let i = appState.chats.length - 1; i >= 0; i--) { - const chat = appState.chats[i] - if (chat.id === appState.currentChatId) { - return snapshot(chat) as Chat - } - } - return undefined -} - const removeChatByIndex = (index: number) => { appState.chats.splice(index, 1) } @@ -289,3 +280,17 @@ export const deleteChat = (id: string) => { } } } + +export const createChat = (name: string, messages: Message[]) => { + const optionClone = _.cloneDeep(appState.option) + const newChat = proxy({ + id: randomHash16Char(), + name: name, + promptId: "", + messages: messages, + option: optionClone, + inputText: "" + }) + appState.chats.push(newChat) + appState.currentChatId = newChat.id +} \ No newline at end of file diff --git a/src/state/migration.ts b/src/state/migration.ts index 8271a27..a950d34 100644 --- a/src/state/migration.ts +++ b/src/state/migration.ts @@ -48,6 +48,14 @@ const steps: Step[] = [ app.option.llm.maxAttached = defaultOption().llm.maxAttached return null } + }, + { + fromVersion: "1.2.7", + toVersion: "1.2.8", + action: (app: AppState): Error | null => { + app.pref.dismissDemo=false + return null + } } ]