Files
InkNote/readme.md
T
2026-02-17 19:25:52 +01:00

212 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ✏️ 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 |