diff --git a/src/biz/Ask.tsx b/src/biz/Ask.tsx
new file mode 100644
index 0000000..9cade4e
--- /dev/null
+++ b/src/biz/Ask.tsx
@@ -0,0 +1,72 @@
+import {AiOutlineCloseCircle, BsDashSquare, BsPlusSquare, FaQuestion} from 'react-icons/all'
+import classNames from 'classnames'
+import Markdown from '../components/Markdown'
+import React, {useCallback} from 'react'
+import {delAskInfo, mergeAskInfo, setPage} from '../redux/envReducer'
+import {useAppDispatch, useAppSelector} from '../hooks/redux'
+import {PAGE_SETTINGS} from '../const'
+import toast from 'react-hot-toast'
+import useTranslate from '../hooks/useTranslate'
+
+const Ask = (props: {
+ ask: AskInfo
+}) => {
+ const {ask} = props
+ const dispatch = useAppDispatch()
+ const envData = useAppSelector(state => state.env.envData)
+ const fontSize = useAppSelector(state => state.env.envData.fontSize)
+ const segments = useAppSelector(state => state.env.segments)
+ const {addAskTask} = useTranslate()
+
+ const onRegenerate = useCallback(() => {
+ const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
+ if (apiKey) {
+ if (segments != null && segments.length > 0) {
+ addAskTask(ask.id, segments[0], ask.question).catch(console.error)
+ }
+ } else {
+ dispatch(setPage(PAGE_SETTINGS))
+ toast.error('需要先设置ApiKey!')
+ }
+ }, [addAskTask, ask.id, ask.question, dispatch, envData.aiType, envData.apiKey, envData.geminiApiKey, segments])
+
+ const onAskFold = useCallback(() => {
+ dispatch(mergeAskInfo({
+ id: ask.id,
+ fold: !ask.fold
+ }))
+ }, [ask, dispatch])
+
+ const onClose = useCallback(() => {
+ dispatch(delAskInfo(ask.id))
+ }, [ask, dispatch])
+
+ return
+
+
+ {ask.fold
+ ? :
+ }
+
+
+
+
+ {!ask.fold && ask.question &&
{ask.question}
}
+ {!ask.fold && ask.content &&
+
+
+
}
+ {!ask.fold &&
}
+ {!ask.fold && ask.error &&
{ask.error}
}
+
+}
+
+export default Ask
diff --git a/src/biz/Body.tsx b/src/biz/Body.tsx
index af8b4c2..4f5007b 100644
--- a/src/biz/Body.tsx
+++ b/src/biz/Body.tsx
@@ -1,7 +1,7 @@
import React, {useCallback, useEffect, useMemo, useRef} from 'react'
import {
- setAskFold,
- setAskQuestion,
+ addAskInfo,
+ mergeAskInfo,
setAutoScroll,
setAutoTranslate,
setCheckAutoScroll,
@@ -16,9 +16,6 @@ import {useAppDispatch, useAppSelector} from '../hooks/redux'
import {
AiOutlineAim,
AiOutlineCloseCircle,
- BsDashSquare,
- BsPlusSquare,
- FaQuestion,
FaRegArrowAltCircleDown,
IoWarning,
MdExpand,
@@ -37,10 +34,10 @@ import {
} from '../const'
import {FaClipboardList} from 'react-icons/fa'
import useTranslate from '../hooks/useTranslate'
-import {getSummarize} from '../util/biz_util'
import {openUrl} from '@kky002/kky-util'
-import Markdown from '../components/Markdown'
import useKeyService from '../hooks/useKeyService'
+import Ask from './Ask'
+import {v4} from 'uuid'
const Body = () => {
const dispatch = useAppDispatch()
@@ -57,20 +54,16 @@ const Body = () => {
const summarizeEnable = useAppSelector(state => state.env.envData.summarizeEnable)
const {addSummarizeTask, addAskTask} = useTranslate()
// const infos = useAppSelector(state => state.env.infos)
- const askFold = useAppSelector(state => state.env.askFold)
- const askQuestion = useAppSelector(state => state.env.askQuestion)
- const askContent = useAppSelector(state => state.env.askContent)
- const askStatus = useAppSelector(state => state.env.askStatus)
- const askError = useAppSelector(state => state.env.askError)
const bodyRef = useRef()
const curOffsetTop = useAppSelector(state => state.env.curOffsetTop)
const checkAutoScroll = useAppSelector(state => state.env.checkAutoScroll)
const needScroll = useAppSelector(state => state.env.needScroll)
const totalHeight = useAppSelector(state => state.env.totalHeight)
const curSummaryType = useAppSelector(state => state.env.tempData.curSummaryType)
- const title = useAppSelector(state => state.env.title)
- const fontSize = useAppSelector(state => state.env.envData.fontSize)
+ // const title = useAppSelector(state => state.env.title)
+ // const fontSize = useAppSelector(state => state.env.envData.fontSize)
const searchText = useAppSelector(state => state.env.searchText)
+ const asks = useAppSelector(state => state.env.asks)
// const recommendIdx = useMemo(() => random(0, 3), [])
const showSearchInput = useMemo(() => {
return (segments != null && segments.length > 0) && (envData.searchEnabled ? envData.searchEnabled : (envData.askEnabled ?? ASK_ENABLED_DEFAULT))
@@ -135,14 +128,19 @@ const Body = () => {
const onFoldAll = useCallback(() => {
dispatch(setFoldAll(!foldAll))
- dispatch(setAskFold(!foldAll))
+ for (const ask of asks) {
+ dispatch(mergeAskInfo({
+ id: ask.id,
+ fold: !foldAll
+ }))
+ }
for (const segment of segments ?? []) {
dispatch(setSegmentFold({
segmentStartIdx: segment.startIdx,
fold: !foldAll
}))
}
- }, [dispatch, foldAll, segments])
+ }, [asks, dispatch, foldAll, segments])
const toggleAutoTranslateCallback = useCallback(() => {
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
@@ -165,14 +163,14 @@ const Body = () => {
}
}, [autoScroll, dispatch])
- const onCopy = useCallback(() => {
- const [success, content] = getSummarize(title, segments, curSummaryType)
- if (success) {
- navigator.clipboard.writeText(content).then(() => {
- toast.success('复制成功')
- }).catch(console.error)
- }
- }, [curSummaryType, segments, title])
+ // const onCopy = useCallback(() => {
+ // const [success, content] = getSummarize(title, segments, curSummaryType)
+ // if (success) {
+ // navigator.clipboard.writeText(content).then(() => {
+ // toast.success('复制成功')
+ // }).catch(console.error)
+ // }
+ // }, [curSummaryType, segments, title])
const onSearchTextChange = useCallback((e: any) => {
const searchText = e.target.value
@@ -188,8 +186,14 @@ const Body = () => {
const apiKey = envData.aiType === 'gemini'?envData.geminiApiKey:envData.apiKey
if (apiKey) {
if (segments != null && segments.length > 0) {
- dispatch(setAskQuestion(searchText))
- addAskTask(segments[0], searchText).catch(console.error)
+ const id = v4()
+ addAskTask(id, segments[0], searchText).catch(console.error)
+ // 添加ask
+ dispatch(addAskInfo({
+ id,
+ question: searchText,
+ status: 'pending',
+ }))
}
} else {
dispatch(setPage(PAGE_SETTINGS))
@@ -198,14 +202,6 @@ const Body = () => {
}
}, [addAskTask, dispatch, envData.aiType, envData.apiKey, envData.askEnabled, envData.geminiApiKey, searchText, segments])
- const onSetAsk = useCallback(() => {
- dispatch(setSearchText(askQuestion??''))
- }, [askQuestion, dispatch])
-
- const onAskFold = useCallback(() => {
- dispatch(setAskFold(!askFold))
- }, [askFold, dispatch])
-
// service
useKeyService()
@@ -282,33 +278,8 @@ const Body = () => {
height: `${totalHeight - HEADER_HEIGHT - TITLE_HEIGHT - (showSearchInput ? SEARCH_BAR_HEIGHT : 0)}px`
}}
>
- {/* ask */}
- {(envData.askEnabled ?? ASK_ENABLED_DEFAULT) && (searchText || askQuestion) &&
-
-
-
- {askFold
- ? :
- }
-
-
-
- {!askFold && askQuestion &&
-
{askQuestion}
}
- {!askFold && askContent &&
-
-
-
}
- {!askFold &&
}
- {!askFold && askStatus === 'init' &&
提问举例:这个视频说了什么
}
- {!askFold && askError &&
{askError}
}
-
}
+ {/* asks */}
+ {asks.map(ask => )}
{/* segments */}
{segments?.map((segment, segmentIdx) => {
}
}, [dispatch, envData, summarizeLanguage.name, title])
- const addAskTask = useCallback(async (segment: Segment, question: string) => {
+ const addAskTask = useCallback(async (id: string, segment: Segment, question: string) => {
if (segment.text.length >= SUMMARIZE_THRESHOLD) {
let prompt: string = envData.prompts?.[PROMPT_TYPE_ASK]??PROMPT_DEFAULTS[PROMPT_TYPE_ASK]
// replace params
@@ -243,10 +241,14 @@ const useTranslate = () => {
// startIdx: segment.startIdx,
apiKey: envData.apiKey,
geminiApiKey: envData.geminiApiKey,
+ askId: id,
}
}
console.debug('addAskTask', taskDef)
- dispatch(setAskStatus({status: 'pending'}))
+ dispatch(mergeAskInfo({
+ id,
+ status: 'pending'
+ }))
const task = await chrome.runtime.sendMessage({type: 'addTask', taskDef})
dispatch(addTaskId(task.id))
}
@@ -304,9 +306,13 @@ const useTranslate = () => {
})
const handleAsk = useMemoizedFn((task: Task, content?: string) => {
- dispatch(setAskContent({content}))
- dispatch(setAskStatus({status: 'done'}))
- dispatch(setAskError({error: task.error}))
+ dispatch(mergeAskInfo({
+ id: task.def.extra.askId,
+ content,
+ status: 'done',
+ error: task.error,
+ }))
+
console.debug('setAsk', content, task.error)
})
diff --git a/src/redux/envReducer.ts b/src/redux/envReducer.ts
index 1b1014a..92d48e7 100644
--- a/src/redux/envReducer.ts
+++ b/src/redux/envReducer.ts
@@ -1,5 +1,5 @@
import {createSlice, PayloadAction} from '@reduxjs/toolkit'
-import {find} from 'lodash-es'
+import {find, findIndex} from 'lodash-es'
import {getDevData} from '../util/biz_util'
import {DEFAULT_SERVER_URL_OPENAI, TOTAL_HEIGHT_DEF} from '../const'
@@ -39,11 +39,7 @@ interface EnvState {
lastSummarizeTime?: number
// ask
- askFold?: boolean
- askQuestion?: string
- askStatus: SummaryStatus
- askError?: string
- askContent?: string
+ asks: AskInfo[]
/**
* 是否输入中(中文)
@@ -66,7 +62,6 @@ const initialState: EnvState = {
tempData: {
curSummaryType: 'overview',
},
- askStatus: 'init',
totalHeight: TOTAL_HEIGHT_DEF,
autoScroll: true,
currentTime: import.meta.env.VITE_ENV === 'web-dev' ? 30 : undefined,
@@ -80,6 +75,8 @@ const initialState: EnvState = {
searchText: '',
searchResult: new Set(),
+
+ asks: [],
}
export const slice = createSlice({
@@ -211,26 +208,20 @@ export const slice = createSlice({
}
}
},
- setAskFold: (state, action: PayloadAction) => {
- state.askFold = action.payload
+ addAskInfo: (state, action: PayloadAction) => {
+ state.asks.push(action.payload)
},
- setAskQuestion: (state, action: PayloadAction) => {
- state.askQuestion = action.payload
+ delAskInfo: (state, action: PayloadAction) => {
+ state.asks = state.asks.filter(ask => ask.id !== action.payload)
},
- setAskContent: (state, action: PayloadAction<{
- content?: any
- }>) => {
- state.askContent = action.payload.content
- },
- setAskStatus: (state, action: PayloadAction<{
- status: SummaryStatus
- }>) => {
- state.askStatus = action.payload.status
- },
- setAskError: (state, action: PayloadAction<{
- error?: string
- }>) => {
- state.askError = action.payload.error
+ mergeAskInfo: (state, action: PayloadAction) => {
+ const idx = findIndex(state.asks, {id: action.payload.id})
+ if (idx >= 0) {
+ state.asks[idx] = {
+ ...state.asks[idx],
+ ...action.payload,
+ }
+ }
},
setSegmentFold: (state, action: PayloadAction<{
segmentStartIdx: number
@@ -303,11 +294,6 @@ export const slice = createSlice({
export const {
setUrl,
- setAskFold,
- setAskQuestion,
- setAskStatus,
- setAskError,
- setAskContent,
setTempReady,
setTempData,
setUploadedTranscript,
@@ -346,6 +332,9 @@ export const {
setSearchText,
setSearchResult,
setInputting,
+ addAskInfo,
+ delAskInfo,
+ mergeAskInfo,
} = slice.actions
export default slice.reducer
diff --git a/src/typings.d.ts b/src/typings.d.ts
index 0493b81..806d57e 100644
--- a/src/typings.d.ts
+++ b/src/typings.d.ts
@@ -105,6 +105,17 @@ interface Summary {
content?: any
}
+interface AskInfo {
+ id: string
+ fold?: boolean
+ question: string
+ status: SummaryStatus
+ error?: string
+ content?: string
+}
+
+type PartialOfAskInfo = Partial
+
/**
* 概览
*/