Projects Oopsidian

code of app.tsx with tailwind css

// App.tsx

import React, { useState, useEffect } from 'react';

import ReactMarkdown from 'react-markdown';

import { PlusCircle, FileText, Save } from 'lucide-react';

import Chatbot from './Chatbot';

import NoteGraph from './NoteGraph'

import './App.css'

  

interface Note {

id: string;

title: string;

content: string;

links: string[];

lastModified: Date;

}

  

interface GraphData {

nodes: Array<{ id: string; name: string }>;

links: Array<{ source: string; target: string }>;

}

  

const App: React.FC = () => {

  

const [notes, setNotes] = useState<Note[]>(() => {

const savedNotes = localStorage.getItem('notes');

return savedNotes ? JSON.parse(savedNotes) : [];

});

const [currentNote, setCurrentNote] = useState<Note | null>(null);

const [graphData, setGraphData] = useState<GraphData>({ nodes: [], links: [] });

const [isDarkMode, setIsDarkMode] = useState(true);

  

// Save notes to localStorage whenever they change

useEffect(() => {

localStorage.setItem('notes', JSON.stringify(notes));

}, [notes]);

  

// Update graph data whenever notes change

useEffect(() => {

updateGraphData(notes);

}, [notes]);

  

const extractLinks = (content: string): string[] => {

const linkRegex = /\[\[(.*?)\]\]/g;

const matches = content.match(linkRegex) || [];

return matches.map(match => match.slice(2, -2));

};

  

const updateGraphData = (notesData: Note[]) => {

const nodes = notesData.map(note => ({

id: note.id,

name: note.title,

}));

  

const links: Array<{ source: string; target: string }> = [];

notesData.forEach(note => {

note.links.forEach(linkTitle => {

const targetNote = notesData.find(n => n.title === linkTitle);

if (targetNote) {

links.push({

source: note.id,

target: targetNote.id,

});

}

});

});

  

setGraphData({ nodes, links });

};

  

const createNewNote = () => {

  

const newNote: Note = {

id: Date.now().toString(),

title: 'Untitled Note',

content: '',

links: [],

lastModified: new Date(),

};

setNotes(prev => [...prev, newNote]);

setCurrentNote(newNote);

  
  
  

};

  

const updateNoteContent = (id: string, content: string) => {

  

setTimeout(() => {

window.scrollTo(0, 0);

}, 1);

  

const updatedNotes = notes.map(note => {

if (note.id === id) {

const links = extractLinks(content);

return { ...note, content, links, lastModified: new Date() };

}

return note;

});

setNotes(updatedNotes);

if (currentNote) {

setCurrentNote({ ...currentNote, content, lastModified: new Date() });

}

};

  

const updateNoteTitle = (id: string, title: string) => {

  
  

  

const updatedNotes = notes.map(note => {

if (note.id === id) {

return { ...note, title, lastModified: new Date() };

}

return note;

});

setNotes(updatedNotes);

if (currentNote) {

setCurrentNote({ ...currentNote, title, lastModified: new Date() });

}

};

  

const deleteNote = (id: string) => {

setNotes(notes.filter(note => note.id !== id));

if (currentNote?.id === id) {

setCurrentNote(null);

}

};

  

const toggleTheme = () => {

setIsDarkMode(!isDarkMode);

document.documentElement.classList.toggle('dark');

};

  

useEffect(()=>{

console.log("reend")

});

  

return (

<div className={`flex h-screen ${isDarkMode ? 'dark' : ''}`}>

{/* Sidebar */}

<div className="lg:w-64 w-16 bg-gray-100 dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 flex flex-col transition-all duration-300 ease-in-out">

{/* Sidebar Header */}

<div className="p-4 border-b border-gray-200 dark:border-gray-700">

<button

onClick={createNewNote}

className="w-full flex items-center justify-center gap-2 bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-colors"

>

<PlusCircle size={20} />

<span>New Note</span>

</button>

</div>

  

{/* Notes List */}

<div className="flex-1 overflow-y-auto">

{notes.map(note => (

<div

key={note.id}

onClick={() => {

  
  

setTimeout(() => {

window.scrollTo(0, 0);

}, 10);

setCurrentNote(note);

  

console.log("hi")

  

setTimeout(() => {

window.scrollTo(0, 0);

}, 10);

}

}

className={`flex items-center gap-2 p-3 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors

${currentNote?.id === note.id ? 'bg-gray-200 dark:bg-gray-700' : ''}`}

>

<FileText size={18} className="text-gray-500 dark:text-gray-400" />

<div className="flex-1 min-w-0">

<div className="truncate text-gray-900 dark:text-gray-100">{note.title}</div>

<div className="text-xs text-gray-500 dark:text-gray-400">

{new Date(note.lastModified).toLocaleDateString()}

</div>

</div>

</div>

))}

</div>

  

{/* Theme Toggle */}

<div className="p-4 border-t border-gray-200 dark:border-gray-700">

<button

onClick={toggleTheme}

className="w-full py-2 px-4 rounded-lg bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200"

>

{isDarkMode ? '🌞 Light Mode' : '🌙 Dark Mode'}

</button>

</div>

</div>

  

{/* Main Content */}

<div className="flex-1 flex flex-col bg-white dark:bg-gray-900" >

{currentNote ? (

<div className="flex-1 flex">

{/* Editor */}

<div className="flex-1 flex flex-col p-4 border-r border-gray-200 dark:border-gray-700">

<input

type="text"

value={currentNote.title}

onChange={(e) => updateNoteTitle(currentNote.id, e.target.value)}

className="w-full mb-4 p-2 text-lg font-semibold bg-transparent border-b border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-blue-500"

placeholder="Note Title"

/>

<textarea

style={{"height":"50vh"}}

value={currentNote.content}

onChange={(e) => updateNoteContent(currentNote.id, e.target.value)}

className="flex-1 w-full p-2 bg-transparent text-gray-900 dark:text-gray-100 focus:outline-none resize-none"

placeholder="Write your note here... Use [[Note Title]] to link to other notes."

/>

<div className="flex justify-between items-center pt-2 text-sm text-gray-500 dark:text-gray-400">

<div>

Last modified: {new Date(currentNote.lastModified).toLocaleString()}

</div>

<button

onClick={() => deleteNote(currentNote.id)}

className="text-red-500 hover:text-red-600 px-2 py-1 rounded"

>

Delete Note

</button>

</div>

</div>

  

{/* Preview */}

<div className="flex-1 p-4 overflow-auto bg-gray-50 dark:bg-gray-800" style={{"height":"50vh"}}>

<div className="prose dark:prose-invert max-w-none">

<ReactMarkdown>{currentNote.content}

  

  

</ReactMarkdown>

</div>

</div>

<div className="w-96">

<Chatbot noteTitle={currentNote.content} />

</div>

</div>

  
  
  

) : (

<div className="flex-1 flex items-center justify-center text-gray-500 dark:text-gray-400">

Select or create a note to get started

</div>

)}

  

{/* Graph View */}

<div className="h-64 border-t border-gray-200 dark:border-gray-700">

<NoteGraph

graphData={graphData}

notes={notes}

setCurrentNote={setCurrentNote}

isDarkMode={isDarkMode}

/>

  

</div>

</div>

</div>

);

};

  

export default App;