171 lines
6.5 KiB
Markdown
171 lines
6.5 KiB
Markdown
# Obsidian reMarkable Sync Plugin - Implementation Summary
|
|
|
|
## Status: ✅ Phase 1 Complete (Reviewed & Fixed)
|
|
|
|
The plugin has been **reviewed, fixed, and rebuilt**. All critical and major issues from the code review have been resolved.
|
|
|
|
---
|
|
|
|
## 🔧 Fixes Applied (Post-Review)
|
|
|
|
### Critical Bugs (all fixed)
|
|
| # | Issue | File | Fix |
|
|
|---|---|---|---|
|
|
| 1 | `SyncTracker` corrupted plugin settings | `src/sync/tracker.ts` | Now merges with existing data before saving |
|
|
| 2 | `runCommand()` hung on spawn errors | `src/utils/process.ts` | Added `error` event handler + dedup resolution |
|
|
| 3 | Invalid `"dir"` in manifest | `manifest.json` | Removed property |
|
|
| 4 | Wrong vault path handling | `src/sync/downloader.ts` | Uses `vault.adapter` paths directly |
|
|
|
|
### Major Issues (all fixed)
|
|
| # | Issue | File | Fix |
|
|
|---|---|---|---|
|
|
| 5 | `ls` flag order (Go parser) | `src/rmapi/bridge.ts` | `--json` before positional path |
|
|
| 6 | No incremental sync | `src/sync/downloader.ts` | Checks `modifiedClient` vs `lastSynced` |
|
|
| 7 | Unsanitized filenames | `src/sync/downloader.ts` | `sanitizeFileName()` replaces illegal chars |
|
|
| 8 | Broken `authenticate()` | `src/rmapi/bridge.ts` | Replaced with `isAuthenticated()` + user message |
|
|
| 9 | Only root-level sync | `src/sync/downloader.ts` | `listAllDocuments()` recursively traverses folders |
|
|
| 10 | Noisy load/unload notices | `src/main.ts` | Removed; status bar shows state instead |
|
|
| 11 | Dev watch script broken | `esbuild.config.mjs` | Uses `esbuild.context().watch()` |
|
|
| 12 | Dead `killProcess()` code | `src/utils/process.ts` | Removed |
|
|
| 13 | `mkdir` silent failures | `src/sync/downloader.ts` | Wrapped in try/catch with warning log |
|
|
|
|
### Minor Issues (all fixed)
|
|
| # | Issue | File | Fix |
|
|
|---|---|---|---|
|
|
| 14 | `pageRenderer` `as any` cast | `src/settings.ts` | Proper union type cast |
|
|
| 15 | `syncInterval` text input | `src/settings.ts` | `type="number"` with `min="0"` + validation |
|
|
| 16 | Static status bar | `src/main.ts` | Dynamic: "Idle" / "Syncing..." / "Last sync: HH:MM:SS" |
|
|
|
|
---
|
|
|
|
## 📁 Project Structure
|
|
|
|
```
|
|
obidian-remarkable/
|
|
├── dist/
|
|
│ └── main.js # Built plugin (minified, ready to install)
|
|
├── src/
|
|
│ ├── main.ts # Plugin entry: ribbon, commands, status bar, auto-sync
|
|
│ ├── settings.ts # Settings tab with validation
|
|
│ ├── types.ts # Shared TypeScript types
|
|
│ ├── rmapi/
|
|
│ │ └── bridge.ts # rmapi CLI wrapper (env, auth check, JSON parsing)
|
|
│ ├── sync/
|
|
│ │ ├── downloader.ts # Recursive doc listing + incremental download
|
|
│ │ └── tracker.ts # Sync state persistence (merges with settings)
|
|
│ └── utils/
|
|
│ └── process.ts # Child process runner with error handling
|
|
├── main.ts # Entry point (re-exports plugin)
|
|
├── manifest.json # Obsidian plugin manifest
|
|
├── package.json # Build scripts
|
|
├── esbuild.config.mjs # esbuild config (build + watch modes)
|
|
└── IMPLEMENTATION.md # This file
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 Usage
|
|
|
|
1. **Install plugin**: Copy folder to Obsidian plugins directory
|
|
2. **Install rmapi**: Download from https://github.com/ddvk/rmapi/releases
|
|
3. **Configure**: Open Settings → reMarkable Sync
|
|
4. **Authenticate**: Run `rmapi` in a terminal once to pair with your tablet
|
|
5. **Sync**: Click ribbon pencil icon or run command "Sync from reMarkable"
|
|
|
|
---
|
|
|
|
## 🔄 Sync Flow
|
|
|
|
```
|
|
User clicks ribbon icon / auto-sync interval
|
|
→ isAuthenticated() check (fails with notice if not paired)
|
|
→ listAllDocuments("/") recursively
|
|
→ ls --json / (root)
|
|
→ For each CollectionType: recurse into subfolder
|
|
→ For each DocumentType:
|
|
→ Skip if lastSynced >= modifiedClient (incremental)
|
|
→ sanitizeFileName() for safe filesystem names
|
|
→ mkdir downloadPath (vault-relative)
|
|
→ rmapi get → download .rm file
|
|
→ (if enabled) rmapi geta → download annotated PDF
|
|
→ trackDocument() in sync state
|
|
→ Status bar updated: "Last sync: HH:MM:SS"
|
|
```
|
|
|
|
---
|
|
|
|
## ⚙️ Configuration
|
|
|
|
| Setting | Default | Description |
|
|
|---|---|---|
|
|
| `remarkableHost` | `https://10.11.99.1` | reMarkable tablet URL |
|
|
| `rmapiBinaryPath` | `rmapi` | Path to rmapi binary |
|
|
| `downloadPath` | `remarkable/` | Vault folder for downloads |
|
|
| `convertToPdf` | `true` | Auto-download annotated PDFs |
|
|
| `enableHandwritingMd` | `true` | Enable OCR pipeline (Phase 2) |
|
|
| `glmocrServerUrl` | `http://100.103.83.12:5002` | GLM-OCR SDK Server |
|
|
| `glmocrApiKey` | `any-string` | Dummy key for self-hosted |
|
|
| `ollamaHost` | `http://100.103.83.12:11435` | Ollama server |
|
|
| `styleModel` | `qwen3:32b` | Model for markdown cleanup |
|
|
| `pageRenderer` | `drawj2d` | `.rm` → PNG tool |
|
|
| `javaPath` | `java` | Java runtime for drawj2d |
|
|
| `syncInterval` | `0` | Minutes between auto-sync (0 = off) |
|
|
|
|
---
|
|
|
|
## 📦 External Dependencies
|
|
|
|
### Required (user-provided)
|
|
- **rmapi binary**: https://github.com/ddvk/rmapi/releases
|
|
- **reMarkable tablet**: On local network at configured host
|
|
|
|
### Optional (for Phase 2 OCR)
|
|
- **GLM-OCR Server**: Run `python -m glmocr.server` on configured host
|
|
- **Ollama**: With `qwen3:32b` (or chosen model) pulled
|
|
- **Java**: For `drawj2d` page rendering
|
|
|
|
---
|
|
|
|
## 🔮 Phase 2: Handwriting OCR Pipeline (Planned)
|
|
|
|
1. **Extract HWR text** from `.rm` file's built-in handwriting recognition layer
|
|
2. **Render pages to PNG** via `drawj2d` or `rM2svg`
|
|
3. **POST to GLM-OCR Server** (`glm-5.1:cloud` equivalent) for structured markdown
|
|
4. **Style refinement** via Ollama (`qwen3:32b`) — light cleanup (Option A):
|
|
- Fix heading hierarchy
|
|
- Consolidate fragments
|
|
- Detect lists, emphasis
|
|
- Preserve all content
|
|
|
|
---
|
|
|
|
## 📝 Notes
|
|
|
|
- **Auth token storage**: `RMAPI_CONFIG` points to `.obsidian/rmapi` inside vault (portable)
|
|
- **Sync state**: Stored via Obsidian's `saveData()` API, merged with settings
|
|
- **Auto-sync**: Registered via `registerInterval()` — Obsidian cleans up on unload
|
|
- **Filename safety**: `sanitizeFileName()` replaces `[\\/:*?"<>|]` with `_`
|
|
- **Incremental logic**: Compares ISO timestamps (`lastSynced >= modifiedClient`)
|
|
|
|
---
|
|
|
|
## 🎯 Build & Install
|
|
|
|
```bash
|
|
# Build
|
|
npm install
|
|
npm run build
|
|
|
|
# Dev (watch mode)
|
|
npm run dev
|
|
|
|
# Install in Obsidian
|
|
Copy the `obidian-remarkable` folder to:
|
|
- Linux: ~/.config/obsidian/plugins/
|
|
- macOS: ~/Library/Application Support/obsidian/plugins/
|
|
- Windows: %APPDATA%\obsidian\plugins\
|
|
```
|
|
|
|
---
|
|
|
|
**Status**: ✅ Ready for testing. All reviewed issues fixed. |