ai-notes / index.html
shri210620's picture
Add 3 files
dc1407e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NeoNotes - Advanced Note Taking with Mind Mapping</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cytoscape.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/cytoscape-cose-bilkent.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/[email protected]/dist/universal-sentence-encoder.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
#cy {
width: 100%;
height: 500px;
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
background-color: #f9fafb;
}
.note-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.tag-chip {
transition: all 0.2s ease;
}
.tag-chip:hover {
background-color: #3b82f6;
color: white;
}
.mindmap-node {
transition: all 0.3s ease;
}
.mindmap-node:hover {
transform: scale(1.05);
}
.ql-editor {
min-height: 200px;
font-size: 16px;
line-height: 1.6;
}
.search-highlight {
background-color: rgba(255, 255, 0, 0.5);
}
</style>
</head>
<body class="bg-gray-50">
<div class="flex h-screen overflow-hidden">
<!-- Sidebar -->
<div class="w-64 bg-white border-r border-gray-200 flex flex-col">
<div class="p-4 border-b border-gray-200">
<h1 class="text-2xl font-bold text-blue-600 flex items-center">
<i class="fas fa-project-diagram mr-2"></i> NeoNotes
</h1>
<p class="text-sm text-gray-500">Advanced note-taking with mind mapping</p>
</div>
<div class="p-4">
<button id="new-note-btn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md flex items-center justify-center mb-4">
<i class="fas fa-plus mr-2"></i> New Note
</button>
<div class="mb-4">
<h3 class="font-semibold text-gray-700 mb-2">Quick Filters</h3>
<div class="space-y-1">
<button class="filter-btn w-full text-left px-2 py-1 rounded hover:bg-gray-100" data-filter="recent">
<i class="fas fa-clock mr-2 text-gray-500"></i> Recent
</button>
<button class="filter-btn w-full text-left px-2 py-1 rounded hover:bg-gray-100" data-filter="starred">
<i class="fas fa-star mr-2 text-yellow-500"></i> Starred
</button>
<button class="filter-btn w-full text-left px-2 py-1 rounded hover:bg-gray-100" data-filter="connected">
<i class="fas fa-link mr-2 text-blue-500"></i> Highly Connected
</button>
</div>
</div>
<div class="mb-4">
<h3 class="font-semibold text-gray-700 mb-2">Tags</h3>
<div id="tag-cloud" class="flex flex-wrap gap-2">
<!-- Tags will be dynamically added here -->
</div>
</div>
<div class="mb-4">
<h3 class="font-semibold text-gray-700 mb-2">Mind Map Views</h3>
<div class="space-y-1">
<button class="map-view-btn w-full text-left px-2 py-1 rounded hover:bg-gray-100" data-view="full">
<i class="fas fa-globe mr-2 text-green-500"></i> Full Knowledge Graph
</button>
<button class="map-view-btn w-full text-left px-2 py-1 rounded hover:bg-gray-100" data-view="current">
<i class="fas fa-focus mr-2 text-purple-500"></i> Current Note Context
</button>
</div>
</div>
</div>
<div class="mt-auto p-4 border-t border-gray-200">
<div class="flex items-center">
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600">
<i class="fas fa-user"></i>
</div>
<div class="ml-2">
<p class="text-sm font-medium">User</p>
<p class="text-xs text-gray-500">Premium</p>
</div>
</div>
</div>
</div>
<!-- Main Content -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Bar -->
<div class="bg-white border-b border-gray-200 p-4 flex items-center justify-between">
<div class="relative w-1/3">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
<input id="search-input" type="text" class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm" placeholder="Search notes, tags, or connections...">
</div>
<div class="flex items-center space-x-4">
<button id="ai-suggest-btn" class="flex items-center text-sm text-gray-600 hover:text-blue-600">
<i class="fas fa-lightbulb mr-1"></i> AI Suggestions
</button>
<button id="embedding-btn" class="flex items-center text-sm text-gray-600 hover:text-blue-600">
<i class="fas fa-brain mr-1"></i> Analyze Text
</button>
<button id="visualize-btn" class="flex items-center text-sm text-gray-600 hover:text-blue-600">
<i class="fas fa-project-diagram mr-1"></i> Visualize
</button>
</div>
</div>
<!-- Dual Panel Layout -->
<div class="flex-1 flex overflow-hidden">
<!-- Notes List Panel -->
<div class="w-1/3 border-r border-gray-200 bg-white overflow-y-auto">
<div class="p-4">
<h2 class="text-lg font-semibold text-gray-800 mb-4">Your Notes</h2>
<div id="notes-list" class="space-y-3">
<!-- Notes will be dynamically added here -->
</div>
</div>
</div>
<!-- Note Editor and Visualization Panel -->
<div class="flex-1 flex flex-col overflow-hidden">
<div class="flex-1 overflow-y-auto p-6">
<!-- Note Editor View -->
<div id="editor-view" class="h-full">
<div class="mb-4 flex justify-between items-center">
<input id="note-title" type="text" class="text-2xl font-bold w-full border-none focus:ring-0 focus:outline-none" placeholder="Note Title">
<div class="flex space-x-2">
<button id="star-note" class="text-gray-400 hover:text-yellow-500">
<i class="far fa-star"></i>
</button>
<button id="delete-note" class="text-gray-400 hover:text-red-500">
<i class="far fa-trash-alt"></i>
</button>
</div>
</div>
<div class="mb-4">
<div id="tags-input" class="flex flex-wrap items-center gap-2">
<!-- Tags will be dynamically added here -->
<input id="new-tag-input" type="text" class="flex-1 min-w-0 border-none focus:ring-0 focus:outline-none text-sm" placeholder="Add tags...">
</div>
</div>
<div id="editor-container" class="bg-white rounded-lg border border-gray-200">
<div id="editor" class="p-4"></div>
</div>
<div class="mt-4">
<h3 class="font-medium text-gray-700 mb-2">Connections</h3>
<div id="connections-list" class="flex flex-wrap gap-2">
<!-- Connections will be dynamically added here -->
</div>
</div>
</div>
<!-- Visualization View -->
<div id="visualization-view" class="h-full hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-semibold">Knowledge Graph Visualization</h2>
<div class="flex space-x-2">
<button id="close-visualization" class="text-gray-600 hover:text-blue-600">
<i class="fas fa-times"></i> Close
</button>
</div>
</div>
<div id="cy"></div>
<div class="mt-4 flex justify-between">
<div>
<span class="inline-block w-3 h-3 rounded-full bg-blue-500 mr-1"></span>
<span class="text-sm">Current Note</span>
<span class="inline-block w-3 h-3 rounded-full bg-green-500 mr-1 ml-2"></span>
<span class="text-sm">Connected Notes</span>
<span class="inline-block w-3 h-3 rounded-full bg-gray-300 mr-1 ml-2"></span>
<span class="text-sm">Other Notes</span>
</div>
<div class="flex space-x-2">
<button id="layout-force" class="px-3 py-1 text-sm bg-gray-100 rounded hover:bg-gray-200">
Force Layout
</button>
<button id="layout-hierarchical" class="px-3 py-1 text-sm bg-gray-100 rounded hover:bg-gray-200">
Hierarchical
</button>
<button id="layout-concentric" class="px-3 py-1 text-sm bg-gray-100 rounded hover:bg-gray-200">
Concentric
</button>
</div>
</div>
</div>
</div>
<!-- AI Suggestions Panel -->
<div id="ai-panel" class="hidden border-t border-gray-200 bg-gray-50 p-4">
<div class="flex justify-between items-center mb-3">
<h3 class="font-medium">AI Suggestions</h3>
<button id="close-ai" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div id="ai-suggestions" class="space-y-3">
<div class="p-3 bg-white rounded-lg border border-gray-200">
<p class="font-medium text-blue-600 mb-1">Related Concepts</p>
<div class="flex flex-wrap gap-2">
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">Machine Learning</span>
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">Neural Networks</span>
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full">Deep Learning</span>
</div>
</div>
<div class="p-3 bg-white rounded-lg border border-gray-200">
<p class="font-medium text-purple-600 mb-1">Potential Connections</p>
<ul class="list-disc list-inside text-sm space-y-1">
<li>Link to your note on "Introduction to AI"</li>
<li>Connect with "History of Neural Networks"</li>
<li>Reference your research on "Backpropagation"</li>
</ul>
</div>
<div class="p-3 bg-white rounded-lg border border-gray-200">
<p class="font-medium text-green-600 mb-1">Content Suggestions</p>
<p class="text-sm">Consider adding a section on different types of neural network architectures (CNNs, RNNs, Transformers) and their applications.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Quill Editor -->
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
<link href="https://cdn.quilljs.com/1.3.6/quill.snow.css" rel="stylesheet">
<script>
// Initialize the app when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
// Initialize Quill editor
const quill = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: [
[{ 'header': [1, 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
['link', 'image', 'video'],
['clean']
]
},
placeholder: 'Write your note here...'
});
// Sample data for demonstration
let notes = [
{
id: '1',
title: 'Introduction to Machine Learning',
content: '<p>Machine learning is a subset of artificial intelligence that focuses on building systems that can learn from data.</p><p>Key concepts include supervised learning, unsupervised learning, and reinforcement learning.</p>',
tags: ['AI', 'Machine Learning', 'Data Science'],
starred: true,
createdAt: new Date('2023-05-15'),
updatedAt: new Date('2023-05-16'),
connections: ['2', '3']
},
{
id: '2',
title: 'Neural Networks Fundamentals',
content: '<p>Neural networks are computing systems inspired by biological neural networks.</p><p>They consist of layers of interconnected nodes (neurons) that process information.</p>',
tags: ['AI', 'Neural Networks', 'Deep Learning'],
starred: false,
createdAt: new Date('2023-06-02'),
updatedAt: new Date('2023-06-05'),
connections: ['1', '3', '4']
},
{
id: '3',
title: 'Supervised Learning Techniques',
content: '<p>Supervised learning involves training a model on labeled data.</p><p>Common algorithms include linear regression, logistic regression, and support vector machines.</p>',
tags: ['Machine Learning', 'Supervised Learning', 'Algorithms'],
starred: false,
createdAt: new Date('2023-06-10'),
updatedAt: new Date('2023-06-12'),
connections: ['1', '2']
},
{
id: '4',
title: 'Deep Learning Architectures',
content: '<p>Deep learning uses neural networks with multiple hidden layers.</p><p>Popular architectures include CNNs for image processing and RNNs for sequence data.</p>',
tags: ['Deep Learning', 'Neural Networks', 'AI'],
starred: true,
createdAt: new Date('2023-07-01'),
updatedAt: new Date('2023-07-05'),
connections: ['2', '5']
},
{
id: '5',
title: 'Natural Language Processing Basics',
content: '<p>NLP enables computers to understand, interpret, and generate human language.</p><p>Key tasks include sentiment analysis, machine translation, and text summarization.</p>',
tags: ['NLP', 'AI', 'Text Processing'],
starred: false,
createdAt: new Date('2023-07-15'),
updatedAt: new Date('2023-07-18'),
connections: ['4']
}
];
let currentNoteId = null;
let allTags = [];
// Initialize Cytoscape for visualization
let cy = cytoscape({
container: document.getElementById('cy'),
elements: [],
style: [
{
selector: 'node',
style: {
'label': 'data(label)',
'text-wrap': 'wrap',
'text-max-width': '100px',
'text-valign': 'center',
'text-halign': 'center',
'background-color': '#3b82f6',
'color': '#ffffff',
'width': '60',
'height': '60',
'font-size': '10'
}
},
{
selector: 'node[type="current"]',
style: {
'background-color': '#ef4444',
'width': '80',
'height': '80',
'font-size': '12'
}
},
{
selector: 'node[type="connected"]',
style: {
'background-color': '#10b981'
}
},
{
selector: 'node[type="other"]',
style: {
'background-color': '#9ca3af'
}
},
{
selector: 'edge',
style: {
'width': 2,
'line-color': '#9ca3af',
'curve-style': 'bezier',
'target-arrow-shape': 'triangle',
'target-arrow-color': '#9ca3af'
}
},
{
selector: 'edge[type="strong"]',
style: {
'line-color': '#3b82f6',
'width': 3,
'target-arrow-color': '#3b82f6'
}
}
],
layout: {
name: 'cose-bilkent',
animate: true,
randomize: true,
nodeRepulsion: 4500,
idealEdgeLength: 100,
nodeDimensionsIncludeLabels: true
}
});
// Extract all tags from notes
function extractTags() {
allTags = [];
notes.forEach(note => {
note.tags.forEach(tag => {
if (!allTags.includes(tag)) {
allTags.push(tag);
}
});
});
renderTagCloud();
}
// Render tag cloud
function renderTagCloud() {
const tagCloud = document.getElementById('tag-cloud');
tagCloud.innerHTML = '';
allTags.forEach(tag => {
const tagElement = document.createElement('div');
tagElement.className = 'tag-chip px-2 py-1 bg-gray-100 text-gray-800 text-xs rounded-full cursor-pointer';
tagElement.textContent = tag;
tagElement.addEventListener('click', () => filterByTag(tag));
tagCloud.appendChild(tagElement);
});
}
// Filter notes by tag
function filterByTag(tag) {
const filteredNotes = notes.filter(note => note.tags.includes(tag));
renderNotesList(filteredNotes);
// Highlight the selected tag
document.querySelectorAll('.tag-chip').forEach(chip => {
if (chip.textContent === tag) {
chip.classList.add('bg-blue-100', 'text-blue-800');
} else {
chip.classList.remove('bg-blue-100', 'text-blue-800');
}
});
}
// Render notes list
function renderNotesList(notesToRender = notes) {
const notesList = document.getElementById('notes-list');
notesList.innerHTML = '';
// Sort by updated date (newest first)
const sortedNotes = [...notesToRender].sort((a, b) => b.updatedAt - a.updatedAt);
sortedNotes.forEach(note => {
const noteElement = document.createElement('div');
noteElement.className = `note-card p-3 bg-white border border-gray-200 rounded-lg cursor-pointer transition-all ${currentNoteId === note.id ? 'border-blue-500 border-2' : ''}`;
noteElement.innerHTML = `
<div class="flex justify-between items-start">
<h3 class="font-medium text-gray-800 truncate">${note.title}</h3>
<div class="flex items-center">
${note.starred ? '<i class="fas fa-star text-yellow-500 ml-2"></i>' : '<i class="far fa-star text-gray-300 ml-2"></i>'}
</div>
</div>
<p class="text-sm text-gray-500 mt-1 line-clamp-2">${note.content.replace(/<[^>]*>/g, '').substring(0, 100)}</p>
<div class="flex flex-wrap gap-1 mt-2">
${note.tags.map(tag => `<span class="px-1.5 py-0.5 bg-gray-100 text-gray-600 text-xs rounded">${tag}</span>`).join('')}
</div>
<div class="flex justify-between items-center mt-2">
<span class="text-xs text-gray-400">${formatDate(note.updatedAt)}</span>
<span class="text-xs text-blue-500">${note.connections.length} connections</span>
</div>
`;
noteElement.addEventListener('click', () => loadNote(note.id));
notesList.appendChild(noteElement);
});
}
// Format date
function formatDate(date) {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
return date.toLocaleDateString(undefined, options);
}
// Load a note into the editor
function loadNote(noteId) {
const note = notes.find(n => n.id === noteId);
if (!note) return;
currentNoteId = noteId;
document.getElementById('note-title').value = note.title;
quill.root.innerHTML = note.content;
// Update tags input
const tagsInput = document.getElementById('tags-input');
tagsInput.innerHTML = '';
note.tags.forEach(tag => {
const tagElement = document.createElement('div');
tagElement.className = 'tag-chip px-2 py-1 bg-gray-100 text-gray-800 text-xs rounded-full flex items-center';
tagElement.innerHTML = `
${tag}
<button class="ml-1 text-gray-500 hover:text-gray-700" data-tag="${tag}">
<i class="fas fa-times text-xs"></i>
</button>
`;
tagsInput.insertBefore(tagElement, document.getElementById('new-tag-input'));
// Add event listener to remove tag
tagElement.querySelector('button').addEventListener('click', (e) => {
e.stopPropagation();
removeTagFromNote(noteId, tag);
});
});
// Update starred status
const starButton = document.getElementById('star-note');
if (note.starred) {
starButton.innerHTML = '<i class="fas fa-star text-yellow-500"></i>';
} else {
starButton.innerHTML = '<i class="far fa-star"></i>';
}
// Update connections list
updateConnectionsList(noteId);
// Re-render notes list to highlight current note
renderNotesList();
}
// Update connections list for a note
function updateConnectionsList(noteId) {
const note = notes.find(n => n.id === noteId);
if (!note) return;
const connectionsList = document.getElementById('connections-list');
connectionsList.innerHTML = '';
note.connections.forEach(connId => {
const connectedNote = notes.find(n => n.id === connId);
if (connectedNote) {
const connElement = document.createElement('div');
connElement.className = 'px-3 py-1 bg-blue-50 text-blue-700 text-sm rounded-full flex items-center cursor-pointer';
connElement.innerHTML = `
${connectedNote.title}
<button class="ml-1 text-blue-400 hover:text-blue-700" data-connection="${connId}">
<i class="fas fa-times text-xs"></i>
</button>
`;
connectionsList.appendChild(connElement);
// Add event listener to remove connection
connElement.querySelector('button').addEventListener('click', (e) => {
e.stopPropagation();
removeConnection(noteId, connId);
});
// Add event listener to navigate to connected note
connElement.addEventListener('click', () => {
loadNote(connId);
});
}
});
// Add "Add Connection" button if there are possible connections
const possibleConnections = notes.filter(n => n.id !== noteId && !note.connections.includes(n.id));
if (possibleConnections.length > 0) {
const addConnElement = document.createElement('div');
addConnElement.className = 'px-3 py-1 bg-gray-100 text-gray-700 text-sm rounded-full flex items-center cursor-pointer';
addConnElement.innerHTML = `
<i class="fas fa-plus mr-1"></i> Add Connection
`;
connectionsList.appendChild(addConnElement);
addConnElement.addEventListener('click', () => {
showConnectionModal(noteId);
});
}
}
// Show modal to add connections
function showConnectionModal(noteId) {
// In a real app, this would be a proper modal
// For this demo, we'll just connect to a random note
const note = notes.find(n => n.id === noteId);
if (!note) return;
const possibleConnections = notes.filter(n => n.id !== noteId && !note.connections.includes(n.id));
if (possibleConnections.length > 0) {
const randomConn = possibleConnections[Math.floor(Math.random() * possibleConnections.length)];
addConnection(noteId, randomConn.id);
alert(`Connected to: ${randomConn.title}`);
}
}
// Add a connection between notes
function addConnection(noteId1, noteId2) {
const note1 = notes.find(n => n.id === noteId1);
const note2 = notes.find(n => n.id === noteId2);
if (note1 && note2) {
if (!note1.connections.includes(noteId2)) {
note1.connections.push(noteId2);
}
if (!note2.connections.includes(noteId1)) {
note2.connections.push(noteId1);
}
if (currentNoteId === noteId1) {
updateConnectionsList(noteId1);
}
renderNotesList();
}
}
// Remove a connection between notes
function removeConnection(noteId1, noteId2) {
const note1 = notes.find(n => n.id === noteId1);
const note2 = notes.find(n => n.id === noteId2);
if (note1 && note2) {
note1.connections = note1.connections.filter(id => id !== noteId2);
note2.connections = note2.connections.filter(id => id !== noteId1);
if (currentNoteId === noteId1) {
updateConnectionsList(noteId1);
}
renderNotesList();
}
}
// Remove tag from note
function removeTagFromNote(noteId, tag) {
const note = notes.find(n => n.id === noteId);
if (note) {
note.tags = note.tags.filter(t => t !== tag);
extractTags();
if (currentNoteId === noteId) {
loadNote(noteId);
}
}
}
// Create a new note
function createNewNote() {
const newNote = {
id: Date.now().toString(),
title: 'Untitled Note',
content: '',
tags: [],
starred: false,
createdAt: new Date(),
updatedAt: new Date(),
connections: []
};
notes.unshift(newNote);
extractTags();
renderNotesList();
loadNote(newNote.id);
// Focus on title input
document.getElementById('note-title').focus();
}
// Save current note
function saveCurrentNote() {
if (!currentNoteId) return;
const note = notes.find(n => n.id === currentNoteId);
if (note) {
note.title = document.getElementById('note-title').value;
note.content = quill.root.innerHTML;
note.updatedAt = new Date();
// Get tags from tags input
const tagElements = document.querySelectorAll('#tags-input .tag-chip');
note.tags = Array.from(tagElements).map(el => el.textContent.trim());
renderNotesList();
}
}
// Delete current note
function deleteCurrentNote() {
if (!currentNoteId) return;
if (confirm('Are you sure you want to delete this note?')) {
// Remove this note from other notes' connections
notes.forEach(note => {
note.connections = note.connections.filter(connId => connId !== currentNoteId);
});
// Remove the note itself
notes = notes.filter(note => note.id !== currentNoteId);
currentNoteId = null;
extractTags();
renderNotesList();
// Clear editor
document.getElementById('note-title').value = '';
quill.root.innerHTML = '';
document.getElementById('tags-input').innerHTML = '';
document.getElementById('connections-list').innerHTML = '';
document.getElementById('star-note').innerHTML = '<i class="far fa-star"></i>';
}
}
// Toggle star status of current note
function toggleStarNote() {
if (!currentNoteId) return;
const note = notes.find(n => n.id === currentNoteId);
if (note) {
note.starred = !note.starred;
const starButton = document.getElementById('star-note');
if (note.starred) {
starButton.innerHTML = '<i class="fas fa-star text-yellow-500"></i>';
} else {
starButton.innerHTML = '<i class="far fa-star"></i>';
}
renderNotesList();
}
}
// Add tag to current note
function addTagToCurrentNote(tag) {
if (!currentNoteId || !tag) return;
const note = notes.find(n => n.id === currentNoteId);
if (note && !note.tags.includes(tag)) {
note.tags.push(tag);
extractTags();
loadNote(currentNoteId);
}
}
// Visualize knowledge graph
function visualizeKnowledgeGraph() {
if (!currentNoteId) {
visualizeFullGraph();
return;
}
// Show visualization view
document.getElementById('editor-view').classList.add('hidden');
document.getElementById('visualization-view').classList.remove('hidden');
// Clear existing elements
cy.elements().remove();
// Add current note as center node
const currentNote = notes.find(n => n.id === currentNoteId);
if (!currentNote) return;
cy.add({
group: 'nodes',
data: {
id: currentNote.id,
label: currentNote.title,
type: 'current'
}
});
// Add connected notes
currentNote.connections.forEach(connId => {
const connectedNote = notes.find(n => n.id === connId);
if (connectedNote) {
cy.add({
group: 'nodes',
data: {
id: connectedNote.id,
label: connectedNote.title,
type: 'connected'
}
});
cy.add({
group: 'edges',
data: {
id: `${currentNote.id}-${connectedNote.id}`,
source: currentNote.id,
target: connectedNote.id,
type: 'strong'
}
});
// Add second-level connections
connectedNote.connections.forEach(secondConnId => {
if (secondConnId !== currentNote.id && !currentNote.connections.includes(secondConnId)) {
const secondConnectedNote = notes.find(n => n.id === secondConnId);
if (secondConnectedNote) {
// Check if node already exists
if (!cy.$id(secondConnectedNote.id).length) {
cy.add({
group: 'nodes',
data: {
id: secondConnectedNote.id,
label: secondConnectedNote.title,
type: 'other'
}
});
}
// Check if edge already exists
if (!cy.$id(`${connectedNote.id}-${secondConnectedNote.id}`).length) {
cy.add({
group: 'edges',
data: {
id: `${connectedNote.id}-${secondConnectedNote.id}`,
source: connectedNote.id,
target: secondConnectedNote.id
}
});
}
}
}
});
}
});
// Apply layout
cy.layout({ name: 'cose-bilkent' }).run();
}
// Visualize full knowledge graph
function visualizeFullGraph() {
// Show visualization view
document.getElementById('editor-view').classList.add('hidden');
document.getElementById('visualization-view').classList.remove('hidden');
// Clear existing elements
cy.elements().remove();
// Add all notes as nodes
notes.forEach(note => {
cy.add({
group: 'nodes',
data: {
id: note.id,
label: note.title,
type: note.id === currentNoteId ? 'current' : 'other'
}
});
});
// Add all connections as edges
notes.forEach(note => {
note.connections.forEach(connId => {
// Only add edge once (avoid duplicates)
if (note.id < connId) {
cy.add({
group: 'edges',
data: {
id: `${note.id}-${connId}`,
source: note.id,
target: connId,
type: note.id === currentNoteId || connId === currentNoteId ? 'strong' : 'normal'
}
});
}
});
});
// Apply layout
cy.layout({ name: 'cose-bilkent' }).run();
}
// Close visualization view
function closeVisualization() {
document.getElementById('editor-view').classList.remove('hidden');
document.getElementById('visualization-view').classList.add('hidden');
}
// Show AI suggestions
function showAISuggestions() {
document.getElementById('ai-panel').classList.remove('hidden');
}
// Close AI suggestions
function closeAISuggestions() {
document.getElementById('ai-panel').classList.add('hidden');
}
// Search notes
function searchNotes(query) {
if (!query) {
renderNotesList();
return;
}
const lowerQuery = query.toLowerCase();
const filteredNotes = notes.filter(note => {
return note.title.toLowerCase().includes(lowerQuery) ||
note.content.toLowerCase().includes(lowerQuery) ||
note.tags.some(tag => tag.toLowerCase().includes(lowerQuery));
});
renderNotesList(filteredNotes);
// Highlight search terms in note cards
document.querySelectorAll('.note-card').forEach(card => {
const title = card.querySelector('h3');
const content = card.querySelector('p');
if (title && content) {
const originalTitle = title.textContent;
const originalContent = content.textContent;
// Remove previous highlights
title.innerHTML = originalTitle;
content.innerHTML = originalContent;
// Add new highlights
if (lowerQuery) {
const regex = new RegExp(lowerQuery, 'gi');
title.innerHTML = originalTitle.replace(regex, match =>
`<span class="search-highlight">${match}</span>`
);
content.innerHTML = originalContent.replace(regex, match =>
`<span class="search-highlight">${match}</span>`
);
}
}
});
}
// Initialize text embedding (simulated for this demo)
async function analyzeTextEmbedding() {
if (!currentNoteId) return;
const note = notes.find(n => n.id === currentNoteId);
if (!note) return;
// In a real app, this would use Universal Sentence Encoder or similar
alert(`Analyzing text embeddings for: ${note.title}\n\nThis would generate vector representations of the text for semantic search and connection suggestions.`);
// Simulate finding similar notes based on content
const similarNotes = findSimilarNotes(note);
if (similarNotes.length > 0) {
const similarList = similarNotes.map(n => `- ${n.title}`).join('\n');
alert(`Potential similar notes based on content:\n${similarList}`);
}
}
// Find similar notes (simulated semantic similarity)
function findSimilarNotes(note) {
// This is a simplified simulation - real app would use vector similarity
const keywords = extractKeywords(note.content);
if (keywords.length === 0) return [];
return notes.filter(n => {
if (n.id === note.id) return false;
const otherKeywords = extractKeywords(n.content);
return keywords.some(kw => otherKeywords.includes(kw));
}).slice(0, 3); // Return top 3
}
// Extract keywords from content (simplified)
function extractKeywords(content) {
// Remove HTML tags
const text = content.replace(/<[^>]*>/g, '');
// Simple keyword extraction - real app would use NLP techniques
const commonWords = ['the', 'and', 'that', 'have', 'for', 'not', 'with', 'you', 'this', 'but'];
const words = text.toLowerCase().split(/\s+/);
return [...new Set(words.filter(word =>
word.length > 3 &&
!commonWords.includes(word) &&
/^[a-z]+$/.test(word)
))].slice(0, 5); // Return top 5 unique keywords
}
// Event listeners
document.getElementById('new-note-btn').addEventListener('click', createNewNote);
document.getElementById('star-note').addEventListener('click', toggleStarNote);
document.getElementById('delete-note').addEventListener('click', deleteCurrentNote);
document.getElementById('visualize-btn').addEventListener('click', visualizeKnowledgeGraph);
document.getElementById('ai-suggest-btn').addEventListener('click', showAISuggestions);
document.getElementById('embedding-btn').addEventListener('click', analyzeTextEmbedding);
document.getElementById('close-visualization').addEventListener('click', closeVisualization);
document.getElementById('close-ai').addEventListener('click', closeAISuggestions);
document.getElementById('search-input').addEventListener('input', (e) => searchNotes(e.target.value));
// Layout buttons
document.getElementById('layout-force').addEventListener('click', () => {
cy.layout({ name: 'cose-bilkent' }).run();
});
document.getElementById('layout-hierarchical').addEventListener('click', () => {
cy.layout({ name: 'dagre' }).run();
});
document.getElementById('layout-concentric').addEventListener('click', () => {
cy.layout({ name: 'concentric' }).run();
});
// Map view buttons
document.querySelectorAll('.map-view-btn').forEach(btn => {
btn.addEventListener('click', () => {
const view = btn.dataset.view;
if (view === 'full') {
visualizeFullGraph();
} else {
visualizeKnowledgeGraph();
}
});
});
// Filter buttons
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', () => {
const filter = btn.dataset.filter;
let filteredNotes = [];
if (filter === 'recent') {
filteredNotes = [...notes].sort((a, b) => b.updatedAt - a.updatedAt);
} else if (filter === 'starred') {
filteredNotes = notes.filter(note => note.starred);
} else if (filter === 'connected') {
filteredNotes = [...notes].sort((a, b) => b.connections.length - a.connections.length);
}
renderNotesList(filteredNotes);
});
});
// Tag input
document.getElementById('new-tag-input').addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
const tag = e.target.value.trim();
if (tag) {
addTagToCurrentNote(tag);
e.target.value = '';
}
}
});
// Auto-save when leaving editor
quill.on('text-change', () => {
if (currentNoteId) {
saveCurrentNote();
}
});
document.getElementById('note-title').addEventListener('blur', saveCurrentNote);
// Initialize the app
extractTags();
renderNotesList();
// Load first note by default for demo
if (notes.length > 0) {
loadNote(notes[0].id);
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=shri210620/ai-notes" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>