fix
This commit is contained in:
		
							parent
							
								
									2a3e60a16c
								
							
						
					
					
						commit
						0244ab851e
					
				@ -1,3 +1,3 @@
 | 
			
		||||
FROM deepseek-r1:8b
 | 
			
		||||
FROM deepseek-r1:1.5b
 | 
			
		||||
SYSTEM You are a helpful AI assistant.
 | 
			
		||||
PARAMETER num_gpu 0
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
# Data Hound
 | 
			
		||||
# Data Identification Manager : Personal Edition
 | 
			
		||||
 | 
			
		||||
An Electron-based desktop application that uses AI to help you search and understand your files through natural language queries.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,8 @@ function createWindow() {
 | 
			
		||||
  const mainWindow = new BrowserWindow({
 | 
			
		||||
    width: 1200,
 | 
			
		||||
    height: 800,
 | 
			
		||||
    minWidth: 1200,  // Minimum width
 | 
			
		||||
    minHeight: 800, // Minimum height
 | 
			
		||||
    frame: false,
 | 
			
		||||
    titleBarStyle: 'hidden',
 | 
			
		||||
    webPreferences: {
 | 
			
		||||
@ -40,7 +42,7 @@ function createWindow() {
 | 
			
		||||
        const response = await fetch(process.env.VITE_DEV_SERVER_URL);
 | 
			
		||||
        if (response.ok) {
 | 
			
		||||
          mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL);
 | 
			
		||||
          mainWindow.webContents.openDevTools();
 | 
			
		||||
          //mainWindow.webContents.openDevTools();
 | 
			
		||||
        } else {
 | 
			
		||||
          setTimeout(pollDevServer, 500);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
const { contextBridge, ipcRenderer } = require('electron');
 | 
			
		||||
 | 
			
		||||
// Import types
 | 
			
		||||
import type { IpcRendererEvent } from 'electron';
 | 
			
		||||
import type { LLMConfig, DocumentMetadata } from './types';
 | 
			
		||||
 | 
			
		||||
interface Directory {
 | 
			
		||||
@ -73,7 +74,7 @@ contextBridge.exposeInMainWorld('electron', {
 | 
			
		||||
  checkModel: (modelName: string) => ipcRenderer.invoke('check-model', modelName),
 | 
			
		||||
  pullModel: (modelName: string, onProgress: (status: string) => void) => {
 | 
			
		||||
    const channel = `pull-model-progress-${modelName}`;
 | 
			
		||||
    ipcRenderer.on(channel, (_event, status) => onProgress(status));
 | 
			
		||||
    ipcRenderer.on(channel, (_event: IpcRendererEvent, status: string) => onProgress(status));
 | 
			
		||||
    return ipcRenderer.invoke('pull-model', modelName).finally(() => {
 | 
			
		||||
      ipcRenderer.removeListener(channel, onProgress);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ export class LLMService {
 | 
			
		||||
  ): Promise<{ answer: string, sources: DocumentMetadata[] }> {
 | 
			
		||||
    try {
 | 
			
		||||
      const ollamaResponse = await ollamaService.chat({
 | 
			
		||||
        model: 'damien113/datahound:latest',
 | 
			
		||||
        model: 'damien113/datahound-gpu:8b',
 | 
			
		||||
        messages: [{ role: 'user', content: question }],
 | 
			
		||||
        temperature: 0.7,
 | 
			
		||||
        onChunk,
 | 
			
		||||
@ -36,7 +36,7 @@ export class LLMService {
 | 
			
		||||
  getConfig() {
 | 
			
		||||
    return {
 | 
			
		||||
      provider: 'ollama',
 | 
			
		||||
      model: 'damien113/datahound:latest',
 | 
			
		||||
      model: 'damien113/datahound-gpu:8b',
 | 
			
		||||
      baseUrl: 'http://localhost:11434',
 | 
			
		||||
      temperature: 0.7
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:;" />
 | 
			
		||||
    <title>Data Hound</title>
 | 
			
		||||
    <title>Data Identification Manager : Personal Edition</title>
 | 
			
		||||
  </head>
 | 
			
		||||
  <body>
 | 
			
		||||
    <div id="root"></div>
 | 
			
		||||
 | 
			
		||||
@ -41,14 +41,15 @@
 | 
			
		||||
      "dist-electron/**/*"
 | 
			
		||||
    ],
 | 
			
		||||
    "win": {
 | 
			
		||||
      "target": "nsis"
 | 
			
		||||
      "target": "portable"
 | 
			
		||||
    },
 | 
			
		||||
    "mac": {
 | 
			
		||||
      "target": "dmg"
 | 
			
		||||
    },
 | 
			
		||||
    "linux": {
 | 
			
		||||
      "target": "AppImage"
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    "forceCodeSigning": false
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/node": "^20.11.16",
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
import { Box, CssBaseline, ThemeProvider, createTheme, Tabs, Tab, TextField, IconButton } from '@mui/material';
 | 
			
		||||
import { Send as SendIcon, DeleteOutline as ClearIcon, Close as CloseIcon, Remove as MinimizeIcon, Fullscreen as MaximizeIcon } from '@mui/icons-material';
 | 
			
		||||
import { Box, CssBaseline, ThemeProvider, createTheme, Tabs, Tab, TextField, IconButton, Typography, Grow, Tooltip, Button, Chip } from '@mui/material';
 | 
			
		||||
import { Send as SendIcon, DeleteOutline as ClearIcon, Close as CloseIcon, Remove as MinimizeIcon, Fullscreen as MaximizeIcon, Download, UpgradeTwoTone, OpenInBrowser } from '@mui/icons-material';
 | 
			
		||||
import { useChat } from './hooks/useChat';
 | 
			
		||||
import ChatPanel from './components/ChatPanel';
 | 
			
		||||
import FileExplorer from './components/FileExplorer';
 | 
			
		||||
@ -97,6 +97,9 @@ function AppContent() {
 | 
			
		||||
    await sendMessage(message);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleClick = () => {
 | 
			
		||||
    window.open('https://data443.com', '_blank');
 | 
			
		||||
  };
 | 
			
		||||
  return (
 | 
			
		||||
    <ThemeProvider theme={theme}>
 | 
			
		||||
      <CssBaseline />
 | 
			
		||||
@ -111,7 +114,7 @@ function AppContent() {
 | 
			
		||||
        {/* Custom titlebar */}
 | 
			
		||||
        <Box
 | 
			
		||||
          sx={{
 | 
			
		||||
            height: '28px',
 | 
			
		||||
            height: '40px',
 | 
			
		||||
            bgcolor: '#2f2f2f',
 | 
			
		||||
            display: 'flex',
 | 
			
		||||
            alignItems: 'center',
 | 
			
		||||
@ -150,7 +153,133 @@ function AppContent() {
 | 
			
		||||
            },
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <div className="window-controls">
 | 
			
		||||
 | 
			
		||||
<Button 
 | 
			
		||||
      variant="outlined"
 | 
			
		||||
      size="small"
 | 
			
		||||
      startIcon={<UpgradeTwoTone />}
 | 
			
		||||
      onClick={(handleClick)}
 | 
			
		||||
      sx={{
 | 
			
		||||
        z:100,
 | 
			
		||||
        '@keyframes flash': {
 | 
			
		||||
          '0%': {
 | 
			
		||||
            borderColor: 'primary.main',
 | 
			
		||||
            color: 'primary.main',
 | 
			
		||||
            boxShadow: '0 0 0 0 rgba(25, 118, 210, 0.4)'
 | 
			
		||||
          },
 | 
			
		||||
          '50%': {
 | 
			
		||||
            borderColor: 'white',
 | 
			
		||||
            color: 'white',
 | 
			
		||||
            boxShadow: '0 0 0 4px rgba(25, 118, 210, 0)'
 | 
			
		||||
          },
 | 
			
		||||
          '100%': {
 | 
			
		||||
            borderColor: 'primary.main',
 | 
			
		||||
            color: 'primary.main',
 | 
			
		||||
            boxShadow: '0 0 0 0 rgba(25, 118, 210, 0)'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        width: '135px',
 | 
			
		||||
        minHeight: '24px',
 | 
			
		||||
        px: 2,
 | 
			
		||||
        py: 0.5,
 | 
			
		||||
        borderColor: 'white',
 | 
			
		||||
        color: 'white',
 | 
			
		||||
        textTransform: 'none',
 | 
			
		||||
        fontWeight: 500,
 | 
			
		||||
        fontSize: '0.8125rem',
 | 
			
		||||
        lineHeight: 1.2,
 | 
			
		||||
        animation: 'flash 2s infinite',
 | 
			
		||||
        cursor: 'pointer',
 | 
			
		||||
        '& .MuiSvgIcon-root': {
 | 
			
		||||
          animation: 'flash 2s infinite'
 | 
			
		||||
        },
 | 
			
		||||
        '&:hover': {
 | 
			
		||||
          borderColor: 'white',
 | 
			
		||||
          backgroundColor: 'rgba(255, 255, 255, 0.08)',
 | 
			
		||||
          cursor: 'pointer'
 | 
			
		||||
        }
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      Upgrade
 | 
			
		||||
    </Button>
 | 
			
		||||
<Button 
 | 
			
		||||
      variant="outlined"
 | 
			
		||||
      size="small"
 | 
			
		||||
      startIcon={<OpenInBrowser />}
 | 
			
		||||
      onClick={(handleClick)}
 | 
			
		||||
      sx={{
 | 
			
		||||
        ml:2,
 | 
			
		||||
        z:100,
 | 
			
		||||
        '@keyframes flash': {
 | 
			
		||||
          '0%': {
 | 
			
		||||
            borderColor: 'primary.main',
 | 
			
		||||
            color: 'primary.main',
 | 
			
		||||
            boxShadow: '0 0 0 0 rgba(25, 118, 210, 0.4)'
 | 
			
		||||
          },
 | 
			
		||||
          '50%': {
 | 
			
		||||
            borderColor: 'white',
 | 
			
		||||
            color: 'white',
 | 
			
		||||
            boxShadow: '0 0 0 4px rgba(25, 118, 210, 0)'
 | 
			
		||||
          },
 | 
			
		||||
          '100%': {
 | 
			
		||||
            borderColor: 'primary.main',
 | 
			
		||||
            color: 'primary.main',
 | 
			
		||||
            boxShadow: '0 0 0 0 rgba(25, 118, 210, 0)'
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        width: '135px',
 | 
			
		||||
        minHeight: '24px',
 | 
			
		||||
        px: 2,
 | 
			
		||||
        py: 0.5,
 | 
			
		||||
        borderColor: 'white',
 | 
			
		||||
        color: 'white',
 | 
			
		||||
        textTransform: 'none',
 | 
			
		||||
        fontWeight: 500,
 | 
			
		||||
        fontSize: '0.8125rem',
 | 
			
		||||
        lineHeight: 1.2,
 | 
			
		||||
        animation: 'flash 2s infinite',
 | 
			
		||||
        cursor: 'pointer',
 | 
			
		||||
        '& .MuiSvgIcon-root': {
 | 
			
		||||
          animation: 'flash 2s infinite'
 | 
			
		||||
        },
 | 
			
		||||
        '&:hover': {
 | 
			
		||||
          borderColor: 'white',
 | 
			
		||||
          backgroundColor: 'rgba(255, 255, 255, 0.08)',
 | 
			
		||||
          cursor: 'pointer'
 | 
			
		||||
        }
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      Enterprise
 | 
			
		||||
    </Button>
 | 
			
		||||
    <Typography
 | 
			
		||||
      sx={{
 | 
			
		||||
        position: 'absolute',
 | 
			
		||||
        left: '50%',
 | 
			
		||||
        transform: 'translateX(-50%)',
 | 
			
		||||
        width: 'auto',
 | 
			
		||||
        textAlign: 'center',
 | 
			
		||||
        display: 'flex',
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
        gap: 1
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      Data Identification Manager
 | 
			
		||||
    </Typography>
 | 
			
		||||
<div className="window-controls">
 | 
			
		||||
      <Chip 
 | 
			
		||||
        label="Personal Edition (Free)"
 | 
			
		||||
        variant="outlined"
 | 
			
		||||
        size="small"
 | 
			
		||||
        sx={{
 | 
			
		||||
          bgcolor: 'primary.main',  // or 'secondary.main', 'error.main', etc.
 | 
			
		||||
          color: 'primary.contrastText',
 | 
			
		||||
          borderRadius: 1,
 | 
			
		||||
          height: 'auto',
 | 
			
		||||
          '& .MuiChip-label': {
 | 
			
		||||
            padding: '4px 8px',
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
            <button 
 | 
			
		||||
              className="control-button"
 | 
			
		||||
              onClick={() => window.electron.closeWindow()}
 | 
			
		||||
@ -200,8 +329,9 @@ function AppContent() {
 | 
			
		||||
                <Tab label="Home" />
 | 
			
		||||
                <Tab label="Chat" />
 | 
			
		||||
                <Tab label="Settings" />
 | 
			
		||||
                <Tab label="Scanning" />
 | 
			
		||||
                <Tab label="Reports" />
 | 
			
		||||
                <Tab disabled label="Scanning" />
 | 
			
		||||
                <Tab disabled label="Reports" />
 | 
			
		||||
                <Tab disabled label="Cleanup" />
 | 
			
		||||
              </Tabs>
 | 
			
		||||
            </Box>
 | 
			
		||||
            <Box sx={{ 
 | 
			
		||||
@ -264,13 +394,34 @@ function AppContent() {
 | 
			
		||||
          >
 | 
			
		||||
            <SendIcon />
 | 
			
		||||
          </IconButton>
 | 
			
		||||
          <IconButton 
 | 
			
		||||
            onClick={clearMessages}
 | 
			
		||||
            color="error"
 | 
			
		||||
            disabled={messages.length === 0}
 | 
			
		||||
          >
 | 
			
		||||
            <ClearIcon />
 | 
			
		||||
          </IconButton>
 | 
			
		||||
          <Tooltip title="Clear all messages" 
 | 
			
		||||
  arrow
 | 
			
		||||
  placement="top">
 | 
			
		||||
            <IconButton
 | 
			
		||||
              onClick={clearMessages}
 | 
			
		||||
              color="error"
 | 
			
		||||
              disabled={messages.length === 0}
 | 
			
		||||
              sx={{
 | 
			
		||||
                '@keyframes glow': {
 | 
			
		||||
                  '0%': {
 | 
			
		||||
                    boxShadow: '0 0 0 0 rgba(211, 47, 47, 0.4)'  // error color with opacity
 | 
			
		||||
                  },
 | 
			
		||||
                  '50%': {
 | 
			
		||||
                    boxShadow: '0 0 0 8px rgba(211, 47, 47, 0)'
 | 
			
		||||
                  },
 | 
			
		||||
                  '100%': {
 | 
			
		||||
                    boxShadow: '0 0 0 0 rgba(211, 47, 47, 0)'
 | 
			
		||||
                  }
 | 
			
		||||
                },
 | 
			
		||||
                animation: messages.length > 0 ? 'glow 2s infinite' : 'none',
 | 
			
		||||
                '&:hover': {
 | 
			
		||||
                  animation: 'none'  // Stop animation on hover
 | 
			
		||||
                }
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              <ClearIcon />  {/* Added back the icon */}
 | 
			
		||||
            </IconButton>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
        </Box>
 | 
			
		||||
      </Box>
 | 
			
		||||
    </ThemeProvider>
 | 
			
		||||
@ -283,7 +434,7 @@ function App() {
 | 
			
		||||
  return (
 | 
			
		||||
    <ElectronProvider>
 | 
			
		||||
      {!ollamaInstalled ? (
 | 
			
		||||
        <OllamaCheck onInstalled={() => setOllamaInstalled(true)} />
 | 
			
		||||
        <OllamaCheck onInstalled={(arg: boolean) => setOllamaInstalled(arg)} />
 | 
			
		||||
      ) : (
 | 
			
		||||
        <AppContent />
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,11 @@
 | 
			
		||||
import React, { useEffect, useRef, useState, useMemo } from 'react';
 | 
			
		||||
import { Box, Typography, Paper, Avatar, IconButton } from '@mui/material';
 | 
			
		||||
import { Box, Typography, Paper, Avatar, IconButton, List, ListItem, ListItemIcon, ListItemText, useTheme } from '@mui/material';
 | 
			
		||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
 | 
			
		||||
import type { DocumentMetadata } from '../../../electron/types';
 | 
			
		||||
import ReactMarkdown from 'react-markdown';
 | 
			
		||||
import rehypeHighlight from 'rehype-highlight';
 | 
			
		||||
import 'highlight.js/styles/github.css';
 | 
			
		||||
import { Keyboard, Waves, Shield, DataObject, Folder, Lock } from '@mui/icons-material';
 | 
			
		||||
 | 
			
		||||
interface ChatMessage {
 | 
			
		||||
  id: string;
 | 
			
		||||
@ -54,7 +55,11 @@ export default function MessageList({ messages }: MessageListProps) {
 | 
			
		||||
          background: 'transparent',
 | 
			
		||||
        },
 | 
			
		||||
    }}>
 | 
			
		||||
      {messages.map((message) => (
 | 
			
		||||
 | 
			
		||||
      {messages.length>0 ? (
 | 
			
		||||
<>
 | 
			
		||||
 | 
			
		||||
{messages.map((message) => (
 | 
			
		||||
        <Box
 | 
			
		||||
          key={message.id}
 | 
			
		||||
          sx={{
 | 
			
		||||
@ -85,14 +90,16 @@ export default function MessageList({ messages }: MessageListProps) {
 | 
			
		||||
                fontWeight: 500
 | 
			
		||||
              }}
 | 
			
		||||
            >
 | 
			
		||||
              {message.isUser ? 'User' : 'Data Hound'}
 | 
			
		||||
              {message.isUser ? 'User' : 'Data Identification Manager'}
 | 
			
		||||
            </Typography>
 | 
			
		||||
          </Box>
 | 
			
		||||
          <Box
 | 
			
		||||
            component={Paper}
 | 
			
		||||
            elevation={1}
 | 
			
		||||
            sx={{
 | 
			
		||||
            p: 2.5,
 | 
			
		||||
              px: 2.5, // Keep horizontal padding
 | 
			
		||||
              py: message.text.includes('<think>') ? 0 : 1.5, // Reduce vertical padding
 | 
			
		||||
              pb: 1.5,
 | 
			
		||||
            flex: 1,
 | 
			
		||||
            bgcolor: message.isUser ? 'primary.main' : 'background.paper',
 | 
			
		||||
            color: message.isUser ? 'primary.contrastText' : 'text.primary',
 | 
			
		||||
@ -106,56 +113,67 @@ export default function MessageList({ messages }: MessageListProps) {
 | 
			
		||||
        >
 | 
			
		||||
          <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'
 | 
			
		||||
            wordBreak: 'break-word',
 | 
			
		||||
            minHeight: '1.5em',
 | 
			
		||||
            lineHeight: 1.6,
 | 
			
		||||
            '& pre': {
 | 
			
		||||
              backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
 | 
			
		||||
              padding: 0,
 | 
			
		||||
              borderRadius: 1,
 | 
			
		||||
              overflow: 'auto'
 | 
			
		||||
            },
 | 
			
		||||
            '& code': {
 | 
			
		||||
              backgroundColor: (theme) => theme.palette.mode === 'dark' ? '#1e1e1e' : '#f6f8fa',
 | 
			
		||||
              borderRadius: 1,
 | 
			
		||||
              fontSize: '85%'
 | 
			
		||||
            },
 | 
			
		||||
            '& h1, & h2, & h3, & h4, & h5, & h6': {
 | 
			
		||||
              marginTop: '0px', // Reduced from 24px
 | 
			
		||||
              marginBottom: '0px', // Added margin bottom for headers
 | 
			
		||||
              fontWeight: 600,
 | 
			
		||||
              lineHeight: 1.25
 | 
			
		||||
            },
 | 
			
		||||
            '& p': {
 | 
			
		||||
              marginTop: '0',
 | 
			
		||||
              marginBottom: '0px' // Added margin bottom for paragraphs
 | 
			
		||||
            },
 | 
			
		||||
            '& 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 0px 0'
 | 
			
		||||
            },
 | 
			
		||||
            '& ul, & ol': {
 | 
			
		||||
              paddingLeft: '2em',
 | 
			
		||||
              marginTop: '0px',    // Added margin top
 | 
			
		||||
              marginBottom: '0px'
 | 
			
		||||
            },
 | 
			
		||||
            '& li': {             // Added specific list item styling
 | 
			
		||||
              marginBottom: '0px'  // Add space between list items
 | 
			
		||||
            },
 | 
			
		||||
            '& li:last-child': {  // Remove bottom margin from last list item
 | 
			
		||||
              marginBottom: '0'
 | 
			
		||||
            },
 | 
			
		||||
            '& li > p': {         // Adjust paragraph spacing within list items
 | 
			
		||||
              marginBottom: '0px'
 | 
			
		||||
            },
 | 
			
		||||
            '& li > p:last-child': {
 | 
			
		||||
              marginBottom: '0'
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          }}>
 | 
			
		||||
            {message.text.includes('<think>') ? (
 | 
			
		||||
            {message.text.includes('<think>') || message.text.length==0 ? (
 | 
			
		||||
              message.text.split(/<think>|<\/think>/).map((segment, index) => {
 | 
			
		||||
                if (index % 2 === 1) { // This is a thinking section
 | 
			
		||||
                  // Calculate thinking time - assume 1 character = 0.1s
 | 
			
		||||
@ -181,13 +199,19 @@ export default function MessageList({ messages }: MessageListProps) {
 | 
			
		||||
                          />
 | 
			
		||||
                          <span>Reasoning</span>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <span className="timestamp">(thought for {thinkingTime}s)</span>
 | 
			
		||||
                        <span className="timestamp">(thought for {thinkingTime}ms)</span>
 | 
			
		||||
                      </div>
 | 
			
		||||
                      <div 
 | 
			
		||||
                        id={`thinking-content-${message.id}-${index}`}
 | 
			
		||||
                        className="thinking-content"
 | 
			
		||||
                      >
 | 
			
		||||
                      <ReactMarkdown
 | 
			
		||||
                        key={index}
 | 
			
		||||
                        className="markdown-body"
 | 
			
		||||
                        rehypePlugins={[rehypeHighlight]}
 | 
			
		||||
                      >
 | 
			
		||||
                        {segment}
 | 
			
		||||
                      </ReactMarkdown>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  );
 | 
			
		||||
@ -247,16 +271,178 @@ export default function MessageList({ messages }: MessageListProps) {
 | 
			
		||||
              ))}
 | 
			
		||||
            </Box>
 | 
			
		||||
          )}
 | 
			
		||||
          <Typography
 | 
			
		||||
            variant="caption"
 | 
			
		||||
            color={message.isUser ? '#ffffff' : 'text.secondary'}
 | 
			
		||||
            sx={{ display: 'block', mt: 0.5 }}
 | 
			
		||||
          >
 | 
			
		||||
            {new Date(message.timestamp).toLocaleTimeString()}
 | 
			
		||||
          </Typography>
 | 
			
		||||
          </Box>
 | 
			
		||||
        </Box>
 | 
			
		||||
      ))}
 | 
			
		||||
      </>
 | 
			
		||||
      ):(
 | 
			
		||||
        <>
 | 
			
		||||
          <WelcomeMessage/>
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    </Box>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const WelcomeMessage = () => {
 | 
			
		||||
  const [text, setText] = useState('');
 | 
			
		||||
  const fullText = 'Send a message...';
 | 
			
		||||
  const [showCursor, setShowCursor] = useState(true);
 | 
			
		||||
  const theme = useTheme();
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (text.length < fullText.length) {
 | 
			
		||||
      const timeout = setTimeout(() => {
 | 
			
		||||
        setText(fullText.slice(0, text.length + 1));
 | 
			
		||||
      }, 100);
 | 
			
		||||
      return () => clearTimeout(timeout);
 | 
			
		||||
    }
 | 
			
		||||
  }, [text]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    const cursorInterval = setInterval(() => {
 | 
			
		||||
      setShowCursor(prev => !prev);
 | 
			
		||||
    }, 530);
 | 
			
		||||
    return () => clearInterval(cursorInterval);
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Paper 
 | 
			
		||||
      elevation={0}
 | 
			
		||||
      sx={{
 | 
			
		||||
        width: '100%',
 | 
			
		||||
        maxWidth: '4xl',
 | 
			
		||||
        mx: 'auto',
 | 
			
		||||
        p: 4,
 | 
			
		||||
        background: 'rgb(0,0,0,0)',
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3 }}>
 | 
			
		||||
        {/* Animated Keyboard and Hands */}
 | 
			
		||||
        <Box sx={{ position: 'relative' }}>
 | 
			
		||||
          <Keyboard 
 | 
			
		||||
            style={{ 
 | 
			
		||||
              width: 64, 
 | 
			
		||||
              height: 64, 
 | 
			
		||||
              color: theme.palette.primary.main,
 | 
			
		||||
              animation: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite'
 | 
			
		||||
            }} 
 | 
			
		||||
          />
 | 
			
		||||
          <Box 
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              position: 'absolute', 
 | 
			
		||||
              bottom: -16, 
 | 
			
		||||
              left: '50%', 
 | 
			
		||||
              transform: 'translateX(-50%)'
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <Waves 
 | 
			
		||||
              style={{ 
 | 
			
		||||
                width: 32, 
 | 
			
		||||
                height: 32, 
 | 
			
		||||
                color: theme.palette.primary.light,
 | 
			
		||||
                animation: 'bounce 1s infinite'
 | 
			
		||||
              }} 
 | 
			
		||||
            />
 | 
			
		||||
          </Box>
 | 
			
		||||
        </Box>
 | 
			
		||||
 | 
			
		||||
        {/* Typing Animation */}
 | 
			
		||||
        <Typography 
 | 
			
		||||
          variant="h4" 
 | 
			
		||||
          sx={{ 
 | 
			
		||||
            fontWeight: 600,
 | 
			
		||||
            color: theme.palette.text.primary,
 | 
			
		||||
            mt: 2
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {text}
 | 
			
		||||
          <Box 
 | 
			
		||||
            component="span" 
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              ml: 0.5,
 | 
			
		||||
              opacity: showCursor ? 1 : 0,
 | 
			
		||||
              transition: 'opacity 0.3s'
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            |
 | 
			
		||||
          </Box>
 | 
			
		||||
        </Typography>
 | 
			
		||||
 | 
			
		||||
        {/* Example Questions */}
 | 
			
		||||
        <List sx={{ width: '100%', mt: 4 }}>
 | 
			
		||||
          <ListItem 
 | 
			
		||||
            component={Paper} 
 | 
			
		||||
            elevation={1}
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              mb: 2, 
 | 
			
		||||
              borderRadius: 1,
 | 
			
		||||
              transition: 'box-shadow 0.3s',
 | 
			
		||||
              '&:hover': { boxShadow: 3 }
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <Folder style={{ color: theme.palette.primary.main }} />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText 
 | 
			
		||||
              primary="Can you analyze my CSV file and create a visualization of the trends?"
 | 
			
		||||
              sx={{ color: theme.palette.text.primary }}
 | 
			
		||||
            />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
 | 
			
		||||
          <ListItem 
 | 
			
		||||
            component={Paper} 
 | 
			
		||||
            elevation={1}
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              mb: 2, 
 | 
			
		||||
              borderRadius: 1,
 | 
			
		||||
              transition: 'box-shadow 0.3s',
 | 
			
		||||
              '&:hover': { boxShadow: 3 }
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <Shield style={{ color: theme.palette.success.main }} />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText 
 | 
			
		||||
              primary="What security measures does Data443 implement for data protection?"
 | 
			
		||||
              sx={{ color: theme.palette.text.primary }}
 | 
			
		||||
            />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
 | 
			
		||||
          <ListItem 
 | 
			
		||||
            component={Paper} 
 | 
			
		||||
            elevation={1}
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              mb: 2, 
 | 
			
		||||
              borderRadius: 1,
 | 
			
		||||
              transition: 'box-shadow 0.3s',
 | 
			
		||||
              '&:hover': { boxShadow: 3 }
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <ListItemIcon>
 | 
			
		||||
              <Lock style={{ color: theme.palette.secondary.main }} />
 | 
			
		||||
            </ListItemIcon>
 | 
			
		||||
            <ListItemText 
 | 
			
		||||
              primary="How can Data443's products help with GDPR compliance?"
 | 
			
		||||
              sx={{ color: theme.palette.text.primary }}
 | 
			
		||||
            />
 | 
			
		||||
          </ListItem>
 | 
			
		||||
        </List>
 | 
			
		||||
 | 
			
		||||
        {/* Footer Note */}
 | 
			
		||||
        <Typography 
 | 
			
		||||
          variant="body2" 
 | 
			
		||||
          sx={{ 
 | 
			
		||||
            color: theme.palette.text.secondary,
 | 
			
		||||
            textAlign: 'center',
 | 
			
		||||
            mt: 3
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          Ask me anything about your data, security needs, or Data443 products
 | 
			
		||||
        </Typography>
 | 
			
		||||
      </Box>
 | 
			
		||||
    </Paper>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,9 @@ import {
 | 
			
		||||
  Alert,
 | 
			
		||||
  Box,
 | 
			
		||||
  Typography,
 | 
			
		||||
  InputAdornment,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  TextField,
 | 
			
		||||
} from '@mui/material';
 | 
			
		||||
import { 
 | 
			
		||||
  Folder as FolderIcon,
 | 
			
		||||
@ -21,6 +24,8 @@ import {
 | 
			
		||||
  Cloud as DropboxIcon,
 | 
			
		||||
  Chat as DiscordIcon,
 | 
			
		||||
  Computer as LocalIcon,
 | 
			
		||||
  X,
 | 
			
		||||
  Search,
 | 
			
		||||
} from '@mui/icons-material';
 | 
			
		||||
import { useElectron } from '../../hooks/useElectron';
 | 
			
		||||
 | 
			
		||||
@ -105,16 +110,48 @@ export default function DirectoryPicker({ onSelect }: DirectoryPickerProps) {
 | 
			
		||||
          mr: 2,
 | 
			
		||||
          mb: 2,
 | 
			
		||||
        }}>
 | 
			
		||||
                <Typography sx={{ pl:2 ,flexGrow: 1 }} variant="h6">Folders</Typography>
 | 
			
		||||
          <Button 
 | 
			
		||||
 | 
			
		||||
<TextField
 | 
			
		||||
      fullWidth
 | 
			
		||||
      size="small"
 | 
			
		||||
      placeholder="Search..."
 | 
			
		||||
      variant="outlined"
 | 
			
		||||
      InputProps={{
 | 
			
		||||
        startAdornment: (
 | 
			
		||||
          <InputAdornment position="start">
 | 
			
		||||
            <Search color="action" />
 | 
			
		||||
          </InputAdornment>
 | 
			
		||||
        ),
 | 
			
		||||
        // endAdornment:  && (
 | 
			
		||||
        //   <InputAdornment position="end">
 | 
			
		||||
        //     <IconButton
 | 
			
		||||
        //       aria-label="clear search"
 | 
			
		||||
        //       edge="end"
 | 
			
		||||
        //       size="small"
 | 
			
		||||
        //     >
 | 
			
		||||
        //       <CloseIcon />
 | 
			
		||||
        //     </IconButton>
 | 
			
		||||
        //   </InputAdornment>
 | 
			
		||||
        // )
 | 
			
		||||
      }}
 | 
			
		||||
      sx={{
 | 
			
		||||
        pl:2,
 | 
			
		||||
        pr:2,
 | 
			
		||||
        maxWidth: 'md',
 | 
			
		||||
        '& .MuiOutlinedInput-root': {
 | 
			
		||||
          '&:hover fieldset': {
 | 
			
		||||
            borderColor: 'primary.main',
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      }}
 | 
			
		||||
    />          <Button 
 | 
			
		||||
            variant="contained" 
 | 
			
		||||
            onClick={handleOpen}
 | 
			
		||||
            size="small"
 | 
			
		||||
            sx={{ 
 | 
			
		||||
              minWidth: 0,
 | 
			
		||||
              width: '32px',
 | 
			
		||||
              height: '32px',
 | 
			
		||||
              borderRadius: '50%',
 | 
			
		||||
              width: '40px',
 | 
			
		||||
              height: '40px',
 | 
			
		||||
              padding: 0,
 | 
			
		||||
              overflow: 'hidden',
 | 
			
		||||
              backgroundColor: 'primary.main',
 | 
			
		||||
@ -130,16 +167,15 @@ export default function DirectoryPicker({ onSelect }: DirectoryPickerProps) {
 | 
			
		||||
              },
 | 
			
		||||
              '&:hover': {
 | 
			
		||||
                backgroundColor: 'primary.dark',
 | 
			
		||||
                width: '180px',
 | 
			
		||||
                borderRadius: '16px',
 | 
			
		||||
                width: '400px',
 | 
			
		||||
                '& .buttonText': {
 | 
			
		||||
                  width: '110px',
 | 
			
		||||
                  width: '300px',
 | 
			
		||||
                  marginLeft: '8px',
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <AddIcon sx={{ fontSize: 20 }} />
 | 
			
		||||
            <AddIcon sx={{ ml:0.25, fontSize: 20 }} />
 | 
			
		||||
            <span className="buttonText">Add New Folder</span>
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Box>
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ const HomePanel = () => {
 | 
			
		||||
    }}>
 | 
			
		||||
      
 | 
			
		||||
      <Paper sx={{ p: 3, mb: 3 }}>
 | 
			
		||||
      <Typography variant="h5" gutterBottom>Welcome to Data Hound</Typography>
 | 
			
		||||
      <Typography variant="h5" gutterBottom>Welcome to Data Identification Manager</Typography>
 | 
			
		||||
        <Typography variant="body1" paragraph>
 | 
			
		||||
          This application helps you search through your files and interact with their contents using AI assistance.
 | 
			
		||||
        </Typography>
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ const theme = createTheme({
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
interface OllamaCheckProps {
 | 
			
		||||
  onInstalled: () => void;
 | 
			
		||||
  onInstalled: (arg:boolean) => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface OllamaStatus {
 | 
			
		||||
@ -37,7 +37,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
  const [modelStatus, setModelStatus] = useState<ModelStatus | null>(null);
 | 
			
		||||
  const [isChecking, setIsChecking] = useState(true);
 | 
			
		||||
  const [downloadProgress, setDownloadProgress] = useState<number>(0);
 | 
			
		||||
  const MODEL_NAME = 'damien113/datahound:latest';
 | 
			
		||||
  const MODEL_NAME = 'damien113/datahound-gpu:8b';
 | 
			
		||||
 | 
			
		||||
  const checkOllama = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
@ -53,16 +53,20 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const checkModel = async () => {
 | 
			
		||||
  const checkModel = async (retries = 3) => {
 | 
			
		||||
    try {
 | 
			
		||||
      const status = await window.electron.checkModel(MODEL_NAME);
 | 
			
		||||
      setModelStatus(status);
 | 
			
		||||
      if (status.installed) {
 | 
			
		||||
        onInstalled();
 | 
			
		||||
        onInstalled(true);
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('Error checking model:', error);
 | 
			
		||||
      setModelStatus({ installed: false, installing: false });
 | 
			
		||||
      if (retries > 0) {
 | 
			
		||||
        setTimeout(() => checkModel(retries - 1), 2000);
 | 
			
		||||
      } else {
 | 
			
		||||
        setModelStatus({ installed: false, installing: false });
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -72,6 +76,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
    try {
 | 
			
		||||
      // Set initial installation state once at the start
 | 
			
		||||
      setModelStatus({ installed: false, installing: true });
 | 
			
		||||
      onInstalled(false);
 | 
			
		||||
      setInstallError(null);
 | 
			
		||||
      setDownloadProgress(0);
 | 
			
		||||
      let downloadComplete = false;
 | 
			
		||||
@ -96,7 +101,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
        // Show verification screen for a moment before transitioning
 | 
			
		||||
        await new Promise(resolve => setTimeout(resolve, 2000)); // 2 second delay
 | 
			
		||||
        setModelStatus({ installed: true, installing: false });
 | 
			
		||||
        onInstalled();
 | 
			
		||||
        onInstalled(true);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      
 | 
			
		||||
@ -108,6 +113,8 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error('Error installing model:', error);
 | 
			
		||||
      setModelStatus({ installed: false, installing: false });
 | 
			
		||||
      
 | 
			
		||||
      onInstalled(false);
 | 
			
		||||
      setInstallError(error instanceof Error ? error.message : 'Unknown error occurred');
 | 
			
		||||
      setDownloadProgress(0);
 | 
			
		||||
    }
 | 
			
		||||
@ -126,7 +133,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
      if (!ollamaStatus?.installed || !ollamaStatus?.running) {
 | 
			
		||||
        checkOllama();
 | 
			
		||||
      }
 | 
			
		||||
    }, 5000);
 | 
			
		||||
    }, 2000);
 | 
			
		||||
 | 
			
		||||
    // Cleanup interval on unmount
 | 
			
		||||
    return () => clearInterval(interval);
 | 
			
		||||
@ -236,7 +243,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
        color: 'text.primary',
 | 
			
		||||
      }}>
 | 
			
		||||
        <Typography variant="h5" sx={{ color: 'text.primary', mb: 1 }}>
 | 
			
		||||
          {modelStatus?.installing ? 'Installing Data Hound AI Model' : 'AI Model Required'}
 | 
			
		||||
          {modelStatus?.installing ? 'Installing Data Identification Manager AI Model' : 'AI Model Required'}
 | 
			
		||||
        </Typography>
 | 
			
		||||
        {installError && (
 | 
			
		||||
          <Typography sx={{ color: 'error.main', mb: 2, textAlign: 'center' }}>
 | 
			
		||||
@ -284,7 +291,7 @@ export function OllamaCheck({ onInstalled }: OllamaCheckProps) {
 | 
			
		||||
              fontSize: '1.1rem',
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            Download Data Hound AI Model
 | 
			
		||||
            Download Data Identification Manager AI Model
 | 
			
		||||
          </Button>
 | 
			
		||||
        )}
 | 
			
		||||
      </Box>
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ html, body {
 | 
			
		||||
 | 
			
		||||
.thinking-section {
 | 
			
		||||
  border-radius: 8px;
 | 
			
		||||
  margin: 0 0 8px 0;
 | 
			
		||||
  margin: 0 0 28px 0;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  background-color: #1976d2;
 | 
			
		||||
  color: white;
 | 
			
		||||
@ -42,11 +42,10 @@ html, body {
 | 
			
		||||
[data-theme="dark"] .thinking-section {
 | 
			
		||||
  background-color: #1565c0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.thinking-header {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  justify-content: space-between; /* Ensures left content stays left, timestamp stays right */
 | 
			
		||||
  padding: 12px 16px;
 | 
			
		||||
  cursor: pointer;
 | 
			
		||||
  user-select: none;
 | 
			
		||||
@ -68,13 +67,17 @@ html, body {
 | 
			
		||||
[data-theme="dark"] .thinking-header:hover {
 | 
			
		||||
  background-color: rgba(0, 0, 0, 0.3);
 | 
			
		||||
}
 | 
			
		||||
.markdown-body {
 | 
			
		||||
  padding-top: ;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.thinking-header-left {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  align-items: center;
 | 
			
		||||
  gap: 8px;
 | 
			
		||||
  gap: 8px; /* Controls spacing between chevron and "Reasoning" */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.thinking-header .indicator {
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
@ -87,8 +90,7 @@ html, body {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.thinking-header .timestamp {
 | 
			
		||||
  font-size: 0.85rem;
 | 
			
		||||
  color: rgba(255, 255, 255, 0.7);
 | 
			
		||||
  margin-left: auto; /* Pushes it to the right */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.thinking-content {
 | 
			
		||||
 | 
			
		||||
@ -5,17 +5,23 @@ import path from 'path';
 | 
			
		||||
// https://vitejs.dev/config/
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  plugins: [react()],
 | 
			
		||||
  base: process.env.ELECTRON_VITE_DEV_SERVER_URL ? '/' : './',
 | 
			
		||||
  base: './',
 | 
			
		||||
  build: {
 | 
			
		||||
    outDir: 'dist',
 | 
			
		||||
    emptyOutDir: true,
 | 
			
		||||
    target: 'esnext',
 | 
			
		||||
    rollupOptions: {
 | 
			
		||||
      external: ['http', 'https', 'path', 'fs', 'electron']
 | 
			
		||||
      external: ['electron', 'electron-store', ...Object.keys(require('./package.json').dependencies)],
 | 
			
		||||
      output: {
 | 
			
		||||
        format: 'es',
 | 
			
		||||
        entryFileNames: '[name].js',
 | 
			
		||||
        chunkFileNames: '[name].js',
 | 
			
		||||
        assetFileNames: '[name][extname]'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    assetsDir: '.',
 | 
			
		||||
    minify: true,
 | 
			
		||||
    sourcemap: false
 | 
			
		||||
    assetsInlineLimit: 0,
 | 
			
		||||
    minify: 'esbuild',
 | 
			
		||||
    sourcemap: true
 | 
			
		||||
  },
 | 
			
		||||
  resolve: {
 | 
			
		||||
    alias: {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user