Headline titles were rendered as plain text, so an inline org link in a heading showed its raw [[target][desc]] syntax and could not be followed. Body text and list items already route links through renderInline and onLinkActivated; apply the same treatment to the title Label. The title now renders as StyledText via renderInline, exposes a pointer cursor over links through a HoverHandler, and dispatches clicks to openLink (which resolves and navigates via the model). Non-link text keeps its done-aware color and strike-through. |
||
|---|---|---|
| crates | ||
| doc | ||
| ui | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| VISION.md | ||
| test.org | ||
README.md
Orkmode
A standalone org-mode implementation for KDE, built with Rust (parser/core) and C++ (Qt UI).
Project Structure
orkmode/
├── Cargo.toml # Workspace root
├── crates/
│ ├── org-ast/ # AST type definitions
│ │ └── src/
│ │ ├── lib.rs # Module exports
│ │ ├── document.rs # Document, settings
│ │ ├── headline.rs # Section, Headline, Planning
│ │ ├── elements.rs # Block elements (paragraph, list, table, etc.)
│ │ ├── objects.rs # Inline objects (markup, links, etc.)
│ │ ├── timestamp.rs # Timestamp types
│ │ └── span.rs # Source location tracking
│ ├── org-parser/ # Tree-sitter based parser
│ │ ├── build.rs # Compiles tree-sitter-org grammar
│ │ ├── tree-sitter-org/ # Git submodule: nvim-orgmode/tree-sitter-org
│ │ └── src/
│ │ ├── lib.rs # Parser API
│ │ ├── convert.rs # Tree-sitter → AST conversion
│ │ ├── ts.rs # Tree-sitter language bindings
│ │ └── error.rs # Error types
│ ├── ork-cli/ # Command-line interface (9P client)
│ │ └── src/
│ │ ├── main.rs # CLI commands (base + friendly verbs)
│ │ └── client.rs # 9P2000 client
│ └── ork-server/ # 9P server exposing org files
│ └── src/
│ ├── bin/ork-server.rs # Server entry point
│ ├── state.rs # Document state, section ops, ctl verbs
│ ├── namespace.rs # 9P namespace (docs, sections, agenda, query)
│ ├── p9.rs # 9P protocol handling
│ └── virtfs.rs # Virtual filesystem tree
└── ui/ # Qt6/QML GUI (9P client, no FFI)
├── CMakeLists.txt # Qt6 build (Core/Gui/Qml/Quick/Network)
├── src/
│ ├── main.cpp # Entry point; --addr, --selftest
│ ├── p9client.{h,cpp} # Minimal synchronous 9P2000 client
│ ├── orkclient.{h,cpp} # QML-facing façade over the 9P client
│ └── documentmodel.{h,cpp} # Lazy QAbstractItemModel section tree
└── qml/
├── Main.qml # Window, document sidebar, outline
└── OutlineView.qml # Folding TreeView with structural styling
Features
Supported Org Syntax
- Headlines with nesting levels
- TODO keywords (customizable via #+TODO:)
- Priority cookies ([#A], [#B], [#C])
- Tags
- Property drawers
- Planning lines (SCHEDULED, DEADLINE, CLOSED)
- Timestamps (active, inactive, ranges, repeaters)
- Paragraphs
- Plain lists (unordered, ordered, checkboxes)
- Source blocks
- Tables
- Drawers
- Keywords/directives
- Comments
- LaTeX environments
- Inline markup (bold, italic, etc.) - AST types ready, parsing TODO
- Links - AST types ready, parsing TODO
- Footnotes
- Citations
Document Settings
Parses common #+KEYWORD directives:
- TITLE, AUTHOR, EMAIL, DATE
- TODO, SEQ_TODO, TYP_TODO
- PROPERTY, STARTUP, OPTIONS
- FILETAGS, CATEGORY, ARCHIVE
Building
Prerequisites
- Rust 1.70+
- C compiler (for tree-sitter grammar)
- Qt 6.3+ and CMake 3.21+ (for the GUI)
Build
# Build all crates
cargo build
# Run tests
cargo test
# Build release
cargo build --release
GUI
The GUI is a separate Qt6/CMake project that talks to ork-server over 9P.
cmake -S ui -B ui/build -DCMAKE_BUILD_TYPE=Release
cmake --build ui/build -j
# Run against a running ork-server (see below)
./ui/build/bin/orkmode -a unix:///tmp/ork.sock
Usage
Server
Start the 9P server to expose an org directory:
# Unix socket (preferred for local use)
ork-server -a unix:///tmp/ork.sock -d ~/org
# TCP socket
ork-server -a tcp://localhost:5640 -d ~/org
CLI (ork)
ork is a 9P client that talks to ork-server. All org-mode intelligence lives in the server.
# Set the server address (or use -a on each call)
export ORK_ADDR=unix:///tmp/ork.sock
# Base 9P operations
ork ls / # List root namespace
ork read /idx # Read a file
ork write /work/task/keyword DONE # Write a value
# Friendly verbs (compositions of 9P ops)
ork docs # List documents (name + title)
ork sections work # Top-level sections in work.org
ork tree work # Full section outline
ork todos # All TODO items across docs
ork todos work # TODOs in work.org only
# Section details
ork get work my-task # Show section fields
ork set work my-task keyword DONE # Set a field
ork set work my-task tags work:urgent # Set tags (colon-separated)
# Create sections
ork mkdoc notes # Create notes.org
ork add work "* TODO New task :work:" # Add top-level section
ork add work "* Subtask" -p parent-id # Add child section
# TODO management
ork todo work my-task # Cycle to next state
ork todo work my-task DONE # Set explicit state
ork tag work my-task urgent # Add tag
ork tag work my-task urgent -r # Remove tag
# Structural operations
ork mv work my-task up # Reorder among siblings
ork promote work my-task # Decrease level
ork demote work my-task # Increase level
ork rm work my-task # Delete section and subtree
ork refile work my-task archive # Move to another doc
ork refile work my-task archive -p done # As child of 'done' section
ork archive work my-task # Move to work.org_archive
# Properties
ork prop work my-task CUSTOM_ID # Get property
ork prop work my-task EFFORT 2h # Set property
ork prop work my-task EFFORT -d # Delete property
# Agenda views
ork agenda todos # All TODOs
ork agenda today # Items scheduled/due today
ork agenda week # Next 7 days
# Query
ork query 'tag:work' # Match sections by tag
ork query 'tag:work+todo:TODO' # Compound query
ork query 'priority:A' # By priority
# Maintenance
ork reload # Rescan files from disk
ork lint # Validate all documents
ork lint work # Validate one document
Any 9P client works (e.g., plan9port 9p):
9p -a 'unix!/tmp/ork.sock' ls /
9p -a 'unix!/tmp/ork.sock' read /idx
9p -a 'unix!/tmp/ork.sock' write /work/task/keyword DONE
Architecture
Parser Design
The parser uses tree-sitter with the nvim-orgmode/tree-sitter-org grammar.
Benefits:
- Incremental parsing: Only re-parses changed regions
- Error recovery: Produces partial AST even with syntax errors
- Performance: O(n) parsing, minimal memory allocation
- Battle-tested: Used by Neovim's org-mode plugin
AST Design
The AST follows the org-element specification:
- Document: Root node with settings and sections
- Section: Headline + content + children
- Elements: Block-level constructs (paragraphs, lists, blocks)
- Objects: Inline constructs (markup, links, timestamps)
All nodes include source span information for editor integration.
Roadmap
Phase 1: Parser ✓
- Tree-sitter integration
- Core AST types
- Headline/section parsing
- Block element parsing
- Complete inline object parsing
Phase 2: 9P Server ✓
- Document state with locking
- Read/write section fields (keyword, priority, title, tags, timestamps, body)
- Create documents and sections
- Structural ops (delete, move, promote, demote, refile, archive)
- Agenda views (todos, today, week)
- Query interface (tag, todo, priority)
- ctl verbs (reload, lint)
Phase 3: Qt UI (Current)
- Document rendering (read-only)
- Structural styling (keywords, priorities, tags, timestamps)
- Folding/cycling (TreeView)
- Navigation
- Inline editing
- TODO state cycling (click to toggle)
- Tag/property editing
- Syntax highlighting for bodies
Phase 4: Agenda
- Dedicated agenda view panel
- Day/week/month views
- Filtering and sorting
- Custom views
Phase 5: Advanced Features
- Code block execution
- Diagram rendering (mermaid, plantuml)
- Capture templates
- Clock/time tracking
License
GPL-3.0-or-later