Event listeners
You can dispatch events very easily. Use EventTarget.dispatchEvent(new CustomEvent("eventName")).
For example, to reboot the system, use window.dispatchEvent(new CustomEvent("ui:desktop.reboot"))
Use on as a reactive object property inside a plan layout node.
{
tag: "button",
text: "save",
on: {
click(e, el) {
// ...
},
},
}
on supports normal DOM events and adds shortcut parsing, delegation, and cleanup options.
Basic shape
{
on: {
click(e, el) {},
keydown(e, el) {},
},
}
Handler arguments:
e: event objectel: current element bound by this layout node- delegated handlers also receive
target(matched child), see delegation section
If a handler returns false, default behavior is prevented and propagation is fully stopped.
Multiple events in one key
Use || to bind the same handler to multiple event names:
{
on: {
"focus || blur"(e, el) {
// runs for both events
},
},
}
|| is parsed with optional spaces, so "a||b" and "a || b" are equivalent.
Item options
Inside a listener item you can use:
selector: delegate to descendants matching selectorrepeatable: allow repeated keyboard events (keydownauto-repeat)disrupt: shorthand for prevent + stop + stopImmediatestop: shorthand for stop + stopImmediateprevent: shorthand for preventDefaultpreventDefault,stopPropagation,stopImmediatePropagation- standard listener options:
capture,once,passive,signal
disrupt, stop, and prevent are normalized to the explicit DOM cleanup flags.
Delegation with selector
{
on: {
selector: ".item",
click(e, target, el) {
// target: closest ".item"
// el: node owning this on-map
},
},
}
If selector contains :scope, it is normalized against the owner element.
Shortcut syntax
Any on key can be a keyboard shortcut expression.
{
on: {
"Ctrl+S"(e) {},
"Ctrl+K Ctrl+C"(e) {},
"Ctrl+S || Meta+S"(e) {},
},
}
The parser supports three concepts:
- aliases: human-friendly key names converted to canonical key names
- sequences: ordered steps separated by spaces
- combinations (chords): keys/events that must match together at one step
Aliases (full list)
These names are remapped automatically:
Ctrl->ControlDown->ArrowDownLeft->ArrowLeftRight->ArrowRightUp->ArrowUpAltGr->AltGraphDel->DeleteEsc->Escape
So "Ctrl+S" and "Control+S" are equivalent.
Sequences
A sequence is a chain of steps separated by spaces.
{
on: {
"Ctrl+K Ctrl+C"(e) {
// step 1 then step 2 in order
},
},
}
Sequence behavior:
- each step must match in order
- mismatch resets sequence progress
Combinations (chords)
A combination is built with + inside a step.
{
on: {
"Control+Shift+KeyP"(e) {},
},
}
Chord behavior:
- keys are checked against
e.key(lowercased) ore.code - code-style tokens like
KeyA,Digit1,Numpad0,ShiftLefttargete.code - if any part fails, chord state resets
Built-in code tokens (always compared to e.code):
ShiftLeftShiftRightControlLeftControlRightAltLeftAltRightMetaLeftMetaRightSpaceSemicolonEqualCommaMinusPeriodSlashBackquoteBracketLeftBackslashBracketRightQuote
Alternatives with ||
Use || for OR branches:
{
on: {
"Ctrl+S || Meta+S"(e) {
// either branch triggers
},
},
}
Each side is parsed as its own full sequence/chord path.
Event token vs key token
A token is treated as a raw event name (not key) when:
- length is greater than 1, and
- it is fully lowercase, or exactly
DOMContentLoaded
Examples:
"click"-> event listener forclick"DOMContentLoaded"-> event listener forDOMContentLoaded"Enter"-> key token (key: "enter")"Return"-> special case for Enter (key: "enter",code: "Enter")
+ parsing note:
"Ctrl++"meansControl++key++is interpreted as a literal plus token
Repeat behavior
Keyboard handlers ignore auto-repeat by default (e.repeat is filtered).
Set repeatable: true to allow repeated keydown firing.
Focus behavior for keyboard shortcuts
Keyboard shortcuts ensure the owner element is focusable when needed.
Internally this may apply tabIndex: -1 so keyboard capture can work reliably.
Practical examples
click + delegated click
{
tag: "ul",
on: {
selector: "li",
"click || keydown"(e, target, el) {
// click/keydown on list items
},
},
}
save shortcut with alternatives
{
on: {
prevent: true,
"Ctrl+S || Meta+S"(e) {
// save action
},
},
}
sequence shortcut
{
on: {
disrupt: true,
"Ctrl+K Ctrl+D"(e) {
// run command palette action chain
},
},
}
Notes
- keep shortcut tokens explicit when possible (
ControloverCtrl) for readability - use
preventordisruptfor browser-reserved shortcuts like save/find
Event reference
Events can be listened to through plan's on or through EventTarget.addEventListener.
ui:desktop.reboot
Reboots the system
ui:menu.items
Fires when a menu is opened (typically a context menu).
This is useful for extending explorer and adding entries to the context menu.
The "opener" is in e.target.openerEl
ui:explorer.navigate
Fires when explorer changes the active directory
ui:folder.nonexistant
Most likely indicates fileindex corruption