First Commit

This commit is contained in:
Honey committed 2026-02-17 19:25:52 +01:00
1 parent fa15d48a52
commit 4709096ab3
8 files changed
+2208

No files matched your search

+212
View File
@@ -0,0 +1,212 @@
# ✏️ InkNote
A cross-platform note-taking application with rich text editing, freehand drawing, PDF annotation, and encrypted sync — inspired by Samsung Notes.
---
## Features
### Core
- **Rich Text Notes** — Bold, italic, underline, headings, bullet/numbered lists, checkboxes
- **Freehand Drawing** — Pen, highlighter, eraser with adjustable thickness and color palette (20 colors, 7 pen sizes)
- **Undo/Redo** — Full history stack for drawing operations
- **PDF Import & Annotation** — Import PDFs, overlay drawing/highlighting, persistent annotations
- **Notebooks & Folders** — Create notebooks with custom gradient covers, organize notes by folder
- **Star/Favorite** — Quick access to important notes
- **Dark & Light Mode** — Full theme support with refined color schemes
### Sync & Storage
- **Offline-First** — Works fully offline, syncs when connection returns
- **Conflict-Aware Sync** — Vector clock–based conflict detection; notifies user of merge conflicts
- **WebSocket Real-Time Push** — Changes appear on other devices instantly
- **Encrypted at Rest** — AES-256-CBC encryption for all note content on the server
- **JWT Authentication** — Secure user accounts with bcrypt password hashing
### Architecture
- **Self-Hosted Backend** — Node.js + Express + SQLite (or PostgreSQL)
- **Docker Ready** — docker-compose up and you're running
- **Client Sync Engine** — Portable JS library for any frontend (React Native, Flutter JS bridge, Electron, web)
---
## Project Structure
```
inknote/
├── note-app.jsx # Full React UI (artifact / web)
├── docker-compose.yml # Container orchestration
├── backend/
│ ├── server.js # Express API + WebSocket server
│ ├── sync-client.js # Client-side sync engine library
│ ├── package.json # Node dependencies
│ ├── Dockerfile # Server container
│ └── .env.example # Environment variable template
└── README.md # This file
```
---
## Quick Start
### 1. Run the UI (Web / Artifact)
The `note-app.jsx` file is a self-contained React component. It runs directly as a Claude artifact, or you can embed it in any React project:
```jsx
import NoteApp from './note-app';
function App() {
return <NoteApp />;
}
```
### 2. Run the Backend
```bash
cd backend
cp .env.example .env # Edit secrets!
npm install
npm start # → http://localhost:4000
```
### 3. Docker Deployment
```bash
# Generate a secure encryption key
export ENCRYPTION_KEY=$(openssl rand -hex 32)
export JWT_SECRET=$(openssl rand -base64 32)
docker-compose up -d
```
The server runs on port `4000`, the web UI on port `3000`.
---
## API Reference
### Authentication
| Method | Endpoint | Body | Response |
|--------|-----------------------|-------------------------------|---------------------|
| POST | `/api/auth/register` | `{email, password, displayName}` | `{token, user}` |
| POST | `/api/auth/login` | `{email, password}` | `{token, user}` |
### Notebooks
| Method | Endpoint | Description |
|--------|-----------------------|--------------------------|
| GET | `/api/notebooks` | List all notebooks |
| POST | `/api/notebooks` | Create notebook |
| PUT | `/api/notebooks/:id` | Update notebook |
| DELETE | `/api/notebooks/:id` | Soft-delete notebook |
### Notes
| Method | Endpoint | Description |
|--------|---------------------------|-------------------------------|
| GET | `/api/notes` | List all notes (metadata) |
| GET | `/api/notes/:id` | Get note with decrypted content |
| POST | `/api/notes` | Create note |
| PUT | `/api/notes/:id` | Update note (conflict-aware) |
| DELETE | `/api/notes/:id` | Soft-delete note |
| PUT | `/api/notes/:id/drawing` | Save drawing data (base64) |
| POST | `/api/notes/pdf` | Upload PDF (multipart/form) |
### Sync
| Method | Endpoint | Description |
|--------|------------- |------------------------------------------|
| POST | `/api/sync` | Bidirectional sync with conflict reports |
### WebSocket
Connect to `ws://localhost:4000/ws`, then send:
```json
{"type": "auth", "token": "JWT_TOKEN", "deviceId": "device-123"}
```
Receive real-time events: `note_created`, `note_updated`, `note_deleted`, `notebook_created`, etc.
---
## Sync Engine (Client)
```javascript
import { SyncEngine } from './sync-client';
const sync = new SyncEngine('https://your-server.com', 'device-abc');
// Authenticate
await sync.authenticate('user@example.com', 'password');
// Listen for events
sync.on('status', (status) => console.log('Sync:', status));
sync.on('conflict', (conflict) => {
// Present both versions to user, let them choose
console.log('Conflict:', conflict);
});
sync.on('remote_change', (change) => {
// Update local state with remote changes
console.log('Remote change:', change);
});
// Start syncing (every 30s + WebSocket push)
sync.start();
// CRUD operations auto-queue for sync
await sync.createNote({ type: 'text', title: 'Hello', content: '<p>World</p>' });
await sync.updateNote('note-id', { title: 'Updated' });
await sync.deleteNote('note-id');
await sync.uploadPDF(fileBlob);
await sync.saveDrawing('note-id', canvasDataUrl);
```
---
## Conflict Resolution
InkNote uses **vector clocks** to detect conflicts:
1. Each device maintains a logical clock counter
2. On save, the device increments its counter and includes the full vector clock
3. The server compares clocks — if the client clock is strictly newer, the update applies
4. If clocks are concurrent (both have changes the other doesn't), a **409 Conflict** is returned
5. The client receives both versions and can present a merge UI to the user
---
## Extending to Native Platforms
### React Native
Use `note-app.jsx` components with React Native adaptations (replace HTML canvas with `react-native-canvas` or `@shopify/react-native-skia` for drawing). The sync engine works as-is.
### Flutter
Use the sync engine via a JavaScript bridge, or port the API calls to Dart. For drawing, use Flutter's `CustomPainter`. For PDF, use `flutter_pdfview`.
### Electron
Wrap the React app directly in Electron for desktop. Add native file system access for PDF import/export.
---
## Security Notes
- All note content is encrypted with AES-256-CBC before database storage
- Passwords are hashed with bcrypt (12 rounds)
- JWT tokens expire after 30 days
- HTTPS should be enforced in production (use a reverse proxy like Nginx or Pangolin)
- The encryption key should be stored securely (e.g., environment variable, secrets manager)
---
## Tech Stack
| Layer | Technology |
|-----------|-------------------------------------|
| Frontend | React (web), adaptable to RN/Flutter |
| Backend | Node.js, Express |
| Database | SQLite (dev), PostgreSQL (prod) |
| Real-time | WebSocket (ws library) |
| Auth | JWT + bcrypt |
| Encryption| AES-256-CBC |
| Container | Docker + docker-compose |