Skip to content

KindleTools/KindleToJoplin

Repository files navigation

KindleToJoplin

Import your Kindle highlights and notes from My Clippings.txt directly into Joplin with full multi-language support, preview dialog, and tag extraction.

Features

  • Multi-language support: Spanish, English, Italian, French, German, and Portuguese
  • Preview dialog: Review and edit highlights before importing
  • Editable table: Modify author, book title, and tags before import
  • Tag extraction: Convert Kindle notes into Joplin tags (UPPERCASE)
  • Bookmark import: Kindle bookmarks are imported as highlights
  • Configurable hierarchy: Organize by Author → Book, Book only, or Author only
  • Duplicate detection: Avoids creating duplicate notebooks and tags
  • Author normalization: "García Márquez, Gabriel" → "Gabriel García Márquez"
  • Clipping limit detection: Identifies highlights truncated by Amazon's 10% limit
  • Metadata block: Optional metadata at the end of each note

Installation

From Joplin

  1. Open Joplin
  2. Go to Tools → Options → Plugins
  3. Search for "KindleToJoplin"
  4. Click Install

Manual Installation

  1. Download the latest .jpl file from Releases
  2. Open Joplin
  3. Go to Tools → Options → Plugins
  4. Click Install from file
  5. Select the downloaded .jpl file

Usage

  1. Connect your Kindle to your computer (or use Kindle for PC/Mac)
  2. Locate your My Clippings.txt file:
    • Kindle device: Kindle/documents/My Clippings.txt
    • Kindle for PC: Documents/My Kindle Content/My Clippings.txt
    • Kindle for Mac: ~/Library/Application Support/Kindle/My Kindle Content/My Clippings.txt
  3. In Joplin, go to Tools → Import Kindle Highlights
  4. Select your My Clippings.txt file
  5. Review the highlights in the preview dialog
  6. Edit any author names, book titles, or tags as needed
  7. Select which highlights to import
  8. Click Import Selected

Settings

Access settings via Tools → Options → Kindle Import:

Setting Description
Default clippings file path Path to your My Clippings.txt file
Kindle language Language of your Kindle device
Note hierarchy How to organize imported highlights
Default destination notebook Root notebook for imports (default: "Kindle Highlights")
Include metadata block Add metadata to each note

Advanced Settings

Setting Description
Note latitude/longitude/altitude GPS coordinates for imported notes
Note author Author field for imported notes
Note source Source field (default: "kindle-to-joplin")
Note source application Source app field (default: "kindle-to-joplin")

Hierarchy Options

Author → Book (Recommended)

Kindle Highlights/
  └── GARCÍA MÁRQUEZ, GABRIEL/
      └── Cien años de soledad/
          ├── [p.42] Muchos años después...
          └── [p.103] El mundo era tan reciente...

Book only

Kindle Highlights/
  └── Cien años de soledad/
      ├── [p.42] Muchos años después...
      └── [p.103] El mundo era tan reciente...

Author only

Kindle Highlights/
  └── GARCÍA MÁRQUEZ, GABRIEL/
      ├── [p.42] Muchos años después...
      └── [p.103] El mundo era tan reciente...

Note Format

Each imported highlight becomes a Joplin note with:

[0042] Muchos años después, frente al pelotón de fusil...

Muchos años después, frente al pelotón de fusilamiento, el coronel
Aureliano Buendía había de recordar aquella tarde remota en que su
padre lo llevó a conocer el hielo.

-----
- date: 15/01/2024 10:30
- author: Gabriel García Márquez
- book: Cien años de soledad
- page: 42
- tags: MEMORIA, TIEMPO
-----

Note format: [NNNN] where NNNN is the zero-padded page number (4 digits).

Tag Extraction

When you add a note to a highlight in Kindle, that note becomes a tag in Joplin. Multiple tags can be separated by commas, semicolons, or periods. Tags are automatically converted to UPPERCASE.

Architecture

This section documents the technical decisions and patterns used in the project.

Project Structure

src/
├── index.ts              # Plugin entry point, registers commands and menu
├── settings.ts           # Settings registration with Joplin API
├── parser/               # Domain: Kindle clippings parsing
│   ├── index.ts          # Main parser orchestration
│   ├── types.ts          # TypeScript interfaces
│   ├── languages.ts      # Multi-language regex patterns (Strategy pattern)
│   └── dateParser.ts     # Locale-aware date parsing with date-fns
├── importer/             # Domain: Joplin API integration
│   ├── index.ts          # Import orchestration
│   └── duplicates.ts     # Notebook/tag cache and deduplication
└── ui/                   # Presentation layer
    ├── dialog.ts         # Dialog controller (plugin side)
    ├── dialog.html       # Dialog template
    ├── dialog.css        # Styles (light/dark theme support)
    └── dialogScript.js   # Webview script (sandboxed)

Design Patterns

Pattern Location Purpose
Strategy languages.ts Each language is a configuration object with the same interface, allowing runtime selection
Factory parseEntry() Creates KindleClipping objects from raw text
Facade parseClippings() Simple API that hides parsing complexity
Repository duplicates.ts Caches notebooks/tags to avoid duplicate API calls
Builder generateNoteBody() Constructs note content step by step

Key Design Decisions

1. Separation of Concerns

  • Parser knows nothing about Joplin - it only transforms text to structured data
  • Importer receives clean data and handles Joplin API
  • UI is sandboxed in a webview, communicates via JSON messages

2. Multi-language Support via Strategy Pattern

// Each language defines patterns with the same interface
const spanish: LanguageConfig = {
  code: 'es',
  patterns: { highlight: /.../, note: /.../, page: /.../ },
  dateFormat: "EEEE, d 'de' MMMM 'de' yyyy H:mm:ss"
};

Adding a new language only requires adding a new configuration object.

3. Webview Communication Joplin sanitizes inline scripts, so we use a hidden <div> with JSON data:

<div id="initial-data" style="display:none">{"clippings":[...]}</div>

The webview script reads this on load, avoiding bidirectional messaging complexity.

4. Duplicate Detection via Caching Instead of querying the API for each notebook/tag, we fetch all once and cache in memory:

const notebookCache = new Map<string, Folder>();
const tagCache = new Map<string, string>();

Tech Stack

Technology Version Purpose
TypeScript 5.x Type-safe development
Webpack 5.x Bundle for Joplin plugin format
date-fns 4.x Locale-aware date parsing (tree-shakeable)
uuid 10.x Unique IDs for clippings
Jest 29.x Unit and integration testing
ESLint 9.x Code quality (flat config)
Prettier 3.x Code formatting

Bundle Optimization

  • date-fns: We import only required functions and locales to minimize bundle size
  • Locales: Only 6 locales imported (es, en, it, fr, de, pt), not the full locale package
  • Tree-shaking: Webpack removes unused code in production builds

Testing Strategy

Test Type Location Coverage
Unit tests test/*.test.ts Parser, importer, date parsing, duplicates
Integration tests test/integration.test.ts Full parsing workflow with fixture files
UI tests test/standalone.html Manual testing with Chrome DevTools
npm test              # Run all tests
npm test -- --watch   # Watch mode
npm test -- --coverage # Coverage report (97%+ coverage)

Development

Prerequisites

  • Node.js 18+
  • npm

Setup

# Clone the repository
git clone https://github.com/KindleTools/KindleToJoplin.git
cd KindleToJoplin

# Install dependencies
npm install

# Build in watch mode
npm run dev

# Build for production
npm run dist

# Run tests
npm test

Testing with Joplin

  1. Build the plugin: npm run dist
  2. In Joplin, go to Tools → Options → Plugins
  3. Under Advanced Settings, add the path to the dist folder
  4. Restart Joplin

Supported Languages

Language Highlights Bookmarks
Spanish subrayado marcador
English Highlight Bookmark
Italian evidenziazione segnalibro
French surlignement signet
German Markierung Lesezeichen
Portuguese destaque marcador

Troubleshooting

Parsing errors

If highlights aren't being parsed correctly:

  1. Check that you've selected the correct language in settings
  2. Ensure the My Clippings.txt file is valid UTF-8
  3. Check the developer console for error messages (Help → Toggle Development Tools)

Encoding issues

The plugin handles UTF-8 with BOM automatically. If you still have encoding issues, try opening the file in a text editor and saving it as UTF-8 without BOM.

Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/my-feature
  3. Commit your changes: git commit -am 'Add new feature'
  4. Push to the branch: git push origin feature/my-feature
  5. Submit a pull request

Future Improvements

Potential enhancements for future versions:

Feature Description Priority
i18n (Internationalization) Translate plugin UI to multiple languages (Spanish, German, French, etc.) using i18n library Medium
File → Import menu Register as import module via joplin.interop.registerImportModule() for native Joplin integration Medium
GitHub Actions CI Automated testing and building on pull requests Low
Clipboard import Paste highlights directly from Kindle app without file Low
Export to Markdown Export selected highlights to a single Markdown file Low

License

MIT License - see LICENSE for details.

Acknowledgments

  • Joplin for the excellent note-taking app and plugin API
  • date-fns for date parsing

About

Import your Kindle highlights and notes from `My Clippings.txt` directly into Joplin with full multi-language support, preview dialog, and tag extraction.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors