Commit Graph

47 Commits

Author SHA1 Message Date
Levi Neely 84101145d7 Add language plugin registry and Emacs-style #+RESULTS: formatting
Code execution now uses a LanguageRegistry with built-in defaults
(bash, python, ruby, perl, javascript) that can be overridden by TOML
config files in /etc/orkmode/languages or ~/.config/orkmode/languages.

Match Emacs org-babel result formatting: fixed-width (": " prefix) for
output under 10 lines, #+begin_example blocks for 10+ lines
(org-babel-min-lines-for-block-output).

Fix insert_results duplication: the source block span end already points
to the start of the line after #+END_SRC, so searching for the next
newline skipped the existing #+RESULTS: line and prepended duplicates on
re-execution. Also handle replacing existing #+begin_example result blocks.

Fix parser: #+begin_example, comment, and export blocks were misparsed as
SourceBlock with a bogus language, causing "unsupported language" errors
on re-execution. Map them to their correct AST element kinds.
2026-10-01 13:36:38 +02:00
Levi Neely 4a865d7546 Write #+RESULTS: blocks after code execution
- Execute returns results and writes them to the org file
- Replaces existing #+RESULTS: block if present
- Results formatted with : prefix per org convention
- Process blocks in reverse order to preserve byte offsets
- Blank line added after results for proper separation
2026-10-01 11:46:16 +02:00
Levi Neely 17f18b044c Add Landlock sandbox for code execution
- sandbox.rs: Landlock filesystem restrictions applied via pre_exec
- Library paths (/lib, /usr/lib) need execute permission for dynamic linker
- 30s timeout with SIGKILL on expiry
- Network access allowed (Landlock only restricts filesystem)
- Minimal env: PATH, HOME=/tmp, LANG, TERM
2026-10-01 11:38:43 +02:00
Levi Neely 445177bbfa Add execute ctl verb for org-babel style code execution
- execute <doc> — all blocks in document
- execute <doc> <section> — all blocks in subtree
- execute <doc> <section> <idx> — single block by index
- execute <doc> <section> <name> — block by #+NAME in section
- execute <doc> :<name> — named block anywhere in doc

Parser now extracts #+NAME: from block's affiliated keywords.
Supports bash, python, ruby, perl, and node interpreters.
Basic sandboxing via env_clear() and restricted PATH.
2026-10-01 10:51:39 +02:00
Levi Neely b2953a7caf Fix font and scaling in Main.qml
Replace all pixelSize with pointSize using baseFontSize:
- Sidebar width: 16em (was 220px)
- Header margins: 1em (was 12px)
- Document title: baseFontSize (was 13px)
- Document name: baseFontSize-2 (was 10px)
- Section header: baseFontSize-1 (was 11px)
- Delegate padding: 0.8em / 0.4em
- All spacing font-relative
2026-10-01 10:25:03 +02:00
Levi Neely 9e48a89c92 Use font-relative units instead of pixels
All spacing/margins now scale with baseFontSize:
- Table cell padding: 0.5em horizontal, 0.25em vertical
- Table margins: 0.75em
- Column spacing: 1.5em
- Code block margins: 0.5em
- Border radius: fontSize/3
- Left rule width: fontSize/6

Ensures proper display at any DPI/font size.
2026-10-01 10:23:20 +02:00
Levi Neely 09962cc800 Fix table rendering: left align, padding, nested Repeater scope
- Store tableData at Rectangle level to avoid scope issues
- Use ColumnLayout + RowLayout instead of GridLayout (cleaner nesting)
- Anchor left instead of center
- 8px margins, 16px column spacing
- Add ?? false fallback for hasHeader to prevent undefined errors
2026-10-01 10:21:09 +02:00
Levi Neely 30fa1bf35e Render org tables as styled grids
- Parse | delimited rows into cells
- Detect header separator (|---+---|)
- Header row: bold, text color
- Data rows: normal, placeholder color
- Same alternate background as code blocks
2026-10-01 10:19:15 +02:00
Levi Neely 17906c06f9 Render org src blocks as styled code blocks
- Parse body text into segments (text vs code)
- Src blocks get: monospace font, alternate background, rounded corners
- Language label shown above code (python, bash, etc.)
- Regular text keeps left rule indicator

Handles #+BEGIN_SRC lang ... #+END_SRC blocks.
2026-10-01 10:16:27 +02:00
Levi Neely 9e6dc589bd Fix Makefile install target
install depends on 'all' (which includes ui if Qt6 present)
instead of having broken split dependency lines
2026-10-01 10:12:53 +02:00
Levi Neely 43565ad1b6 Fix planning line stripping in section_body
- Use 'contains' instead of 'starts_with' to handle combined lines
  like 'SCHEDULED: <...> DEADLINE: <...>'
- Track 'seen_content' to strip planning/drawers only at the start
- Skip empty lines before content
- Continue stripping planning lines until real content appears
2026-10-01 10:10:45 +02:00
Levi Neely f5f2bffce4 Strip planning line and drawers from section body
section_body() now filters out:
- Leading planning line (SCHEDULED/DEADLINE/CLOSED)
- Property drawers (:PROPERTIES: ... :END:)
- Any other drawers (:LOGBOOK:, etc.)
- Leading/trailing blank lines

These are displayed separately in the UI.
2026-10-01 10:09:39 +02:00
Levi Neely ff48f9c7ee Remove Qt jargon from comments 2026-10-01 10:01:22 +02:00
Levi Neely 429c896f5a Add collapsible properties drawer
- PropertiesRole returns QVariantMap of key->value
- loadProperties() parses KEY=value lines from /properties file
- QML drawer shows ':PROPERTIES:' header with ▶/▼ toggle
- Properties shown as monospace :KEY: value pairs when expanded
- Drawer starts collapsed by default
2026-10-01 10:00:20 +02:00
Levi Neely eb3b174b12 Increase body text size and line height
Body text: baseFontSize + 1 (was baseFontSize)
Line height: 1.3 (was 1.15)
2026-10-01 09:47:56 +02:00
Levi Neely 7f948de2ab Use listDirs() instead of list() for section loading
list() returns files AND directories. Section entries are directories only.
Files like 'title', 'keyword', 'body' were being incorrectly treated as
child sections. Using listDirs() filters to directories only.
2026-10-01 09:40:03 +02:00
Levi Neely 350ce66860 Fix DocumentModel section loading
- loadSections: recursively load first, then populate childIds after
  all sections exist (checking parentId matches)
- Remove childIdsFor helper (dead code)
- Remove isSectionVisible (dead code)
2026-10-01 09:37:55 +02:00
Levi Neely fec815ac50 Fix path prefix in DocumentModel::loadDocument
9P paths need leading slash. loadSections was called with bare document
name (e.g., 'test') instead of '/test'.
2026-10-01 09:35:42 +02:00
Levi Neely 70ec9509a4 Replace TreeView with ListView for document outline
TreeView (inherits TableView) is designed for file browsers with fixed row
heights, not structured document editing. ListView gives us:
- Full control over row heights (implicitHeight just works)
- Simpler delegate without TreeViewDelegate quirks
- Manual indentation via leftMargin based on level
- Expansion state tracked in model with hasChildren/expanded roles
- Click-to-expand triangles for sections with children

DocumentModel changed from QAbstractItemModel (tree) to QAbstractListModel
(flat list) with sections ordered depth-first. Tree structure preserved via
level, parentId, childIds fields. Expansion state controls visibility.
2026-10-01 09:32:56 +02:00
Levi Neely 04425d9e9f Use rowHeightProvider for proper row sizing
TreeView (inherits TableView) defaults to fixed row heights. Setting
rowHeightProvider to return -1 tells it to use each delegate's
implicitHeight, enabling variable-height rows.
2026-10-01 09:29:35 +02:00
Levi Neely b0a4956d8f Fix row overlap in OutlineView
TreeViewDelegate doesn't auto-size from contentItem. Explicitly bind
implicitHeight to contentColumn.implicitHeight so rows size correctly.
2026-10-01 09:27:15 +02:00
Levi Neely 3d2edf1807 Fix system theme consistency
- In system mode, read palette from QGuiApplication::palette() rather
  than parsing kdeglobals (platform plugin already handles this)
- Don't call setPalette() in system mode — let Qt handle it
- Add isOverriding property for QML to check mode
- Use Fusion style by default for consistent palette respect
- Improved fallback logic in systemSchemePath() for dark/light detection
- Added /etc/xdg/kdeglobals as fallback for system-wide defaults
2026-10-01 09:15:31 +02:00
Levi Neely 5677be6236 Add font customization and UI scaling
ThemeManager now supports:
  - fontFamily, fontSize, fontStyle with full QFontDatabase access
  - uiScale (0.5x to 4.0x) saved to theme.conf
  - resetFont() to return to system defaults

SettingsDialog provides UI for:
  - Color scheme selection (all installed KDE schemes)
  - Font family/style/size with live preview
  - UI scale slider with percentage display

OutlineView uses pointSize (scalable) instead of pixelSize (fixed).
All font sizes derived from theme.fontSize or baseFontSize.
2026-10-01 09:11:45 +02:00
Levi Neely 64c7272f70 Add KDE color scheme support
ThemeManager reads .colors files from /usr/share/color-schemes and
~/.local/share/color-schemes (KDE standard locations). Supports:
  - 'system' mode: follows kdeglobals ColorScheme setting
  - explicit scheme name: forces specific scheme (BreezeDark, etc.)

QML now uses theme.* colors throughout instead of hardcoded Nord values.
Org-mode semantic colors (todoColor, doneColor, priorityAColor, etc.)
adjust automatically for dark/light themes.

Settings saved to ~/.config/orkmode/theme.conf
2026-10-01 09:08:15 +02:00
Levi Neely bc2130d5b1 Update Makefile to install all targets
- Add ork-server and orkmode (GUI) to install target
- Make UI build optional (only if Qt6 Qml available)
- Add ui target for explicit GUI build
- Clean ui/build directory on 'make clean'
- Update help text with requirements
2026-10-01 08:58:42 +02:00
Levi Neely ed73ae5cca Clean up docs and fix set_planning_field
- README: update CLI docs to reflect 9P client architecture
- README: update roadmap to reflect v0.3 progress (server, UI)
- AGENTS.md: update CLI examples and current limitations
- state.rs: rewrite set_planning_field to properly parse/rebuild
  planning lines (removes dead code, fixes replace logic)
2026-10-01 08:53:22 +02:00
Levi Neely e129f9ee94 Fix overlapping text in OutlineView
- Replace manual Item+anchoring with ColumnLayout for proper height calc
- Remove redundant bodyMeasure Text and bodyWidth property
- Add proper top/bottom margins using rowPad constant
- Body row now uses RowLayout with Layout.fillWidth
2026-10-01 08:50:52 +02:00
Levi Neely 71a89943ae Add v0.3 read-only Qt6/QML GUI (9P client)
A standalone Qt6/QML app in ui/ that speaks 9P to ork-server; no FFI,
no privileged path. All org logic stays in the server.

- p9client: minimal synchronous 9P2000 client over QTcp/QLocalSocket
- orkclient: QML-facing facade (read/list/listDirs/write/rdwr/docs)
- documentmodel: lazy QAbstractItemModel of the section tree, with a
  BodyRole that reads /<doc>/<id>/body (planning line + :PROPERTIES:
  drawer stripped, since those are shown as structural objects)
- Main.qml: document sidebar + outline; OutlineView.qml: folding
  TreeView rendering keyword/priority/title/tags/timestamps as distinct
  objects, with section body shown inline beneath each headline
- main.cpp: --addr/$ORK_ADDR, --selftest (headless outline dump),
  --shot (headless window grab for debugging)
- CMake build; binary emitted to bin/ to avoid the QML-module-URI vs
  target-name directory clash

Known issue: inline body layout still needs work (row sizing/overlap
when bodies wrap); to be iterated.
2026-10-01 02:02:53 +02:00
Levi Neely 33173bcc29 Remove dead orkmode-core (FFI) crate
Dropping the C FFI layer left orkmode-core unused: nothing in the
workspace depends on it, and the GUI will speak 9P instead. Delete the
crate, drop it from the workspace, and scrub FFI references from the
docs (README structure/usage, AGENTS structure/FFI/next-steps).
2026-09-30 22:27:50 +02:00
Levi Neely 7ee65a4a2d Update VISION to the 9P architecture and editing model
The vision predated the 9P pivot and the GUI direction. Correct it:

- Architecture: clients (GUI, CLI, Emacs/vim, agents) speak 9P to
  ork-server; drop the C FFI layer entirely.
- Replace "keyboard-first, mouse optional" with "the document is the
  control surface": actions live on objects, mouse and keyboard equal,
  no menu trees or button walls.
- Add an explicit Editing Model: structured render, per-section
  raw-text editing with reparse-on-commit, and direct-manipulation
  structural edits that each map to one 9P operation.
- Refresh milestones (v0.1 parser/CLI and v0.2 9P server/client done;
  GUI is next) and the rationale (add the "Why 9P" section).
2026-09-30 22:21:10 +02:00
Levi Neely 943c05b61c Add rm, mv, promote, demote, refile, archive, lint, query to ork
Client verbs for the new server capabilities, each a thin wrapper over
the base 9P operations: the structural verbs write to /ctl, and query
writes to /query and prints the matches.
2026-09-30 21:52:33 +02:00
Levi Neely 12869fb9f0 Add structural ops, query, lint, and real agenda views to ork-server
Rework /ctl into a self-describing dispatch table (name, desc, fn),
inspired by the ollie 9P server: an empty write or "help" lists the
verbs, and unknown verbs report the valid set.

New control verbs, each operating on a section subtree:
- rm: delete a section and its subtree
- move up|down: reorder among siblings
- promote/demote: shift the subtree's outline level
- refile <to-doc> [parent]: move a subtree across or within documents,
  re-leveling to the target depth
- archive: move a subtree to the companion <doc>.org_archive file
- lint [doc]: reparse and report per document

Add a root /query file (rdwr): write a tag:X+todo:Y+priority:Z match
expression, read back matching sections.

Implement the previously stubbed agenda today/week views with real date
filtering, using a dependency-free civil-date day-number calculation.

Subtree edits share span-based helpers (subtree_range, sibling_ranges,
shift_headline_levels, insert_at). Covered by unit tests.
2026-09-30 21:52:33 +02:00
Levi Neely 73c4eb6cff Add tag, prop, todo, and tree commands to ork
Four more friendly verbs, all pure compositions of the base 9P
operations against the existing namespace (no server change):

- tag <doc> <id> <tag> [--remove]: toggle a single tag, mirroring
  org-toggle-tag, instead of rewriting the whole tag list.
- prop <doc> <id> <key> [value] [--delete]: get, set, or delete one
  property, mirroring org-set-property, instead of the whole drawer.
- todo <doc> <id> [state]: set the keyword, or with no argument cycle
  to the next state following the /keywords sequence, like org-todo.
- tree <doc>: print the recursive section outline.

Each edit reads the relevant namespace file, modifies it, and writes it
back; all validate the section exists first for a clear error.
2026-09-30 21:40:39 +02:00
Levi Neely 32e4fd0007 Make ork a pure 9P client with friendly commands
ork now operates solely as a 9P client against ork-server; all org
intelligence lives in the server. Remove the local file-parsing command
layer (commands.rs) and its org-ast/org-parser/orkmode-core/serde deps.

Base verbs speak raw 9P: ls, read, write, rdwr. Address resolves from
--addr, then $ORK_ADDR, then tcp://localhost:5640.

Add friendly commands, all thin compositions of the base 9P operations:
docs, sections, todos, agenda, get, set, add (--parent), mkdoc, reload.

Fix the directory-listing parser in client.rs: the stat-entry name offset
double-counted the size field (pos + 2 + 41 instead of pos + 41), so ls
returned nothing. Handle a downstream reader closing early (e.g. | head)
by treating BrokenPipe as a clean exit instead of panicking.
2026-09-30 21:29:09 +02:00
Levi Neely 623aef4f42 Fix phantom namespace nodes and child section insertion
Dynamic child resolvers returned Some for any name, fabricating phantom
document and section directories so walks to nonexistent paths appeared
to succeed. Validate existence and return None instead:
- root resolver checks get_doc
- document resolver checks new OrkState::section_exists
- section resolver checks child_ids

Also fix create_child_section, which corrupted the org file:
- find_section_end used the parent's headline-end offset, inserting the
  child before the parent's PROPERTIES/ID drawer and detaching its ID.
  Use section.span.end, which covers planning, drawer, body, children.
- The child headline was written verbatim; demote it to parent.level + 1
  via the new restar helper.

Add regression tests for restar and child section nesting.
2026-09-30 21:28:52 +02:00
Levi Neely 17f4caa37f Add write handlers for section properties
- set_section_keyword/priority/title/tags
- set_section_scheduled/deadline/closed
- set_section_properties/body
- rebuild_headline helper for atomic headline updates
- Fixed replace_headline to preserve DEADLINE/:PROPERTIES: lines
- Clear read cache on write in 9P server

All writes tested working via 9p client.
2026-09-30 16:09:44 +02:00
Levi Neely 94862b7f26 Add 9P client to ork-cli
Simple 9P2000 client supporting:
- Connect via Unix socket or TCP
- read/write/rdwr operations
- Directory listing

Preparation for reworking CLI to use 9P server.
2026-09-30 15:11:10 +02:00
Levi Neely 9e2fcb9010 Add 9P2000 server implementation
- p9.rs: Full 9P2000 protocol handler
  - Tversion/Tattach/Twalk/Topen/Tread/Twrite/Tclunk/Tstat
  - Directory listing with stat encoding
  - File read/write with buffering
  - Rdwr support for command files

- ork-server binary:
  - TCP or Unix socket listener
  - Threaded connection handling
  - Usage: ork-server -a unix:///tmp/ork.sock ~/org

Tested with plan9port 9p client - read/write/ls all working.
2026-09-30 15:09:59 +02:00
Levi Neely 96f89acd65 Add complete section properties to 9P namespace
Section files now include:
- level (read-only): headline level 1-6
- scheduled: SCHEDULED timestamp
- deadline: DEADLINE timestamp
- closed: CLOSED timestamp
- properties: key=value lines from property drawer
- body: section content (text between headline and first child)

All timestamp fields use org-mode format: <YYYY-MM-DD Day HH:MM +repeat>
2026-09-30 14:16:18 +02:00
Levi Neely 6bb2994dff Add section creation with UUID IDs
- new file at root: create org file
- new file at doc level: create top-level section
- new file at section level: create child section
- All created sections get UUID in :ID: property
- body file added to doc (alias for raw)
- Test for section creation via 9P namespace
2026-09-30 10:41:19 +02:00
Levi Neely 246bd9bb5a Add ork-server crate with virtfs EDSL
- virtfs: Declarative filesystem EDSL (port of ollie's pattern)
  - dir(), file(), each(), dir_dynamic() constructors
  - read(), write(), rdwr(), rw() handlers
  - FsNode with walk(), list(), read(), write()

- state: OrkState manages parsed documents
  - Scans org directory, parses on load
  - Section ID from CUSTOM_ID > ID > slugified title
  - Read/write document content

- namespace: 9P namespace builder
  - ctl, idx, keywords at root
  - agenda/todos, agenda/today, agenda/week
  - {doc}/ with raw, meta, and sections
  - {section}/ with headline, title, keyword, priority, tags
  - Nested children for subsections
2026-09-30 10:31:11 +02:00
Levi Neely 0c5f56aad9 Revise 9P design: nested hierarchy, EDSL sketch
- Sections nest as directories mirroring org structure
- One file per property (title, keyword, tags, etc.)
- ID from CUSTOM_ID > ID > slug+hash
- EDSL pattern from ollie's virtfs for declarative namespace
2026-09-30 10:25:47 +02:00
Levi Neely 030ac3beeb Add 9P server design doc (draft)
Documents design options for:
- Section hierarchy (nested vs flat)
- Property granularity (per-file vs structured)
- Change notification
- Write semantics
- Agenda/query views

Needs decisions before implementation.
2026-09-30 10:05:18 +02:00
Levi Neely 7e24a9cf02 Implement property/tag writes, timestamp formatting, add tests
- property command: set/delete properties in property drawer
- tag command: add/remove tags on headlines
- Timestamp formatting: agenda shows actual dates instead of 'set'
- Added 17 unit tests for parser (headlines, TODOs, tags, planning, etc.)
2026-09-30 09:52:14 +02:00
Levi Neely 320fda7acc Add Makefile
Targets: build, install, uninstall, test, check, fmt
Installs to ~/.local/bin by default
2026-09-30 09:46:41 +02:00
Levi Neely 6f3cc0578d Add VISION.md and AGENTS.md
VISION.md: Project goals, design principles, architecture, milestones
AGENTS.md: AI agent guidelines for working on the codebase
2026-09-30 09:45:38 +02:00
Levi Neely 14fc3233e9 Initial commit: org-mode parser and CLI
- org-ast: AST types for org documents
- org-parser: tree-sitter based parser
- orkmode-core: library + C FFI
- ork-cli: directory-aware CLI tool

CLI operates on directories by default, supports -R for recursive.
2026-09-30 09:43:21 +02:00