Architecture ============ jott uses a mixin-based architecture to keep files under 300 lines while providing extensive functionality. Package Layout -------------- .. code-block:: text jot/ ├── __init__.py # Dynamic import bridge to jot.py ├── app.py # Main App class + dispatch table ├── _app_navigation_mixin.py # App navigation mixin ├── _dispatch_mixin.py # App dispatch mixin ├── core/ # Task management engine │ ├── task_manager.py # TaskManager base (8 mixins) │ ├── id_manager.py # ID generation/management │ ├── constants.py # Shared constants │ ├── archive_manager.py # Archive operations │ └── _*_mixin.py # 10 mixins (crud, delete, persistence, ...) ├── commands/ # Command processing │ ├── handler.py # CommandHandler base (11 mixins) │ └── _*_mixin.py # 11 mixins (core, bulk, notes, gcal, ...) ├── ui/ # Display and input │ ├── display*.py # Task, footer, help, project, archive display │ ├── formatting.py # Text formatting utilities │ ├── styles.py # Color and style definitions │ ├── rendering.py # Buffered output for flicker-free rendering │ ├── input.py # Keyboard input handling │ └── picker.py # Fuzzy-search picker widget ├── projects/ # Multi-project support │ ├── registry.py # ProjectRegistry (auto-discovery) │ └── backup.py # ProjectBackup ├── categories/ # Category system │ ├── manager.py # CategoryManager │ ├── config.py # CategoryConfig │ └── templates.py # CategoryTemplates ├── integrations/ # External services │ ├── gcal/ # Google Calendar (auth, events, accounts) │ └── keywords/ # Keyword automation (handler, config, handlers) ├── mcp/ # Model Context Protocol server │ ├── server.py # FastMCP server │ ├── handlers.py # Request handlers │ └── schemas.py # Data schemas ├── cli/ # CLI subcommands │ ├── archive.py # Archive management │ ├── config.py # Configuration commands │ └── views.py # Project views └── utils/ # Shared utilities ├── text_utils.py # Text processing ├── date_utils.py # Date handling └── validation.py # Input validation Mixin Pattern ------------- jott uses Python's multiple inheritance to compose large classes from focused mixins. Each mixin file targets fewer than 300 lines. **TaskManager** composes 10 mixins: - ``_CrudMixin`` — create, read, update operations - ``_DeleteMixin`` — delete with smart current-task reassignment - ``_PersistenceMixin`` — JSON file load/save - ``_NavigationMixin`` — cursor movement through task list - ``_MetadataMixin`` — priority, labels, status - ``_SubtaskMixin`` — subtask sync from notes - ``_CompressMixin`` — task list compression - ``_ExportMixin`` — export to various formats - ``_AgeBacklogMixin`` — aging and backlog management - ``_IdMigrationMixin`` — ID scheme migration **CommandHandler** composes 11 mixins covering core commands, bulk operations, notes, transfers, AI analysis, Google Calendar, and more. **App** composes 2 mixins for navigation and dispatch, plus contains the main event loop and the ``_QUICK_ADD_SIMPLE`` dispatch table that maps key codes to ``CommandHandler`` methods. Main Event Loop --------------- The ``App`` class in ``app.py`` runs the main loop: 1. If ``_needs_render`` is set, clear screen and render the task display inside a ``buffered_output()`` context manager (all output captured in a ``StringIO`` buffer and written atomically in one ``sys.stdout.write()`` call to prevent tearing) 2. Read a single keypress (with ~1s timeout for auto-refresh) 3. On timeout, check file mtime — only set ``_needs_render`` if file changed 4. On keypress, set ``_needs_render`` and dispatch to the appropriate handler 5. Repeat This design eliminates idle flickering (no redraws when nothing changed) and partial-frame tearing (atomic writes). In **quick-add mode**, printable characters accumulate in a buffer; Enter submits the task. Special keys (Shift+combos, Ctrl+combos) are dispatched directly. In **command mode**, single keypresses map to commands (``d`` for delete, ``e`` for edit, etc.). Data Model ---------- Tasks are stored as JSON in ``.jot.json``: .. code-block:: json { "tasks": [ { "id": 1, "text": "Task description", "done": false, "current": true, "priority": "normal", "labels": ["bug"], "notes": "Detailed notes...", "created_at": "2025-01-01T00:00:00" } ], "archived": [] } The ``ProjectRegistry`` maintains ``~/.jot-projects.json`` mapping project names to filesystem paths. Auto-discovery scans ``~/projects/`` for directories containing ``.jot.json`` files. Dynamic Import Bridge --------------------- ``jot/__init__.py`` uses ``importlib.util`` to load the root-level ``jot.py`` script as a module, bridging the standalone script entry point with the package structure: .. code-block:: python _jot_script_path = Path(__file__).parent.parent / 'jot.py' _spec = importlib.util.spec_from_file_location("_jot_script", _jot_script_path) _jot_script = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(_jot_script) main = _jot_script.main This allows ``from jot import main`` to work while the actual ``main()`` function lives in the standalone script. This is relevant for Sphinx autodoc configuration — optional dependencies must be mocked before any import of the ``jot`` package.