mandal-org.el shows one Org file as a hierarchical 3x3 mandala memo.
The data remains ordinary Org headings in one .org file. The mandala
buffer is a read-only XPM-framed view for selecting cells and moving
through the hierarchy; editing happens in the normal full Org buffer.
(use-package mandal-org
:vc (:url "https://github.com/askdkc/mandal-org" :rev :newest)
:bind (("C-c m n" . mandal-org-open-or-create))
:config
(setopt mandal-org-directory "~/notes/mandala/"))git clone https://github.com/askdkc/mandal-org ~/.emacs.d/site-lisp/mandal-org(use-package mandal-org
:load-path "~/.emacs.d/site-lisp/mandal-org"
:bind (("C-c m n" . mandal-org-open-or-create))
:config
;; Where new mandala files are created and searched.
(setopt mandal-org-directory "~/notes/mandala/")
;; Optional: auto-refit the view after a window resize settles (off by default).
(setopt mandal-org-auto-refresh-on-window-size-change t)
;; Optional: cell edit popup height (fraction of the frame, or a line count).
(setopt mandal-org-edit-popup-height 0.4))(add-to-list 'load-path "~/.emacs.d/site-lisp/mandal-org")
(require 'mandal-org)Loading the package binds C-c m n globally to
mandal-org-open-or-create, and C-c C-v m in Org buffers to
mandal-org-view.
The quickest entry point is:
C-c m n mandal-org-open-or-createThis prompts with completion over the mandala files already in
mandal-org-directory:
- Pick an existing file to open it as a mandala.
- Type a new central keyword to create a brand new mandala. The file is
written into
mandal-org-directorywith a Denote-like name, e.g.20240115T143022--central-keyword.org.
Set the storage directory with :config / Customize:
(setopt mandal-org-directory "~/notes/mandala/")You can also create a file at an explicit path with M-x mandal-org-new,
or open the bundled tutorial sample directly:
M-x mandal-org-tutorialA new file holds only the minimal Org data: title metadata plus one
top-level heading with an ID. The eight empty surrounding cells are not
created until you edit one of them.
To open an existing Org mandala file, visit the file in org-mode and run:
M-x mandal-org-viewIf point is before the first heading, the first heading is used as the center. Otherwise the heading at point is used, unless you are returning from an edit session started in the mandala view.
| Key | Action |
|---|---|
h | select west cell |
j | select south cell |
k | select north cell |
l | select east cell |
y | select northwest cell |
u | select northeast cell |
b | select southwest cell |
n | select southeast cell |
c, . | move selection back to the center cell |
C-n | select next cell, clockwise from center (wraps) |
M-n, C-p | select previous cell, counterclockwise (wraps) |
e | edit selected cell in a popup (create if empty) |
RET, C-d | enter selected existing cell as the next center |
DEL, ^, C-u | go to the parent mandala |
g | redraw from the current Org buffer contents |
q | quit the view and return to the Org buffer |
The view always shows one 3x3 grid. The middle cell is the current center; the eight cells around it are the center’s direct child headings:
nw | n | ne ---+----+--- w | C | e ---+----+--- sw | s | se
Selecting only moves the highlight; it does not change the center. There are three ways to move the selection:
- Directional keys map to compass positions:
hwest,least,knorth,jsouth, and the diagonalsynw,une,bsw,nse. C-nsteps clockwise andM-n(orC-p) steps counterclockwise through every cell. From the center the clockwise order isC s sw w nw n ne e seback toC, wrapping at the ends.cor.jumps the selection back to the center.
Select a cell that already exists, then press RET (or C-d). That child
becomes the new center and its eight children fill the surrounding
cells. This is the recursive Mandal-Art expansion: every cell can become
the middle of its own 3x3 grid. Pressing RET on an empty cell does
nothing (create it with e first).
Press DEL, ^, or C-u to move to the parent mandala, i.e. make the
current center’s parent heading the center again. Repeat to climb back toward the
top-level theme. At the top there is no parent, so it reports “Already at
the top mandala”.
Press e on the selected cell to edit it (see below). On an empty
surrounding cell, e first creates the child heading at that position,
then opens it. After saving, the view refreshes in place.
g redraws from the current Org contents (use it after resizing the
window or after external edits). q closes the view and returns to the
source Org buffer.
Pressing e opens a popup window at the bottom of the frame showing
that cell’s heading and its direct body as ordinary Org text (it is an
indirect buffer narrowed onto the source file, so you are editing the
real data). Take notes in Org format, then:
| Key | Action |
|---|---|
C-c C-c | save the file, close the popup, refresh the view |
C-c C-k | close the popup without writing the file |
The save shortcut is also shown in the popup’s header line. Selecting an
empty cell and pressing e first creates the child heading (with ID
and MANDALA_POS) and then opens it for editing. The popup height is
controlled by mandal-org-edit-popup-height.
Each real mandala cell is one Org heading. The current center is a heading, and its direct children are the eight possible surrounding cells.
Surrounding cells use MANDALA_POS on the child heading:
** マンダラは人間の基本機能
:PROPERTIES:
:ID: basic-function
:MANDALA_POS: s
:END:
Valid positions are:
nw n ne w e sw s se
The display position does not depend on heading order. Duplicate
MANDALA_POS values under the same parent, or invalid position values,
raise a user-error instead of producing an ambiguous view.
When editing an empty cell, a new child heading is created at the correct
level with ID and MANDALA_POS. If you cancel editing and leave the
title empty, the MVP keeps it as an existing untitled cell.
The XPM cell preview includes the heading title and the beginning of the heading’s direct body. It excludes property drawers, other drawers, source/example block contents, and child heading contents. Width and line count are controlled by:
In the XPM view, heading titles are rendered with a heavier weight and
body preview lines are rendered slightly smaller. If body text was
accidentally inserted before the property drawer, mandal-org still
detects the cell’s MANDALA_POS so reopening the same cell does not
create another node at the same position. Duplicate positions that already
exist under the same parent must still be fixed in the Org file.
mandal-org-cell-preview-lines
mandal-org-cell-preview-width
mandal-org-svg-width
mandal-org-svg-height
mandal-org-fit-window
mandal-org-auto-refresh-on-window-size-change
mandal-org-window-size-change-delayBy default the XPM view uses the fixed mandal-org-svg-width and
mandal-org-svg-height values. The default fixed size is intentionally
modest to avoid expensive XPM redisplay during frame resizing. Set
mandal-org-fit-window to non-nil to fit the view to the visible Emacs
window when the view is drawn, then press g in the mandala view to
redraw manually after resizing.
By default no window resize hook is installed, so resizing the frame
never triggers a redraw; press g when you are done. If you want the
view to refit itself automatically, opt in with:
(setopt mandal-org-auto-refresh-on-window-size-change t)This is safe for live resizing: nothing is rendered while you drag a
frame edge. A single redraw runs only after resizing has been idle for
mandal-org-window-size-change-delay seconds (default 0.18), so the
expensive XPM redraw never happens mid-drag and cannot make Emacs
unresponsive. Use setopt or Customize (not setq) so the resize hook
is installed or removed automatically.
When the mandala view window becomes extremely small, mandal-org does
not attempt XPM rendering and shows a short resize message instead. This
prevents heavy redisplay loops on tiny windows; enlarge the window and
press g to render again.
This MVP does not edit text directly inside the XPM view. Mouse selection is
not guaranteed. The view buffer is not saved as a file; only the Org
source file is persistent. Only direct children of the current center are
shown; grandchildren appear only after entering a cell. Duplicate or
invalid MANDALA_POS values under one parent are rejected with a
user-error and must be fixed in the Org file.
Run the ERT suite from the repository root:
emacs -Q --batch -L . \
-l ert \
-l mandal-org.el \
-l mandal-org-tests.el \
-f ert-run-tests-batch-and-exit