Compare commits
3 Commits
dev
...
release/v0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
804b0f0116 | ||
|
|
4727132d3c | ||
|
|
4f5bde4964 |
@ -30,14 +30,31 @@ const ThinkingBlock = ({ id, text }: Props) => {
|
|||||||
const { thinkingState, setThinkingState } = useThinkingStore()
|
const { thinkingState, setThinkingState } = useThinkingStore()
|
||||||
const { streamingContent } = useAppState()
|
const { streamingContent } = useAppState()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const loading = !text.includes('</think>') && streamingContent
|
// Check for thinking formats
|
||||||
|
const hasThinkTag = text.includes('<think>') && !text.includes('</think>')
|
||||||
|
const hasAnalysisChannel = text.includes('<|channel|>analysis<|message|>') && !text.includes('<|start|>assistant<|channel|>final<|message|>')
|
||||||
|
const loading = (hasThinkTag || hasAnalysisChannel) && streamingContent
|
||||||
const isExpanded = thinkingState[id] ?? (loading ? true : false)
|
const isExpanded = thinkingState[id] ?? (loading ? true : false)
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
const newExpandedState = !isExpanded
|
const newExpandedState = !isExpanded
|
||||||
setThinkingState(id, newExpandedState)
|
setThinkingState(id, newExpandedState)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!text.replace(/<\/?think>/g, '').trim()) return null
|
// Extract thinking content from either format
|
||||||
|
const extractThinkingContent = (text: string) => {
|
||||||
|
return text
|
||||||
|
.replace(/<\/?think>/g, '')
|
||||||
|
.replace(/<\|channel\|>analysis<\|message\|>/g, '')
|
||||||
|
.replace(/<\|start\|>assistant<\|channel\|>final<\|message\|>/g, '')
|
||||||
|
.replace(/assistant<\|channel\|>final<\|message\|>/g, '')
|
||||||
|
.replace(/<\|channel\|>/g, '') // remove any remaining channel markers
|
||||||
|
.replace(/<\|message\|>/g, '') // remove any remaining message markers
|
||||||
|
.replace(/<\|start\|>/g, '') // remove any remaining start markers
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
const thinkingContent = extractThinkingContent(text)
|
||||||
|
if (!thinkingContent) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -63,7 +80,7 @@ const ThinkingBlock = ({ id, text }: Props) => {
|
|||||||
|
|
||||||
{isExpanded && (
|
{isExpanded && (
|
||||||
<div className="mt-2 pl-6 pr-4 text-main-view-fg/60">
|
<div className="mt-2 pl-6 pr-4 text-main-view-fg/60">
|
||||||
<RenderMarkdown content={text.replace(/<\/?think>/g, '').trim()} />
|
<RenderMarkdown content={thinkingContent} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -170,18 +170,33 @@ export const ThreadContent = memo(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const { reasoningSegment, textSegment } = useMemo(() => {
|
const { reasoningSegment, textSegment } = useMemo(() => {
|
||||||
const isThinking = text.includes('<think>') && !text.includes('</think>')
|
// Check for thinking formats
|
||||||
if (isThinking) return { reasoningSegment: text, textSegment: '' }
|
const hasThinkTag = text.includes('<think>') && !text.includes('</think>')
|
||||||
|
const hasAnalysisChannel = text.includes('<|channel|>analysis<|message|>') && !text.includes('<|start|>assistant<|channel|>final<|message|>')
|
||||||
|
|
||||||
const match = text.match(/<think>([\s\S]*?)<\/think>/)
|
if (hasThinkTag || hasAnalysisChannel) return { reasoningSegment: text, textSegment: '' }
|
||||||
if (match?.index === undefined)
|
|
||||||
return { reasoningSegment: undefined, textSegment: text }
|
|
||||||
|
|
||||||
const splitIndex = match.index + match[0].length
|
// Check for completed think tag format
|
||||||
|
const thinkMatch = text.match(/<think>([\s\S]*?)<\/think>/)
|
||||||
|
if (thinkMatch?.index !== undefined) {
|
||||||
|
const splitIndex = thinkMatch.index + thinkMatch[0].length
|
||||||
return {
|
return {
|
||||||
reasoningSegment: text.slice(0, splitIndex),
|
reasoningSegment: text.slice(0, splitIndex),
|
||||||
textSegment: text.slice(splitIndex),
|
textSegment: text.slice(splitIndex),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for completed analysis channel format
|
||||||
|
const analysisMatch = text.match(/<\|channel\|>analysis<\|message\|>([\s\S]*?)<\|start\|>assistant<\|channel\|>final<\|message\|>/)
|
||||||
|
if (analysisMatch?.index !== undefined) {
|
||||||
|
const splitIndex = analysisMatch.index + analysisMatch[0].length
|
||||||
|
return {
|
||||||
|
reasoningSegment: text.slice(0, splitIndex),
|
||||||
|
textSegment: text.slice(splitIndex),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { reasoningSegment: undefined, textSegment: text }
|
||||||
}, [text])
|
}, [text])
|
||||||
|
|
||||||
const { getMessages, deleteMessage } = useMessages()
|
const { getMessages, deleteMessage } = useMessages()
|
||||||
|
|||||||
@ -266,14 +266,7 @@ describe('useSmallScreenStore', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('useSmallScreen', () => {
|
describe('useSmallScreen', () => {
|
||||||
beforeEach(() => {
|
it('should return small screen state', () => {
|
||||||
// Reset the store state before each test
|
|
||||||
act(() => {
|
|
||||||
useSmallScreenStore.getState().setIsSmallScreen(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return small screen state and update store', () => {
|
|
||||||
const mockMediaQueryList = {
|
const mockMediaQueryList = {
|
||||||
matches: true,
|
matches: true,
|
||||||
addEventListener: vi.fn(),
|
addEventListener: vi.fn(),
|
||||||
@ -285,7 +278,6 @@ describe('useSmallScreen', () => {
|
|||||||
const { result } = renderHook(() => useSmallScreen())
|
const { result } = renderHook(() => useSmallScreen())
|
||||||
|
|
||||||
expect(result.current).toBe(true)
|
expect(result.current).toBe(true)
|
||||||
expect(useSmallScreenStore.getState().isSmallScreen).toBe(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update when media query changes', () => {
|
it('should update when media query changes', () => {
|
||||||
@ -309,7 +301,6 @@ describe('useSmallScreen', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(result.current).toBe(true)
|
expect(result.current).toBe(true)
|
||||||
expect(useSmallScreenStore.getState().isSmallScreen).toBe(true)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should use correct media query for small screen detection', () => {
|
it('should use correct media query for small screen detection', () => {
|
||||||
@ -325,20 +316,4 @@ describe('useSmallScreen', () => {
|
|||||||
|
|
||||||
expect(mockMatchMedia).toHaveBeenCalledWith('(max-width: 768px)')
|
expect(mockMatchMedia).toHaveBeenCalledWith('(max-width: 768px)')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should persist state across multiple hook instances', () => {
|
|
||||||
const mockMediaQueryList = {
|
|
||||||
matches: true,
|
|
||||||
addEventListener: vi.fn(),
|
|
||||||
removeEventListener: vi.fn(),
|
|
||||||
}
|
|
||||||
|
|
||||||
mockMatchMedia.mockReturnValue(mockMediaQueryList)
|
|
||||||
|
|
||||||
const { result: result1 } = renderHook(() => useSmallScreen())
|
|
||||||
const { result: result2 } = renderHook(() => useSmallScreen())
|
|
||||||
|
|
||||||
expect(result1.current).toBe(true)
|
|
||||||
expect(result2.current).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
@ -77,14 +77,7 @@ export function useMediaQuery(
|
|||||||
return matches || false
|
return matches || false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific hook for small screen detection with state management
|
// Specific hook for small screen detection
|
||||||
export const useSmallScreen = (): boolean => {
|
export const useSmallScreen = (): boolean => {
|
||||||
const { isSmallScreen, setIsSmallScreen } = useSmallScreenStore()
|
return useMediaQuery('(max-width: 768px)')
|
||||||
const mediaQuery = useMediaQuery('(max-width: 768px)')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsSmallScreen(mediaQuery)
|
|
||||||
}, [mediaQuery, setIsSmallScreen])
|
|
||||||
|
|
||||||
return isSmallScreen
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -102,6 +102,15 @@ export class CompletionMessagesBuilder {
|
|||||||
content = content.slice(splitIndex).trim()
|
content = content.slice(splitIndex).trim()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (content.includes('<|channel|>analysis<|message|>')) {
|
||||||
|
const match = content.match(
|
||||||
|
/<\|channel\|>analysis<\|message\|>([\s\S]*?)<\|start\|>assistant<\|channel\|>final<\|message\|>/
|
||||||
|
)
|
||||||
|
if (match?.index !== undefined) {
|
||||||
|
const splitIndex = match.index + match[0].length
|
||||||
|
content = content.slice(splitIndex).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user