auto-git:
[add] src/AssistantMessageContent.jsx [add] src/appConfig.js [add] src/attachments.jsx [add] src/chatText.js [add] src/modelPicker.js
This commit is contained in:
80
src/AssistantMessageContent.jsx
Normal file
80
src/AssistantMessageContent.jsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import React from 'react'
|
||||
import { markdownToHTML } from './markdown'
|
||||
import { enrichOllamaErrorText, splitThinkBlocks } from './chatText'
|
||||
|
||||
export default function AssistantMessageContent({ content, streamOutput, sources }) {
|
||||
const displayContent = enrichOllamaErrorText(content || '')
|
||||
const { think, answer } = splitThinkBlocks(displayContent)
|
||||
const [open, setOpen] = React.useState(false)
|
||||
const showThinkButton = !!think
|
||||
|
||||
return (
|
||||
<div className="assistant-message">
|
||||
{showThinkButton && (
|
||||
<div className="assistant-thoughts">
|
||||
<button
|
||||
className="think-toggle"
|
||||
onClick={() => setOpen(o => !o)}
|
||||
aria-expanded={open ? 'true' : 'false'}
|
||||
aria-controls="think-content"
|
||||
>
|
||||
<span className="think-toggle-icon" aria-hidden="true">
|
||||
{open ? '▾' : '▸'}
|
||||
</span>
|
||||
Thoughts
|
||||
</button>
|
||||
{open && (
|
||||
<div
|
||||
id="think-content"
|
||||
className="think-content"
|
||||
dangerouslySetInnerHTML={{ __html: markdownToHTML(think) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="msg-content"
|
||||
dangerouslySetInnerHTML={{ __html: markdownToHTML(answer || displayContent || '') }}
|
||||
/>
|
||||
{Array.isArray(sources) && sources.length > 0 && (
|
||||
<div className="msg-sources chips">
|
||||
{sources.map((u, i) => {
|
||||
let label = u
|
||||
let isFile = false
|
||||
try {
|
||||
const parsed = new URL(u)
|
||||
if (parsed.protocol === 'file:') {
|
||||
isFile = true
|
||||
const parts = parsed.pathname.split('/').filter(Boolean)
|
||||
label = decodeURIComponent(parts[parts.length - 1] || u)
|
||||
} else {
|
||||
const host = parsed.hostname || u
|
||||
label = host.replace(/^www\./i, '')
|
||||
}
|
||||
} catch {}
|
||||
return (
|
||||
<a
|
||||
key={u + i}
|
||||
className="chip"
|
||||
href={u}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title={u}
|
||||
onClick={(event) => {
|
||||
if (!isFile) return
|
||||
event.preventDefault()
|
||||
try {
|
||||
const parsed = new URL(u)
|
||||
window.electronAPI?.openPath?.(decodeURIComponent(parsed.pathname))
|
||||
} catch {}
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</a>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user