diff --git a/.gitignore b/.gitignore index 9308a4b..b1aa25c 100644 --- a/.gitignore +++ b/.gitignore @@ -324,3 +324,6 @@ TSWLatexianTemp* # option is specified. Footnotes are the stored in a file with suffix Notes.bib. # Uncomment the next line to have this generated file ignored. #*Notes.bib + +/notes/ +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index 3c5adf2..d306990 100644 --- a/README.md +++ b/README.md @@ -48,133 +48,115 @@ ---
-

TODO: Project Name

+

Smart Notes

-[TODO](https://TODO.stability.nexus/) is a ... TODO: Project Description. +[Smart Notes](https://github.com/tani-dubey/Smart-Notes) is a local-first, privacy-focused desktop application for personal knowledge management. +It provides a markdown-based note editor with safe file persistence, draft handling, and dynamic titles, forming a solid foundation for future AI-powered semantic search and RAG features. + +The project prioritizes offline usage, user data ownership, and incremental feature development. --- ## 🚀 Features -TODO: List your main features here: - -- **Feature 1**: Description -- **Feature 2**: Description -- **Feature 3**: Description -- **Feature 4**: Description +Core Editor (Current Prototype) + +- **📝 Markdown note editing**: raw text, no rendering yet +- **💾 Explicit Save / Saved state** with dirty-tracking +- **📄 Draft-based note creation**: + - Auto-creates drafts on first typing + - Finalizes drafts on save +- **🏷️ Dynamic note titles**: + - Filename derived from first line of content + - Empty notes handled as `untitled.md` +- **🔁 Safe file switching**: + - Open, edit, and update multiple notes + - No accidental overwrites or data loss +- **📂 Local filesystem storage**: + - Notes stored as `.md` files on disk + - Fully offline by default + + ⚠️ AI features (semantic search, embeddings, RAG) are planned and intentionally not included yet. --- ## 💻 Tech Stack -TODO: Update based on your project - -### Frontend -- React / Next.js / Flutter / React Native -- TypeScript -- TailwindCSS - -### Backend -- Flask / FastAPI / Node.js / Supabase -- Database: PostgreSQL / SQLite / MongoDB +### Desktop Application +- **Electron** — cross-platform desktop shell +- **HTML / CSS / JavaScript** — UI and editor logic +- **Node.js (fs, path)** — local filesystem access -### AI/ML (if applicable) -- LangChain / LangGraph / LlamaIndex -- Google Gemini / OpenAI / Anthropic Claude -- Vector Database: Weaviate / Pinecone / Chroma -- RAG / Prompt Engineering / Agent Frameworks +### Storage +- Local markdown files `(.md)` +- No database, no cloud dependency -### Blockchain (if applicable) -- Solidity / solana / cardano / ergo Smart Contracts -- Hardhat / Truffle / foundry -- Web3.js / Ethers.js / Wagmi -- OpenZeppelin / alchemy / Infura +### AI/ML (Planned) +- Ollama (local LLMs) +- Embeddings + vector store (local) +- Retrieval-Augmented Generation (RAG) --- ## ✅ Project Checklist -TODO: Complete applicable items based on your project type - -- [ ] **The protocol** (if applicable): - - [ ] has been described and formally specified in a paper. - - [ ] has had its main properties mathematically proven. - - [ ] has been formally verified. -- [ ] **The smart contracts** (if applicable): - - [ ] were thoroughly reviewed by at least two knights of The Stable Order. - - [ ] were deployed to: [Add deployment details] -- [ ] **The mobile app** (if applicable): - - [ ] has an _About_ page containing the Stability Nexus's logo and pointing to the social media accounts of the Stability Nexus. - - [ ] is available for download as a release in this repo. - - [ ] is available in the relevant app stores. -- [ ] **The AI/ML components** (if applicable): - - [ ] LLM/model selection and configuration are documented. - - [ ] Prompts and system instructions are version-controlled. - - [ ] Content safety and moderation mechanisms are implemented. - - [ ] API keys and rate limits are properly managed. - ---- - -## 🔗 Repository Links +- ### Editor + - [x] Local-first design + - [x] Draft handling + - [x] Safe save/load + - [x] Dynamic file naming + - [ ] Markdown rendering (planned) + - [ ] Search (planned) -TODO: Update with your repository structure - -1. [Main Repository](https://github.com/AOSSIE-Org/TODO) -2. [Frontend](https://github.com/AOSSIE-Org/TODO/tree/main/frontend) (if separate) -3. [Backend](https://github.com/AOSSIE-Org/TODO/tree/main/backend) (if separate) +- ### AI / ML (Planned) + - [ ] Local embedding pipeline + - [ ] Hybrid search (keyword + vector) + - [ ] On-device RAG + - [ ] Model backend selection --- ## 🏗️ Architecture Diagram -TODO: Add your system architecture diagram here - ``` -[Architecture Diagram Placeholder] -``` - -You can create architecture diagrams using: -- [Draw.io](https://draw.io) -- [Excalidraw](https://excalidraw.com) -- [Lucidchart](https://lucidchart.com) -- [Mermaid](https://mermaid.js.org) (for code-based diagrams) +smart-notes/ +├─ app/ +│ ├─ index.html +│ ├─ style.css +│ └─ main.js +| +├─ notes/ (optional to have) +│ └─ (your .md files go here) +| +├─ main.js (Entrypoint) +├─ package.json +└─ README.md -Example structure to include: -- Frontend components -- Backend services -- Database architecture -- External APIs/services -- Data flow between components +``` +### Planned Extension +- Python backend for embeddings & RAG +- Local IPC between Electron and backend --- ## 🔄 User Flow - -TODO: Add user flow diagrams showing how users interact with your application - -``` -[User Flow Diagram Placeholder] -``` +image ### Key User Journeys -TODO: Document main user flows: - -1. **User Journey 1**: Description - - Step 1 - - Step 2 - - Step 3 - -2. **User Journey 2**: Description - - Step 1 - - Step 2 - - Step 3 +### User Journey: Create & Save a Note +1. User opens the app +2. Starts typing → draft is auto-created +3. First line becomes the title +4. Clicks Save +5. Draft is finalized and stored as a markdown file -3. **User Journey 3**: Description - - Step 1 - - Step 2 - - Step 3 +### User Journey: Edit Existing Note +1. User clicks a note in the sidebar +2. Content loads into editor +3. User edits content +4. Save updates the file safely --- @@ -182,11 +164,8 @@ TODO: Document main user flows: ### Prerequisites -TODO: List what developers need installed - -- Node.js 18+ / Python 3.9+ / Flutter SDK -- npm / yarn / pnpm -- [Any specific tools or accounts needed] +- Node.js 18+ +- npm ### Installation @@ -195,46 +174,22 @@ TODO: Provide detailed setup instructions #### 1. Clone the Repository ```bash -git clone https://github.com/AOSSIE-Org/TODO.git -cd TODO +git clone https://github.com/AOSSIE-Org/SmartNotes.git +cd SmartNotes ``` #### 2. Install Dependencies ```bash npm install -# or -yarn install -# or -pnpm install ``` -#### 3. Configure Environment Variables(.env.example) - -Create a `.env` file in the root directory: - -```env -# Add your environment variables here -API_KEY=your_api_key -DATABASE_URL=your_database_url -``` - -#### 4. Run the Development Server +#### 3. Run the app ```bash -npm run dev -# or -yarn dev -# or -pnpm dev +npm start ``` -#### 5. Open your Browser - -Navigate to [http://localhost:3000](http://localhost:3000) to see the application. - -For detailed setup instructions, please refer to our [Installation Guide](./docs/INSTALL_GUIDE.md) (if you have one). - --- ## 📱 App Screenshots @@ -257,8 +212,6 @@ Thank you for considering contributing to this project! Contributions are highly ## ✨ Maintainers -TODO: Add maintainer information - - [Maintainer Name](https://github.com/username) - [Maintainer Name](https://github.com/username) @@ -273,8 +226,8 @@ See the [LICENSE](LICENSE) file for details. ## 💪 Thanks To All Contributors -Thanks a lot for spending your time helping TODO grow. Keep rocking 🥂 +Thanks a lot for spending your time helping Smart-Notes grow. Keep rocking 🥂 -[![Contributors](https://contrib.rocks/image?repo=AOSSIE-Org/TODO)](https://github.com/AOSSIE-Org/TODO/graphs/contributors) +[![Contributors](https://contrib.rocks/image?repo=AOSSIE-Org/Smart-Notes)](https://github.com/AOSSIE-Org/Smart-Notes/graphs/contributors) -© 2025 AOSSIE +© 2025 AOSSIE \ No newline at end of file diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..4f09e52 --- /dev/null +++ b/app/index.html @@ -0,0 +1,28 @@ + + + + + + + Smart Notes MVP + + + + +
+ + +
+
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/app/main.js b/app/main.js new file mode 100644 index 0000000..6c681a4 --- /dev/null +++ b/app/main.js @@ -0,0 +1,238 @@ +const fs = require("fs"); +const path = require("path"); + +const notesDir = path.join(__dirname, "..", "notes"); + +const editor = document.getElementById("editor"); +const notesList = document.getElementById("notes-list"); +const newNoteBtn = document.getElementById("new-note"); +const saveBtn = document.getElementById("save-note"); + +let currentNote = null; +let isDraft = false; +let isDirty = false; + +// Ensure notes directory exists +try{ + if (!fs.existsSync(notesDir)) { + fs.mkdirSync(notesDir); +} +} catch (err) { + console.error("Failed to create notes directory:", err); + alert("Failed to initialize notes directory."); +} + +// -------------------- UI -------------------- + +function updateSaveButton() { + if (isDirty) { + saveBtn.innerText = "💾 Save"; + saveBtn.classList.add("unsaved"); + } else { + saveBtn.innerText = "✅ Saved"; + saveBtn.classList.remove("unsaved"); + } +} +function showError(message, err) { + console.error(message, err); + alert(message); +} + +// helpers +function isEditorEmpty() { + return editor.value.trim().length === 0; +} + +function getNextUntitled(prefix) { + let count = 1; + let name; + + do { + name = `${prefix}-${count}.md`; + count++; + } while (fs.existsSync(path.join(notesDir, name))); + + return name; +} + +function titleToFilename(title) { + return ( + title + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, "") + .trim() + .replace(/\s+/g, "-") + .slice(0, 50) || "untitled" + ); +} + +function getTitleFromEditor() { + return editor.value.split("\n")[0].trim(); +} + +// AUTO SAVE +function autoSaveIfDirty() { + if (!currentNote || !isDirty) return; + + try { + fs.writeFileSync( + path.join(notesDir, currentNote), + editor.value + ); + isDirty = false; + updateSaveButton(); + } catch (err) { + showError("Failed to auto-save note.", err); + } +} + +// EDITOR TRACKING +editor.addEventListener("input", () => { + if (!currentNote) return; + isDirty = true; + updateSaveButton(); +}); + +// LOAD NOTES LIST +function loadNotes() { + notesList.innerHTML = ""; + + let files = []; + + try { + files = fs + .readdirSync(notesDir) + .filter(f => f.endsWith(".md")) + .sort(); + } catch (err) { + showError("Failed to load notes list.", err); + return; + } + + files.forEach(file => { + const li = document.createElement("li"); + li.textContent = file; + + li.addEventListener("click", () => { + autoSaveIfDirty(); + loadNote(file); + }); + + notesList.appendChild(li); + }); +} + +// LOAD NOTE +function loadNote(filename) { + filename = path.basename(filename); + + const filePath = path.join(notesDir, filename); + + try { + const content = fs.readFileSync(filePath, "utf-8"); + editor.value = content; + } catch (err) { + showError("Failed to load note.", err); + return; + } + + currentNote = filename; + isDraft = filename.startsWith("draft-"); + isDirty = false; + + updateSaveButton(); + + editor.readOnly = false; + editor.disabled = false; + editor.focus(); +} + +// NEW NOTE +newNoteBtn.onclick = () => { + autoSaveIfDirty(); + + const name = getNextUntitled("draft-untitled"); + const filePath = path.join(notesDir, name); + + try { + fs.writeFileSync(filePath, ""); + } catch (err) { + showError("Failed to create new note.", err); + return; + } + + currentNote = name; + isDraft = true; + + editor.value = ""; + editor.readOnly = false; + editor.disabled = false; + editor.focus(); + + isDirty = false; + updateSaveButton(); + loadNotes(); +}; + +// -------------------- SAVE BUTTON -------------------- + +saveBtn.onclick = () => { + if (!currentNote) return; + + let filePath = path.join(notesDir, currentNote); + let renamed = false; + + if (currentNote.startsWith("draft-")) { + let finalName; + + if (isEditorEmpty()) { + finalName = getNextUntitled("untitled"); + } else { + const title = getTitleFromEditor(); + const safeName = titleToFilename(title); + + let candidate = `${safeName}.md`; + let n = 1; + + while ( + fs.existsSync(path.join(notesDir, candidate)) && + candidate !== currentNote + ) { + candidate = `${safeName}-${n++}.md`; + } + + finalName = candidate; + } + + const finalPath = path.join(notesDir, finalName); + + try { + fs.renameSync(filePath, finalPath); + currentNote = finalName; + filePath = finalPath; + isDraft = false; + renamed = true; + } catch (err) { + showError("Failed to rename draft note.", err); + return; + } + } + + try { + fs.writeFileSync(filePath, editor.value); + } catch (err) { + showError("Failed to save note.", err); + return; + } + + isDirty = false; + updateSaveButton(); + editor.focus(); + + if (renamed) loadNotes(); +}; + +// -------------------- INIT -------------------- + +loadNotes(); +updateSaveButton(); \ No newline at end of file diff --git a/app/style.css b/app/style.css new file mode 100644 index 0000000..3a549d7 --- /dev/null +++ b/app/style.css @@ -0,0 +1,61 @@ +body { + margin: 0; + font-family: sans-serif; +} + +#container { + display: flex; + height: 100vh; +} + +#sidebar { + width: 200px; + padding: 10px; + box-sizing: border-box; +} + +#main-area { + display: flex; + flex-direction: column; + flex: 1; +} + +#editor { + padding: 10px; + font-family: monospace; + font-size: 14px; + flex: 1; + border: none; + outline: none; + resize: none; +} + +#save-note { + padding: 6px 10px; + margin-bottom: 8px; + border: 1px solid #ccc; + background-color: #f5f5f5; + cursor: pointer; + font-size: 13px; + border-radius: 4px; +} + +#save-note:hover { + background-color: #eaeaea; +} + +#editor-toolbar { + border-bottom: 1px solid #ddd; + padding: 6px; + background-color: #fafafa; +} + +#save-note.unsaved { + background-color: #ffeeba; + border-color: #f0ad4e; +} + +button { + width: 100%; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..f51af19 --- /dev/null +++ b/main.js @@ -0,0 +1,35 @@ +const { app, BrowserWindow } = require("electron"); +const path = require("path"); + +let mainWindow; + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 900, + height: 600, + webPreferences: { + nodeIntegration: true, + contextIsolation: false + } + }); + + mainWindow.loadFile(path.join(__dirname, "app/index.html")); +} + +app.whenReady().then(() => { + createWindow(); + + app.on("activate", () => { + // On macOS, recreate window when dock icon clicked + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +// Quit app when all windows closed (except macOS) +app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } +}); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9e0253b --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "smart-notes", + "version": "0.1.0", + "main": "main.js", + "scripts": { + "start": "electron ." + }, + "devDependencies": { + "electron": "^40.0.0" + } +} \ No newline at end of file