-
-
Notifications
You must be signed in to change notification settings - Fork 31
Initial MVP: Local-First Smart Notes with RAG, Summarization, and Electron #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,151 @@ | ||||||||||
| # Smart Notes (Local AI Notes App) | ||||||||||
|
|
||||||||||
| Hi! This project is a small but meaningful attempt to build a local, privacy-first note-taking system with AI features. It is inspired by the Smart Notes idea from AOSSIE for GSoC. | ||||||||||
|
|
||||||||||
| Instead of relying on cloud services, everything here runs locally on your machine. The idea is simple β your notes are yours, and the intelligence around them should also stay with you. | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π± What this project does | ||||||||||
|
|
||||||||||
| This app lets you: | ||||||||||
|
|
||||||||||
| * Write and store notes | ||||||||||
| * Ask questions based on your notes | ||||||||||
| * Get short summaries of your notes | ||||||||||
| * See related notes automatically | ||||||||||
|
|
||||||||||
| All of this is powered by a basic Retrieval-Augmented Generation (RAG) pipeline running locally. | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π§ Features | ||||||||||
|
|
||||||||||
| ### 1. Ask Questions (Q&A) | ||||||||||
|
|
||||||||||
| You can ask questions about your notes, and the system will try to answer using only the content youβve written. | ||||||||||
|
|
||||||||||
| ### 2. Smart Summarization | ||||||||||
|
|
||||||||||
| Notes can be summarized into a short, clean sentence instead of just repeating the same text. | ||||||||||
|
|
||||||||||
| ### 3. Related Notes | ||||||||||
|
|
||||||||||
| When you add a new note, the system suggests other notes that are similar in meaning. | ||||||||||
|
|
||||||||||
| ### 4. Local First | ||||||||||
|
|
||||||||||
| Everything runs on your system β no mandatory APIs, no cloud dependency. | ||||||||||
|
|
||||||||||
| ### 5. Desktop App | ||||||||||
|
|
||||||||||
| The app is wrapped using Electron so it can run like a desktop application. | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## βοΈ Tech Stack | ||||||||||
|
|
||||||||||
| * Python (Flask) | ||||||||||
| * HTML + CSS (simple UI) | ||||||||||
| * HuggingFace Transformers | ||||||||||
| * Sentence Transformers (for embeddings) | ||||||||||
| * Electron (for desktop wrapper) | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## ποΈ How it works (simple view) | ||||||||||
|
|
||||||||||
| 1. Notes are stored locally | ||||||||||
| 2. Each note is converted into embeddings | ||||||||||
| 3. When you ask a question: | ||||||||||
|
|
||||||||||
| * relevant notes are retrieved | ||||||||||
| * a model generates an answer from that context | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π How to run the project | ||||||||||
|
|
||||||||||
| ### 1. Clone the repo | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| git clone https://github.com/KRISHNPRIY2820/smart-notes-mvp.git | ||||||||||
| cd smart-notes | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### 2. Create virtual environment | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| python -m venv venv | ||||||||||
| venv\Scripts\activate | ||||||||||
| ``` | ||||||||||
|
Comment on lines
+78
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Windows-specific activation command.
π§ Proposed cross-platform instructions ```bash
python -m venv venv
-venv\Scripts\activate
+# Windows:
+venv\Scripts\activate
+# macOS/Linux:
+source venv/bin/activateπ Committable suggestion
Suggested change
π§° Toolsπͺ markdownlint-cli2 (0.22.0)[warning] 78-78: Fenced code blocks should have a language specified (MD040, fenced-code-language) π€ Prompt for AI Agents |
||||||||||
|
|
||||||||||
| ### 3. Install requirements | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| pip install -r requirements.txt | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| ### 4. Run the backend | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| python app.py | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| Open in browser: | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| http://127.0.0.1:5000 | ||||||||||
| ``` | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π» Run as Desktop App (Electron) | ||||||||||
|
|
||||||||||
| ``` | ||||||||||
| cd frontend | ||||||||||
| npm install | ||||||||||
| npm start | ||||||||||
| ``` | ||||||||||
|
Comment on lines
+71
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§Ή Nitpick | π΅ Trivial Add language specifiers to fenced code blocks. Per markdownlint (MD040), fenced code blocks should have a language specified for syntax highlighting and accessibility. Add π§° Toolsπͺ markdownlint-cli2 (0.22.0)[warning] 71-71: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 78-78: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 85-85: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 91-91: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 97-97: Fenced code blocks should have a language specified (MD040, fenced-code-language) [warning] 105-105: Fenced code blocks should have a language specified (MD040, fenced-code-language) π€ Prompt for AI Agents |
||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π§ͺ Current limitations | ||||||||||
|
|
||||||||||
| * UI is basic (focused more on functionality) | ||||||||||
| * Models are simple and not heavily optimized | ||||||||||
| * No persistent vector database yet | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π Why this project | ||||||||||
|
|
||||||||||
| Most note-taking tools today depend heavily on the cloud. This project explores a different direction β keeping everything local while still providing AI assistance. | ||||||||||
| >This MVP focuses on building a strong local-first foundation, with scalability planned in future iterations. | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π§ Future Improvements | ||||||||||
|
|
||||||||||
| - Multi-page UI instead of a single screen for better feature separation | ||||||||||
| - Persistent storage so notes are saved locally and do not vanish after restarting the app | ||||||||||
| - Knowledge graph visualization to explore connections between notes | ||||||||||
| - Improved retrieval using hybrid search (keyword + semantic) | ||||||||||
| - Enhanced UI using React + Electron | ||||||||||
| - Optional multi-device sync while maintaining privacy | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π€ Contribution | ||||||||||
|
|
||||||||||
| This is part of my GSoC preparation with AOSSIE. Any feedback or suggestions are genuinely welcome. | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| ## π License | ||||||||||
|
|
||||||||||
| GNU GPL v3 | ||||||||||
|
|
||||||||||
| --- | ||||||||||
|
|
||||||||||
| Thanks for checking this out π | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,55 @@ | ||||||||||
| from flask import Flask, render_template, request | ||||||||||
| from logic.notes import add_note, get_notes | ||||||||||
| from logic.qa import answer | ||||||||||
| from logic.linking import suggest | ||||||||||
| from logic.context import update_embeddings | ||||||||||
| from logic.summarizer import summarize_notes | ||||||||||
|
|
||||||||||
| app = Flask(__name__) | ||||||||||
|
|
||||||||||
| @app.route("/", methods=["GET", "POST"]) | ||||||||||
| def index(): | ||||||||||
| answer_text = "" | ||||||||||
| links = [] | ||||||||||
| summary_text = "" | ||||||||||
|
|
||||||||||
| # Always get latest notes | ||||||||||
| notes = get_notes() | ||||||||||
|
|
||||||||||
| # Ensure embeddings are synced | ||||||||||
| update_embeddings(notes) | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove redundant embedding recomputation on request entry. Line 20 recomputes embeddings for every request, and the add-note flow recomputes again via Proposed fix- # Ensure embeddings are synced
- update_embeddings(notes)
@@
elif "question" in request.form and request.form["question"].strip():
question = request.form["question"].strip()
+ update_embeddings(notes)
answer_text = answer(question, notes)Also applies to: 28-31 π€ Prompt for AI Agents |
||||||||||
|
|
||||||||||
|
Comment on lines
+19
to
+21
|
||||||||||
| # Ensure embeddings are synced | |
| update_embeddings(notes) |
Copilot
AI
Mar 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
app.run(debug=True) enables the Flask debugger and interactive console, which should not be committed as the default for anything beyond local development. Please default to debug=False (or read from an environment variable) and document how to enable debug mode locally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
#!/bin/bash
# Verify whether debug mode is hardcoded in Flask startup paths.
rg -nP --type=py -C2 'app\.run\([^)]*debug\s*=\s*True'Repository: AOSSIE-Org/SmartNotes
Length of output: 146
Remove hardcoded debug=True from Flask startup.
Line 55 unconditionally enables debug mode, which exposes sensitive data if used outside local development.
Proposed fix
+import os
@@
if __name__ == "__main__":
- app.run(debug=True)
+ app.run(debug=os.getenv("FLASK_DEBUG") == "1")π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if __name__ == "__main__": | |
| app.run(debug=True) | |
| if __name__ == "__main__": | |
| app.run(debug=os.getenv("FLASK_DEBUG") == "1") |
π§° Tools
πͺ ast-grep (0.41.1)
[warning] 54-54: Detected Flask app with debug=True. Do not deploy to production with this flag enabled as it will leak sensitive information. Instead, consider using Flask configuration variables or setting 'debug' using system environment variables.
Context: app.run(debug=True)
Note: [CWE-489] Active Debug Code. [REFERENCES]
- https://labs.detectify.com/2015/10/02/how-patreon-got-hacked-publicly-exposed-werkzeug-debugger/
(debug-enabled-python)
πͺ Ruff (0.15.7)
[error] 55-55: Use of debug=True in Flask app detected
(S201)
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app.py` around lines 54 - 55, The startup currently forces Flask into debug
mode via the hardcoded debug=True in the "__main__" block (app.run), which must
be removed; change the app.run call to read the debug flag from configuration or
environment (e.g. app.config['DEBUG'] or an env var like FLASK_DEBUG) and pass
that value to app.run (or omit the debug argument entirely and ensure config
sets DEBUG), so debug is enabled only when explicitly configured for development
rather than always.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,12 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { app, BrowserWindow } = require('electron'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function createWindow() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const win = new BrowserWindow({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| width: 1200, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| height: 800, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| win.loadURL('http://127.0.0.1:5000'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2
to
+9
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function createWindow() { | |
| const win = new BrowserWindow({ | |
| width: 1200, | |
| height: 800, | |
| }); | |
| win.loadURL('http://127.0.0.1:5000'); | |
| const { shell } = require('electron'); | |
| function createWindow() { | |
| const win = new BrowserWindow({ | |
| width: 1200, | |
| height: 800, | |
| webPreferences: { | |
| contextIsolation: true, | |
| nodeIntegration: false, | |
| sandbox: true, | |
| }, | |
| }); | |
| const appUrl = 'http://127.0.0.1:5000'; | |
| win.webContents.on('will-navigate', (event, url) => { | |
| if (!url.startsWith(appUrl)) { | |
| event.preventDefault(); | |
| shell.openExternal(url); | |
| } | |
| }); | |
| win.webContents.setWindowOpenHandler(({ url }) => { | |
| if (url.startsWith(appUrl)) { | |
| return { action: 'allow' }; | |
| } | |
| shell.openExternal(url); | |
| return { action: 'deny' }; | |
| }); | |
| win.loadURL(appUrl); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No error handling if Flask backend is unavailable.
win.loadURL() will show an error page if the Flask server isn't running. Consider adding error handling to provide a better user experience or retry logic.
π§ Proposed error handling
- win.loadURL('http://127.0.0.1:5000');
+ win.loadURL('http://127.0.0.1:5000').catch((err) => {
+ console.error('Failed to load backend:', err);
+ // Optionally show a helpful error page or retry
+ });π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| win.loadURL('http://127.0.0.1:5000'); | |
| win.loadURL('http://127.0.0.1:5000').catch((err) => { | |
| console.error('Failed to load backend:', err); | |
| // Optionally show a helpful error page or retry | |
| }); |
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/main.js` at line 9, The call to win.loadURL('http://127.0.0.1:5000')
has no handling for a down Flask backend; update the startup to detect load
failures (use win.webContents.on('did-fail-load', ...) and/or the Promise
returned by win.loadURL) and implement fallback/retry behavior: attempt a few
retries with backoff to the same URL, and if still failing load a local error
page (via win.loadFile or serve an inline HTML) that explains the backend is
unreachable and provides a retry button; reference the BrowserWindow instance
variable win, its webContents event 'did-fail-load', and the win.loadURL call
when implementing these changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π οΈ Refactor suggestion | π Major
Add explicit security settings for BrowserWindow.
The BrowserWindow lacks explicit webPreferences security settings. While recent Electron defaults are secure (nodeIntegration: false), explicitly setting security options is a best practice and makes the security posture clear.
π Proposed security hardening
function createWindow() {
const win = new BrowserWindow({
width: 1200,
height: 800,
+ webPreferences: {
+ nodeIntegration: false,
+ contextIsolation: true,
+ },
});
win.loadURL('http://127.0.0.1:5000');
}π Committable suggestion
βΌοΈ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function createWindow() { | |
| const win = new BrowserWindow({ | |
| width: 1200, | |
| height: 800, | |
| }); | |
| win.loadURL('http://127.0.0.1:5000'); | |
| } | |
| function createWindow() { | |
| const win = new BrowserWindow({ | |
| width: 1200, | |
| height: 800, | |
| webPreferences: { | |
| nodeIntegration: false, | |
| contextIsolation: true, | |
| }, | |
| }); | |
| win.loadURL('http://127.0.0.1:5000'); | |
| } |
π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/main.js` around lines 3 - 10, The BrowserWindow created in
createWindow is missing explicit security webPreferences; update the
BrowserWindow options in createWindow to include secure settings such as
webPreferences: { nodeIntegration: false, contextIsolation: true,
enableRemoteModule: false, sandbox: true, nativeWindowOpen: false, webSecurity:
true, allowRunningInsecureContent: false } and ensure any devtools enablement is
explicit (only enable when needed) before calling
win.loadURL('http://127.0.0.1:5000').
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§Ή Nitpick | π΅ Trivial
Missing macOS app lifecycle handling.
On macOS, apps typically stay active even when all windows are closed. The standard Electron pattern handles window-all-closed and activate events for cross-platform behavior.
π Proposed lifecycle handling
app.whenReady().then(createWindow);
+
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit();
+ }
+});
+
+app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow();
+ }
+});π€ Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/main.js` at line 12, The app currently only calls
app.whenReady().then(createWindow) and lacks macOS lifecycle handling; add
standard Electron event handlers for 'window-all-closed' (call app.quit() on
non-darwin platforms) and for 'activate' (recreate the window by calling
createWindow if no BrowserWindow exists) so the createWindow function is used on
activate and the app stays running on macOS when all windows are closed;
reference the existing createWindow and the app.whenReady().then(createWindow)
call to insert the 'window-all-closed' and 'activate' listeners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directory name mismatch in clone instructions.
The clone URL is
smart-notes-mvp.gitbut thecdcommand usessmart-notes. This will fail for users following the instructions.π§ Proposed fix
π Committable suggestion
π§° Tools
πͺ markdownlint-cli2 (0.22.0)
[warning] 71-71: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
π€ Prompt for AI Agents