fixsta
This commit is contained in:
parent
086e311aa4
commit
1cebb80b08
1
.gitignore
vendored
1
.gitignore
vendored
@ -39,3 +39,4 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
/electron-file-search/datahound-win32-x64
|
||||||
|
9
Modelfile
Normal file
9
Modelfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
FROM deepseek-r1:1.5b
|
||||||
|
# Disable GPU usage
|
||||||
|
PARAMETER num_gpu 0
|
||||||
|
|
||||||
|
# Increase context length
|
||||||
|
PARAMETER max_tokens 16384
|
||||||
|
|
||||||
|
# Set temperature to 0.6
|
||||||
|
PARAMETER temperature 0.6
|
@ -148,7 +148,7 @@ export class LLMService {
|
|||||||
|
|
||||||
case 'ollama':
|
case 'ollama':
|
||||||
const ollamaResponse = await ollamaService.chat({
|
const ollamaResponse = await ollamaService.chat({
|
||||||
model: this.#config.model || 'phi4:latest',
|
model: this.#config.model || 'llama2:7b',
|
||||||
messages: [{ role: 'user', content: question }],
|
messages: [{ role: 'user', content: question }],
|
||||||
temperature: this.#config.temperature,
|
temperature: this.#config.temperature,
|
||||||
onChunk,
|
onChunk,
|
||||||
@ -195,7 +195,7 @@ export class LLMService {
|
|||||||
const cleanConfig = {
|
const cleanConfig = {
|
||||||
provider: newConfig.provider,
|
provider: newConfig.provider,
|
||||||
apiKey: newConfig.apiKey ?? null,
|
apiKey: newConfig.apiKey ?? null,
|
||||||
model: newConfig.model ?? (newConfig.provider === 'ollama' ? 'phi4' : null),
|
model: newConfig.model ?? (newConfig.provider === 'ollama' ? 'llama2:7b' : null),
|
||||||
baseUrl: newConfig.provider === 'ollama' ? (newConfig.baseUrl ?? 'http://localhost:11434') : (newConfig.baseUrl ?? null),
|
baseUrl: newConfig.provider === 'ollama' ? (newConfig.baseUrl ?? 'http://localhost:11434') : (newConfig.baseUrl ?? null),
|
||||||
temperature: typeof newConfig.temperature === 'number' ? newConfig.temperature : 0.7
|
temperature: typeof newConfig.temperature === 'number' ? newConfig.temperature : 0.7
|
||||||
};
|
};
|
||||||
|
@ -58,7 +58,7 @@ const schema: Store.Schema<Schema> = {
|
|||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
default: {
|
default: {
|
||||||
provider: 'ollama',
|
provider: 'ollama',
|
||||||
model: 'phi4',
|
model: 'llama2:7b',
|
||||||
baseUrl: 'http://localhost:11434',
|
baseUrl: 'http://localhost:11434',
|
||||||
temperature: 0.7
|
temperature: 0.7
|
||||||
}
|
}
|
||||||
|
10
electron-file-search/package-lock.json
generated
10
electron-file-search/package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"lucide-react": "^0.474.0",
|
||||||
"ollama": "^0.5.12",
|
"ollama": "^0.5.12",
|
||||||
"openai": "^4.82.0",
|
"openai": "^4.82.0",
|
||||||
"openrouter-client": "^1.2.0",
|
"openrouter-client": "^1.2.0",
|
||||||
@ -6546,6 +6547,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.474.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.474.0.tgz",
|
||||||
|
"integrity": "sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/matcher": {
|
"node_modules/matcher": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
|
"lucide-react": "^0.474.0",
|
||||||
"ollama": "^0.5.12",
|
"ollama": "^0.5.12",
|
||||||
"openai": "^4.82.0",
|
"openai": "^4.82.0",
|
||||||
"openrouter-client": "^1.2.0",
|
"openrouter-client": "^1.2.0",
|
||||||
|
@ -149,6 +149,18 @@ function AppContent() {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flex: 1,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Data Hound V2
|
||||||
|
</Box>
|
||||||
<div className="window-controls">
|
<div className="window-controls">
|
||||||
<button
|
<button
|
||||||
className="control-button"
|
className="control-button"
|
||||||
@ -247,15 +259,48 @@ function AppContent() {
|
|||||||
boxSizing: 'border-box'
|
boxSizing: 'border-box'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
multiline
|
||||||
placeholder="Ask a question..."
|
variant="outlined"
|
||||||
value={input}
|
placeholder="Ask a question..."
|
||||||
onChange={(e) => setInput(e.target.value)}
|
value={input}
|
||||||
disabled={isLoading}
|
onChange={(e) => setInput(e.target.value)}
|
||||||
size="small"
|
disabled={isLoading}
|
||||||
/>
|
size="small"
|
||||||
|
minRows={1}
|
||||||
|
maxRows={4}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (event.shiftKey) {
|
||||||
|
return; // Allow new line with Shift+Enter
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
handleSubmit(event);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
InputProps={{
|
||||||
|
style: {
|
||||||
|
resize: 'vertical'
|
||||||
|
},
|
||||||
|
sx: {
|
||||||
|
'&::-webkit-scrollbar': {
|
||||||
|
width: '8px',
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: (theme) => theme.palette.primary.main, // Using your theme's primary blue
|
||||||
|
borderRadius: '2px', // Made more square
|
||||||
|
'&:hover': {
|
||||||
|
background: (theme) => theme.palette.primary.light, // Lighter blue on hover
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-track': {
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isLoading || !input.trim()}
|
disabled={isLoading || !input.trim()}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { useEffect, useRef } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Box, Typography, Paper, Avatar } from '@mui/material';
|
import { Box, Typography, Paper, Avatar, Collapse, IconButton } from '@mui/material';
|
||||||
import type { DocumentMetadata } from '../../../electron/types';
|
import { Loader2, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import rehypeHighlight from 'rehype-highlight';
|
import rehypeHighlight from 'rehype-highlight';
|
||||||
import 'highlight.js/styles/github.css';
|
import 'highlight.js/styles/github.css';
|
||||||
|
import type { DocumentMetadata } from '../../../electron/types';
|
||||||
|
|
||||||
interface ChatMessage {
|
interface ChatMessage {
|
||||||
id: string;
|
id: string;
|
||||||
@ -18,8 +19,220 @@ interface MessageListProps {
|
|||||||
messages: ChatMessage[];
|
messages: ChatMessage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MessageList({ messages }: MessageListProps) {
|
interface ThinkingContentProps {
|
||||||
const messageListRef = useRef<HTMLDivElement>(null);
|
content: string;
|
||||||
|
isThinking: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageSegment {
|
||||||
|
type: 'text' | 'thinking';
|
||||||
|
content: string;
|
||||||
|
inProgress?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MessageContentProps {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ThinkingContent: React.FC<ThinkingContentProps> = ({ content, isThinking }) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ mt: 1 }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 1,
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'text.secondary'
|
||||||
|
}}
|
||||||
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
|
>
|
||||||
|
<IconButton size="small">
|
||||||
|
{isExpanded ? <ChevronUp size={16} /> : <ChevronDown size={16} />}
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="caption" sx={{ fontWeight: 500 }}>
|
||||||
|
Thinking Process
|
||||||
|
</Typography>
|
||||||
|
{isThinking && (
|
||||||
|
<Box
|
||||||
|
component={Loader2}
|
||||||
|
size={16}
|
||||||
|
className="animate-spin"
|
||||||
|
sx={{
|
||||||
|
animation: 'spin 1s linear infinite',
|
||||||
|
'@keyframes spin': {
|
||||||
|
'0%': {
|
||||||
|
transform: 'rotate(0deg)',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'rotate(360deg)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<Collapse in={isExpanded}>
|
||||||
|
<Box sx={{
|
||||||
|
pl: 4,
|
||||||
|
pr: 2,
|
||||||
|
py: 1,
|
||||||
|
mt: 1,
|
||||||
|
borderLeft: '2px solid',
|
||||||
|
borderColor: 'divider',
|
||||||
|
color: 'text.secondary'
|
||||||
|
}}>
|
||||||
|
<ReactMarkdown
|
||||||
|
className="markdown-body"
|
||||||
|
rehypePlugins={[rehypeHighlight]}
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MessageContent: React.FC<MessageContentProps> = ({ text }) => {
|
||||||
|
const [segments, setSegments] = useState<MessageSegment[]>([]);
|
||||||
|
const [isThinking, setIsThinking] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const parseContent = (): void => {
|
||||||
|
const parts: MessageSegment[] = [];
|
||||||
|
let remainingText = text;
|
||||||
|
|
||||||
|
// Handle <think> tags
|
||||||
|
const thinkRegex = /<think>([\s\S]*?)(?:<\/think>|$)/g;
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = thinkRegex.exec(text)) !== null) {
|
||||||
|
// Add text before the think tag if there is any
|
||||||
|
const beforeText = text.slice(lastIndex, match.index).trim();
|
||||||
|
if (beforeText) {
|
||||||
|
parts.push({
|
||||||
|
type: 'text',
|
||||||
|
content: beforeText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the thinking content
|
||||||
|
parts.push({
|
||||||
|
type: 'thinking',
|
||||||
|
content: match[1],
|
||||||
|
inProgress: !match[0].endsWith('</think>')
|
||||||
|
});
|
||||||
|
|
||||||
|
lastIndex = match.index + match[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any remaining text after the last think tag
|
||||||
|
const afterText = text.slice(lastIndex).trim();
|
||||||
|
if (afterText) {
|
||||||
|
parts.push({
|
||||||
|
type: 'text',
|
||||||
|
content: afterText
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setSegments(parts);
|
||||||
|
setIsThinking(parts.some(part => part.type === 'thinking' && part.inProgress));
|
||||||
|
};
|
||||||
|
|
||||||
|
parseContent();
|
||||||
|
}, [text]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
{segments.map((segment, index) => {
|
||||||
|
if (segment.type === 'text' && segment.content.trim()) {
|
||||||
|
return (
|
||||||
|
<Box key={index}>
|
||||||
|
<Box sx={{
|
||||||
|
'& .markdown-body': {
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
minHeight: '1.5em',
|
||||||
|
lineHeight: 1.6,
|
||||||
|
'& pre': {
|
||||||
|
backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
|
||||||
|
padding: 2,
|
||||||
|
borderRadius: 1,
|
||||||
|
overflow: 'auto'
|
||||||
|
},
|
||||||
|
'& code': {
|
||||||
|
backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
|
||||||
|
padding: '0.2em 0.4em',
|
||||||
|
borderRadius: 1,
|
||||||
|
fontSize: '85%'
|
||||||
|
},
|
||||||
|
'& h1, & h2, & h3, & h4, & h5, & h6': {
|
||||||
|
marginTop: '24px',
|
||||||
|
marginBottom: '16px',
|
||||||
|
fontWeight: 600,
|
||||||
|
lineHeight: 1.25
|
||||||
|
},
|
||||||
|
'& p': {
|
||||||
|
marginTop: '0',
|
||||||
|
marginBottom: '16px'
|
||||||
|
},
|
||||||
|
'& a': {
|
||||||
|
color: (theme) => theme.palette.primary.main,
|
||||||
|
textDecoration: 'none',
|
||||||
|
'&:hover': {
|
||||||
|
textDecoration: 'underline'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'& img': {
|
||||||
|
maxWidth: '100%',
|
||||||
|
height: 'auto'
|
||||||
|
},
|
||||||
|
'& blockquote': {
|
||||||
|
padding: '0 1em',
|
||||||
|
color: (theme) => theme.palette.text.secondary,
|
||||||
|
borderLeft: (theme) => `0.25em solid ${theme.palette.divider}`,
|
||||||
|
margin: '0 0 16px 0'
|
||||||
|
},
|
||||||
|
'& ul, & ol': {
|
||||||
|
paddingLeft: '2em',
|
||||||
|
marginBottom: '16px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<ReactMarkdown
|
||||||
|
className="markdown-body"
|
||||||
|
rehypePlugins={[rehypeHighlight]}
|
||||||
|
>
|
||||||
|
{segment.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (segment.type === 'thinking') {
|
||||||
|
return (
|
||||||
|
<Box key={index}>
|
||||||
|
<ThinkingContent
|
||||||
|
content={segment.content}
|
||||||
|
isThinking={segment.inProgress || false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const MessageList: React.FC<MessageListProps> = ({ messages }) => {
|
||||||
|
// Memoize messages to preserve their original timestamps
|
||||||
|
const memoizedMessages = React.useMemo(() => messages, [messages]);
|
||||||
|
const messageListRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (messageListRef.current) {
|
if (messageListRef.current) {
|
||||||
@ -49,11 +262,9 @@ export default function MessageList({ messages }: MessageListProps) {
|
|||||||
background: (theme) => theme.palette.action.hover,
|
background: (theme) => theme.palette.action.hover,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'&::-webkit-scrollbar-track': {
|
}}
|
||||||
background: 'transparent',
|
>
|
||||||
},
|
{memoizedMessages.map((message) => (
|
||||||
}}>
|
|
||||||
{messages.map((message) => (
|
|
||||||
<Box
|
<Box
|
||||||
key={message.id}
|
key={message.id}
|
||||||
sx={{
|
sx={{
|
||||||
@ -65,142 +276,45 @@ export default function MessageList({ messages }: MessageListProps) {
|
|||||||
alignSelf: message.isUser ? 'flex-end' : 'flex-start',
|
alignSelf: message.isUser ? 'flex-end' : 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 0.5 }}>
|
<Avatar
|
||||||
<Avatar
|
src={message.isUser ? '/profiles/user-profile.webp' : '/profiles/ai-profile.webp'}
|
||||||
src={message.isUser ? '/profiles/user-profile.webp' : '/profiles/ai-profile.webp'}
|
alt={message.isUser ? 'User' : 'AI'}
|
||||||
alt={message.isUser ? 'User' : 'AI'}
|
sx={{ width: 40, height: 40 }}
|
||||||
variant="square"
|
/>
|
||||||
sx={{
|
<Paper
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
boxShadow: 1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
sx={{
|
|
||||||
fontSize: '0.75rem',
|
|
||||||
color: 'text.secondary',
|
|
||||||
fontWeight: 500
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{message.isUser ? 'User' : 'Data Hound'}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
component={Paper}
|
|
||||||
elevation={1}
|
elevation={1}
|
||||||
sx={{
|
sx={{
|
||||||
p: 2,
|
px: 2,
|
||||||
flex: 1,
|
py: 1.5,
|
||||||
bgcolor: message.isUser ? 'primary.main' : 'background.paper',
|
flex: 1,
|
||||||
color: message.isUser ? 'primary.contrastText' : 'text.primary',
|
bgcolor: message.isUser ? 'primary.main' : 'background.paper',
|
||||||
transition: 'all 0.2s ease-in-out',
|
color: message.isUser ? 'primary.contrastText' : 'text.primary',
|
||||||
boxShadow: 1,
|
borderRadius: 2,
|
||||||
borderRadius: 2,
|
}}
|
||||||
'&:hover': {
|
|
||||||
boxShadow: 2,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{
|
|
||||||
'& .markdown-body': {
|
|
||||||
whiteSpace: 'pre-wrap',
|
|
||||||
wordBreak: 'break-word',
|
|
||||||
minHeight: '1.5em',
|
|
||||||
lineHeight: 1.6,
|
|
||||||
'& pre': {
|
|
||||||
backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
|
|
||||||
padding: 2,
|
|
||||||
borderRadius: 1,
|
|
||||||
overflow: 'auto'
|
|
||||||
},
|
|
||||||
'& code': {
|
|
||||||
backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
|
|
||||||
padding: '0.2em 0.4em',
|
|
||||||
borderRadius: 1,
|
|
||||||
fontSize: '85%'
|
|
||||||
},
|
|
||||||
'& h1, & h2, & h3, & h4, & h5, & h6': {
|
|
||||||
marginTop: '24px',
|
|
||||||
marginBottom: '16px',
|
|
||||||
fontWeight: 600,
|
|
||||||
lineHeight: 1.25
|
|
||||||
},
|
|
||||||
'& p': {
|
|
||||||
marginTop: '0',
|
|
||||||
marginBottom: '16px'
|
|
||||||
},
|
|
||||||
'& a': {
|
|
||||||
color: (theme) => theme.palette.primary.main,
|
|
||||||
textDecoration: 'none',
|
|
||||||
'&:hover': {
|
|
||||||
textDecoration: 'underline'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'& img': {
|
|
||||||
maxWidth: '100%',
|
|
||||||
height: 'auto'
|
|
||||||
},
|
|
||||||
'& blockquote': {
|
|
||||||
padding: '0 1em',
|
|
||||||
color: (theme) => theme.palette.text.secondary,
|
|
||||||
borderLeft: (theme) => `0.25em solid ${theme.palette.divider}`,
|
|
||||||
margin: '0 0 16px 0'
|
|
||||||
},
|
|
||||||
'& ul, & ol': {
|
|
||||||
paddingLeft: '2em',
|
|
||||||
marginBottom: '16px'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<ReactMarkdown
|
|
||||||
className="markdown-body"
|
|
||||||
rehypePlugins={[rehypeHighlight]}
|
|
||||||
>
|
|
||||||
{message.text}
|
|
||||||
</ReactMarkdown>
|
|
||||||
</Box>
|
|
||||||
{message.sources && message.sources.length > 0 && (
|
|
||||||
<Box sx={{ mt: 2, pt: 1, borderTop: '1px solid', borderColor: 'divider' }}>
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
color="text.secondary"
|
|
||||||
sx={{
|
|
||||||
display: 'block',
|
|
||||||
mb: 0.5,
|
|
||||||
fontWeight: 'medium'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Sources:
|
|
||||||
</Typography>
|
|
||||||
{message.sources.map((source, index) => (
|
|
||||||
<Typography
|
|
||||||
key={index}
|
|
||||||
variant="caption"
|
|
||||||
component="div"
|
|
||||||
color="text.secondary"
|
|
||||||
sx={{
|
|
||||||
pl: 1,
|
|
||||||
borderLeft: '2px solid',
|
|
||||||
borderColor: 'divider'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{source.path}
|
|
||||||
</Typography>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
color={message.isUser ? '#ffffff' : 'text.secondary'}
|
|
||||||
sx={{ display: 'block', mt: 0.5 }}
|
|
||||||
>
|
>
|
||||||
{new Date(message.timestamp).toLocaleTimeString()}
|
<MessageContent text={message.text} />
|
||||||
</Typography>
|
{message.sources && message.sources.length > 0 && (
|
||||||
</Box>
|
<Box sx={{ mt: 2, pt: 1, borderTop: '1px solid', borderColor: 'divider' }}>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
Sources:
|
||||||
|
</Typography>
|
||||||
|
{message.sources.map((source, index) => (
|
||||||
|
<Typography
|
||||||
|
key={index}
|
||||||
|
variant="caption"
|
||||||
|
component="div"
|
||||||
|
color="text.secondary"
|
||||||
|
>
|
||||||
|
{source.path}
|
||||||
|
</Typography>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default MessageList;
|
@ -13,6 +13,8 @@ import {
|
|||||||
Alert,
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
Typography,
|
||||||
|
TextField,
|
||||||
|
InputAdornment,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import {
|
import {
|
||||||
Folder as FolderIcon,
|
Folder as FolderIcon,
|
||||||
@ -21,7 +23,10 @@ import {
|
|||||||
Cloud as DropboxIcon,
|
Cloud as DropboxIcon,
|
||||||
Chat as DiscordIcon,
|
Chat as DiscordIcon,
|
||||||
Computer as LocalIcon,
|
Computer as LocalIcon,
|
||||||
|
Search,
|
||||||
|
Padding
|
||||||
} from '@mui/icons-material';
|
} from '@mui/icons-material';
|
||||||
|
|
||||||
import { useElectron } from '../../hooks/useElectron';
|
import { useElectron } from '../../hooks/useElectron';
|
||||||
|
|
||||||
interface Directory {
|
interface Directory {
|
||||||
@ -105,16 +110,46 @@ export default function DirectoryPicker({ onSelect }: DirectoryPickerProps) {
|
|||||||
mr: 2,
|
mr: 2,
|
||||||
mb: 2,
|
mb: 2,
|
||||||
}}>
|
}}>
|
||||||
<Typography sx={{ pl:2 ,flexGrow: 1 }} variant="h6">Folders</Typography>
|
|
||||||
<Button
|
<TextField
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
placeholder="Search..."
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<Search className="text-gray-400"/>
|
||||||
|
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
sx: {ml:2,
|
||||||
|
'&::-webkit-scrollbar': {
|
||||||
|
width: '8px',
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: '#2196f3',
|
||||||
|
borderRadius: '2px',
|
||||||
|
'&:hover': {
|
||||||
|
background: '#64b5f6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-track': {
|
||||||
|
background: 'transparent',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/> <Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={handleOpen}
|
onClick={handleOpen}
|
||||||
size="small"
|
size="small"
|
||||||
sx={{
|
sx={{
|
||||||
|
ml:3,
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
width: '32px',
|
width: '64px',
|
||||||
height: '32px',
|
height: '38px',
|
||||||
borderRadius: '50%',
|
borderRadius: '5%',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
backgroundColor: 'primary.main',
|
backgroundColor: 'primary.main',
|
||||||
@ -130,10 +165,10 @@ export default function DirectoryPicker({ onSelect }: DirectoryPickerProps) {
|
|||||||
},
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'primary.dark',
|
backgroundColor: 'primary.dark',
|
||||||
width: '180px',
|
width: '400%',
|
||||||
borderRadius: '16px',
|
borderRadius: '5%',
|
||||||
'& .buttonText': {
|
'& .buttonText': {
|
||||||
width: '110px',
|
width: '150px',
|
||||||
marginLeft: '8px',
|
marginLeft: '8px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -22,7 +22,7 @@ export function useLLMConfig() {
|
|||||||
});
|
});
|
||||||
setConfig(config || {
|
setConfig(config || {
|
||||||
provider: 'ollama',
|
provider: 'ollama',
|
||||||
model: 'phi4',
|
model: 'llama2:7b',
|
||||||
baseUrl: 'http://localhost:11434',
|
baseUrl: 'http://localhost:11434',
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
apiKey: undefined
|
apiKey: undefined
|
||||||
|
Loading…
x
Reference in New Issue
Block a user