TOML Guide

TOML configuration guide for Tool-Tree

This guide is for Tool-Tree, the component that powers the function menu in the com.tool.tree Android app. All page configuration files must use TOML (XML is no longer supported). The parser lives in com.omarea.krscript.config.PageConfigReader and uses the org.tomlj:tomlj library.

A TOML configuration file describes the node tree of a page: each page is a list of nodes (group, action, switch, picker, text, page, editor, download, resource, menu, fab) displayed in the order they appear in the file. All nodes are declared flat at the top level of the document (dot-notation nesting like [[group.action]] has been removed) - a node that follows a [[group]] (and precedes the next one) automatically becomes a child of that group.

2. Syntax rules

2.1. Always use double brackets [[name]] for every entry

TOML does not allow mixing [name] (single brackets, single table) and [[name]] (double brackets, array of tables) for the same key at the same position - this will cause a parse error. To be safe, always use [[name]] for any entry, even if there is currently only one entry of that type. This prevents breaking the file later when you add a second entry of the same type but forget to switch the brackets.

Wrong: declaring [group] and then later declaring another [[group]] in the same file -> parse error.
Right: always use [[group]], [[action]], etc. for all entries.

2.2. Node type = TOML table name

There is no separate type field. A node's type is determined by its TOML table name:

Table nameNode typeDescription
groupContainerHolds child nodes - the flat entries declared after it become its children (no nesting)
pageSub-pageOpens another page when clicked
actionActionRuns a shell script
switchToggleOn/off switch with get/set shell
pickerPickerSingle/multi value selector
textRich text blockMulti-row text (bold/italic/link/photo...)
editorFile editorOpens a file in the built-in editor
downloadDownloadDownloads a file, then runs a script
resourceAsset extractionExtracts assets from APK (invisible)
menuOverflow menuToolbar 3-dot menu container
fabFloating buttonFAB container on the page

2.3. Group children = nearest preceding [[group]] (dot-notation removed)

All node types are declared as flat [[type]] entries at the top level of the document - the dotted [[group.action]] / [[subgroup.type]] style is no longer supported. A child node simply belongs to the most recently declared [[group]] above it in the file; the parser walks the document once, in line order, and assigns each entry to the current group.

[[group]]
title = "CPU"

[[switch]]        # child of group "CPU"
...

[[action]]        # also a child of group "CPU"
...

[[group]]
title = "Battery"

[[switch]]        # child of group "Battery"
...
Deprecated: entries nested via dotted paths such as [[group.action]] are silently ignored - the parser only reads node tables at the top level of the document (tomlChildren() is called exactly once, on the root table, and is no longer recursive per group).
Reserved name: [[toml]] is not a node type. It is only a marker used to recognize inline TOML output from config-sh (see section 6). If it appears in a file, the parser skips it harmlessly.

2.4. Display order = position in file

Display order is always top-to-bottom by the position of the [[name]] entry in the file, regardless of node type (group, action, page, text, switch ...) - even when types are interleaved (e.g. an action, then a page, then another action). The position is read directly from the tomlj API, so there is no need for an order field.

The parser uses TomlArray.inputPositionOf(index).line() to obtain the line number of each array element. If the API returns null (due to error or unsupported feature), the parser falls back to read order (seq) - it never crashes.

2.5. Boolean values - 3 accepted forms

Boolean fields such as confirm, readonly, auto-off accept:

ValueResultNotes
true / 1trueStatically true
false / 0falseStatically false
(any other string)Run as shell, "1" => true, otherwise => falseshell via resolveBoolOrShell()

Example: readonly = "test -f /sdcard/lock && echo 1" - the parser runs that command, and if it returns "1" then readonly = true.

Note: where the shell result only decides layout (support/visible of nodes and params, show of actions), the script runs immediately during parsing. Deferred fields - switch/picker/toggle get, row support, and all *-sh dynamic strings (title-sh, desc-sh, summary-sh, warn-sh) - are queued into pending states and batched at the end (see section 20).

2.6. String resource references

Any text field (title, desc, summary ...) may include a reference to the app's string resources via @string/name or @string:name. The parser resolves these automatically. If the resource is not found, the original string is kept.

[[group]]
title = "@string/group_battery_title"

[[switch]]
title = "@string/switch_fast_charge_title"
desc = "@string:switch_fast_charge_desc"
get = "getprop sys.fastcharge"
set = "setprop sys.fastcharge $state"
Both @string/name and @string:name point to the same resource lookup - use whichever separator you prefer, they are interchangeable.

3. Node type overview

Quick reference for all node types. Click a name to jump to its detailed section.

4. Shared fields (NodeInfoBase + ClickableNode + RunnableNode)

These are the fields that every node has (inherited from NodeInfoBase). All text fields accept @string/... references.

4.1. NodeInfoBase (present on every node)

FieldAliasTypeDefaultDescription
title-String""Display title. Accepts @string/...
title-sh-String""shell Script that produces title dynamically. Runs during parse; overrides title
desc-String""Short description shown below title
desc-sh-String""shell Script that produces desc dynamically
summary-String""Extra info shown in small grey text
summary-sh-String""shell Script that produces summary dynamically
keyindex, idStringauto UUIDUnique ID. Required for Desktop shortcuts. If key starts with @, allowShortcut defaults to false
supportvisibleBool|ShelltrueHide/show this node. Accepts true/false or a shell returning "1"

4.2. ClickableNode (for page, action, switch, picker, editor, download)

FieldAliasTypeDescription
iconicon-pathStringIcon path shown on the left of the item
logologo-pathStringLarge icon used when creating a shortcut (different from small icon)
photophoto-pathStringLarge image shown in the detail dialog
photo-real-sizephoto-original-sizeBooltrue: show image at its real size, no stretching
photo-gif-numgif-num, gif_numInt>0: image is an animated GIF (photo_1.png, photo_2.png...)
photo-gif-timegif-time, gif_timeIntTime per frame (ms; default 300)
photo-gif-autoplaygif-autoplay, gif_autoplayBooltrue (default): auto-play the GIF
photo-gif-loopphoto-gif-loop-count, gif-loop, gif-loop-count, gif_loop_countIntLoop count (0 = infinite)
icon-gif-numicon-gif_numIntSame as photo-gif-num but for the small icon
icon-gif-timeicon-gif_timeIntTime per frame for the icon
icon-gif-autoplayicon-gif_autoplayBoolAuto-play icon GIF
icon-gif-loopicon-gif-loop-countIntLoop count for the icon GIF
bgbg-pathStringBackground image for the item/dialog
locklock-stateStringLock the item (no interaction allowed). true/1 = locked; or the format "state|message", e.g. lock = "1|Requires root" - the message is shown when the user clicks the locked item
lock-sh-Stringshell Script that checks the lock state dynamically (returns "1" = locked)
min-sdksdk-minIntMinimum Android SDK version
max-sdksdk-maxIntMaximum Android SDK version (default 100)
target-sdksdk-targetIntTarget Android SDK version
allow-shortcut-Bool?Allow creating a shortcut. Default null (auto). Forced false when key starts with @

4.3. RunnableNode (for action, switch, picker, download)

FieldAliasTypeDescription
confirm-BoolAsk for confirmation before running
warnwarningStringWarning text shown in the confirmation dialog
warn-shwarning-shStringshell Script that produces warning dynamically
auto-offauto-closeBoolAuto-close the log dialog after running
auto-finish-BoolAuto-close the page after running
auto-kill-BoolAuto-kill related processes
auto-restart-BoolAuto-restart the service
interruptibleinterruptableBoolAllow interrupting mid-run
need-inputneeds-input, require-inputBoolScript uses read to receive keyboard input
reload-page-BoolReload the whole page after running
reload-Bool|Stringtrue = reload page; or a comma-separated list of block IDs to refresh only those blocks
shell-StringInteraction mode, NOT the script content: default (log dialog) / bg-task (background, no dialog) / hidden. Use script to declare the script itself
bg-taskbackground-task, async-taskBoolRun in background (no log dialog) - equivalent to shell = "bg-task"
scriptset, setstateStringMain script for action/download; set-state script for switch/picker (receives $state)
Refreshing the page: use reload-page = true or reload = true to make the app reload the whole page after a script finishes (e.g. after toggling a switch, to refresh the summary). To refresh only specific blocks (e.g. only the affected switches), use reload = "id1,id2".

5. [[group]] - Container for child nodes

[[group]] is a container that groups related nodes. It has no icon and is not clickable - it's just a title with a list of child nodes below it.

5.1. Group fields

FieldAliasTypeDefaultDescription
title-String""Group title (displayed uppercase, grey)
title-sh-String""shell Script that produces title
keyindex, idStringautoUnique ID
supportvisibleBool|ShelltrueHide/show the group - a hidden group ALSO hides all of its children (every entry until the next [[group]] is skipped, their shells are not run). Accepts shell too!

5.2. Demo

Demo (Android UI)
<Settings
CPU
Optimize CPU
Set governor to performance
Governor
performance
Memory
🗑
Clear cache
[[group]]
title = "CPU"

[[action]]
title = "Optimize CPU"
desc = "Set governor to performance"
script = "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"

[[picker]]
title = "Governor"
get = "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
set = "echo $state > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"

[[group]]
title = "Memory"

[[action]]
title = "Clear cache"
script = "sync; echo 3 > /proc/sys/vm/drop_caches"

5.3. Grouping rules (no nesting)

Since dot-notation was removed, groups can no longer be nested inside each other. Every [[group]] is a top-level sibling: declaring a new [[group]] closes the previous one, and all following entries belong to the new group. Entries declared before the first [[group]] are rendered directly on the page without a group header.

Groups also don't support inline resource-file/resource-dir fields (see section 15) and have no desc/summary - a group is just a title + its children.

6. [[page]] - Sub-page

[[page]] is not an action - it's a link to another page. Clicking it opens a new page (another .toml file, an HTML page, or another Activity).

6.1. Page fields

FieldAliasTypeDescription
config-StringPath to the sub-page .toml file (relative or absolute)
config-sh-Stringshell Script returning inline TOML content or a .toml file path. Auto-detected by the [[group]] header
html-StringOnline HTML URL - opens in a WebView
linkhrefStringURL opened by the system browser
activitya, intentStringActivity intent to launch (e.g. com.example.MyActivity)
before-loadbefore-readStringshell Script run BEFORE reading the sub-page config
after-loadafter-readStringshell Script run AFTER reading completes
load-okload-successStringshell Script run if load succeeds
load-failload-errorStringshell Script run if load fails
process-Boolnew true: show items one by one as they build (with progress bar) instead of waiting (see section 21)
locklock-stateStringStatic lock: true/1 or the "1|message" format
lock-sh-Stringshell Script to check the lock state (returns "1" = locked)
rows-ArrayRich-text rows shown below the page (see section 12)

6.2. Demo

Demo (Android UI)
<CPU
📊
App list
Manage installed apps
Advanced settings
Open external config file
[[page]]
title = "App list"
desc = "Manage installed apps"
config = "pages/app_list.toml"

[[page]]
title = "Advanced settings"
desc = "Open external config file"
config = "/sdcard/Tool-Tree/advanced.toml"
config-sh returns one of two things: either a .toml file path (output ending with .toml), or inline TOML content. Inline TOML is recognized when the 1st or 2nd non-empty line of the output starts with [[group]] or the reserved marker [[toml]] - see PageConfigSh.looksLikeInlineToml(). Anything else shows an error toast.

7. [[action]] - Action (runs a shell script)

[[action]] is the most common node type: clicking it shows a dialog (if it has confirm/params/warning), then runs the script and shows the output in a log dialog.

7.1. Action fields

FieldAliasTypeDescription
scriptset, setstateStringrequired Main shell script. Receives $param_name env vars from params
locklock-stateStringStatic lock: true/1 or the "1|message" format. When locked, the action is not clickable
lock-sh-Stringshell Script to check lock dynamically (returns "1" = locked)
menu-Boolnew true: action does NOT appear in the list; instead appears as its own icon on the toolbar
show-Bool|Shellnew true (or shell): auto-open this action's dialog when entering the page (only once)
params-ArrayList of input parameters - see section 8
rows-ArrayRich-text rows shown below the item - see section 12
params-rows-ArrayRows for the params dialog only (separate from rows which appear both in list and dialog)
+ all fields of RunnableNode, ClickableNode, NodeInfoBase (see section 4)

7.2. Demo

Demo (Android UI)
<CPU
Optimize CPU
Set governor to performance
🔥
Clear cache
Clear system cache
[[action]]
title = "Optimize CPU"
desc = "Set governor to performance"
menu = true
key = "optimize_cpu"
script = "echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"

[[action]]
title = "Clear cache"
desc = "Clear system cache"
confirm = true
warn = "This will clear all app caches"
script = "sync; echo 3 > /proc/sys/vm/drop_caches"
auto-off = true
menu = true and show = true are independent: an action can be both on the toolbar (menu=true) and auto-open its dialog (show=true). When an action moves to the toolbar (menu=true), an unset key falls back to title to use as the Menu itemId.

8. [[action.params]] - Input parameters

Each [[action.params]] defines one input field in the dialog shown when the user clicks an action. The parameter's value is passed to the action's script via the $param_name environment variable.

8.1. Basic param fields

FieldAliasTypeDescription
name-Stringrequired Variable name (unique within an action). Becomes $name in the script
title-StringDisplay label
title-sh-Stringshell Script that produces title dynamically
label-StringSecondary label
label-sh-Stringshell Script that produces label
desc-StringShort description
desc-sh-Stringshell Script that produces desc
desc-onon-desc, desc-checkedStringnew Note shown SPECIFICALLY when the checkbox/switch is ON (only for type=bool/checkbox/switch)
desc-on-shon-desc-sh, desc-checked-shStringnew shell Script that produces desc-on
type-StringInput type: text/bool/checkbox/switch/seekbar/file/folder/app/spinner/single-select/multi-select
value-StringDefault value
value-shvalue-suStringshell Script that fetches value dynamically (runs when dialog opens)
placeholder-StringHint when empty
placeholder-sh-Stringshell Script that produces placeholder
required-BoolRequired input
readonly-Bool|ShellRead-only (no editing). true/1/readonly = read-only, false/0/empty = editable; any other string is stored as a shell (readonlySh) evaluated later
maxlength-IntMax character count (type=text)
min-IntMinimum value (seekbar)
max-IntMaximum value (seekbar). Default Int.MAX_VALUE
options-ArrayList of choices (spinner/single-select/multi-select)
items-Array of StringShorthand choice list - each entry is "value|title" or just "value". Takes priority over options tables when both are declared
options-shoption-sh, options-suStringshell Script that produces options dynamically
multiple-BoolAllow multiple files/options
separator-StringValue separator (multi). Default \n
suffix-StringFile extension filter (e.g. "zip,apk"). Only type=file
mime-StringMIME type filter. Only type=file
path-homehome-pathStringInitial directory when opening file picker
editable-BoolAllow manual path entry
supportvisibleBool|ShellHide/show the param
sort-Boolnew Move readonly params to the bottom (only effective when readonly is declared)
allow-no-selectionno-selectBoolnew Allow spinner to be empty (e.g. when you need to distinguish "not selected" from "first item")

8.2. Common type values

typeUIRelated fields
textText inputvalue, placeholder, maxlength, required
bool / checkbox / switchToggle ON/OFFvalue = "1"/"0", desc-on for ON-state note
seekbarSlidermin, max, value
fileFile pickersuffix, mime, path-home, multiple, editable
folderDirectory pickerpath-home, multiple, editable
appApp pickermultiple
spinner / single-selectDropdown single choiceoptions/options-sh, allow-no-selection
multi-selectCheckbox list multi choiceoptions, multiple=true, separator

8.3. Demo

Demo - params dialog
<Set CPU max freq
Frequency (kHz)
Enter max frequency
2400000
Governor
performance
Apply to all CPUs
Config file
[[action]]
title = "Set CPU max freq"
script = """
for cpu in /sys/devices/system/cpu/cpu*/cpufreq; do
  [ "$all_cpus" = "0" ] && [ "$cpu" != "/sys/devices/system/cpu/cpu0/cpufreq" ] && continue
  echo $freq > "$cpu/scaling_max_freq"
  echo $gov > "$cpu/scaling_governor"
done
"""

[[action.params]]
name = "freq"
title = "Frequency (kHz)"
desc = "Enter max frequency"
type = "text"
value = "2400000"

[[action.params]]
name = "gov"
title = "Governor"
type = "spinner"
value = "performance"

[[action.params.options]]
title = "Performance"
value = "performance"

[[action.params.options]]
title = "Powersave"
value = "powersave"

[[action.params]]
name = "all_cpus"
title = "Apply to all CPUs"
type = "switch"
value = "1"

[[action.params]]
name = "cfg"
title = "Config file"
type = "file"
suffix = "conf"

8.4. Dependencies (depend-*)

A param can be hidden/shown (or switched to readonly) based on the value of another param. This is a large feature - see section 17 for details.

9. [[switch]] - On/off toggle

[[switch]] displays an ON/OFF toggle. When the user toggles it, the set script is called with the env var $state set to "1" or "0". When the page loads, the get script is called to read the current state.

9.1. Switch fields

FieldAliasTypeDescription
getgetstateStringrequired Script to read state. Returning "1"/"true" = ON. If omitted, the switch always shows OFF
setsetstateStringrequired Script to set state. Receives $state = "1"/"0"
locklock-stateStringStatic lock: true/1 or the "1|message" format
lock-sh-Stringshell Script to check lock dynamically (returns "1" = locked)
+ all fields of RunnableNode, ClickableNode, NodeInfoBase

9.2. Demo

Demo (Android UI)
<Display
Display
Dark mode
Enable dark UI
Auto brightness
[[group]]
title = "Display"

[[switch]]
title = "Dark mode"
desc = "Enable dark UI"
get = "cmd uimode night get | grep -q yes && echo 1"
set = "cmd uimode night $([ \"$state\" = \"1\" ] && echo yes || echo no)"

[[switch]]
title = "Auto brightness"
get = "settings get system screen_brightness_mode"
set = "settings put system screen_brightness_mode $state"
The parser does NOT run get immediately when parsing each switch. Instead, all get scripts of switches/pickers are queued into pendingSwitchStates and run exactly once at the end of readConfigToml() via resolvePendingStates() - reducing N shell round-trips to 1 (see section 20).

10. [[picker]] - Value selector

[[picker]] displays a current value; clicking it opens a popup to choose one (or more) values from a list. When the user confirms, the set script is called with $state = the chosen value.

10.1. Picker fields

FieldAliasTypeDescription
getgetstateStringrequired Script to read the current value
setsetstateStringrequired Script to set the value. Receives $state
options-ArrayStatic options list - [[picker.options]] with title/value
option-shoptions-sh, options-suStringshell Script that produces options dynamically. Each line value|title or just value
multiple-BoolAllow selecting multiple values
separator-StringSeparator for multi-select values (default \n)
locklock-stateStringStatic lock: true/1 or the "1|message" format
lock-sh-Stringshell Script to check lock dynamically (returns "1" = locked)
+ all fields of RunnableNode, ClickableNode, NodeInfoBase

10.2. Demo

Demo (Android UI)
<CPU
Governor
performance
Max freq
2400000 kHz
[[picker]]
title = "Governor"
get = "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
set = "echo $state > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"

[[picker.options]]
title = "Performance"
value = "performance"

[[picker.options]]
title = "Powersave"
value = "powersave"

[[picker]]
title = "Max freq"
get = "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
set = "echo $state > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
option-sh = "cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies | tr ' ' '\\n'"
When a picker has both static [[picker.options]] and dynamic option-sh, the final list is the union of both (static options first, dynamic options appended after running the shell).

11. [[text]] - Rich text block

[[text]] is a non-clickable display block containing multiple rich-text rows. Use it for notices, instructions, or inline image/gif blocks.

11.1. Text fields

FieldAliasTypeDescription
rows-Arrayrequired Rich-text rows - see section 12
+ NodeInfoBase fields (title, title-sh, desc, desc-sh, summary, support...)

11.2. Demo

Demo (Android UI)
<Notice
CAUTION
The actions below may affect the system.
$ run backup --full
See more at: example.com/docs
[[text]]
title = "Notice"

[[text.rows]]
text = "CAUTION"
bold = true

[[text.rows]]
text = "The actions below may affect the system."

[[text.rows]]
line = true

[[text.rows]]
text = "  $ run backup --full"
monospace = true

[[text.rows]]
text = "See more at: example.com/docs"
italic = true

12. [[text.rows]] - Rich text row

This is the most common display component: used in [[text]], [[action]], [[page]], [[download]] and [[action.params-rows]]. Each row is a single line of text that can have style, icon, toggle, photo, etc.

12.1. Row fields

FieldAliasTypeDefaultDescription
text-String""Text content
shtext-shString""shell Script that produces text dynamically
boldbBoolfalseBold
italiciBoolfalseItalic
underlineuBoolfalseUnderline
strikethroughline-through, delete-line, delBoolfalseStrikethrough
monospacemono, codeBoolfalseMonospace font (for code/log)
letter-spacingletterspacing, spacingFloat0Letter spacing (em units)
line-heightlineheight, row-heightFloat0Line height multiplier (1.5 = +50%)
margin-topspacing-top, top-marginInt0Top margin (dp)
margin-bottomspacing-bottom, bottom-marginInt0Bottom margin (dp)
alphaopacityFloat-1Opacity 0.0-1.0 or 0-255
foregroundcolorString-1Text color (e.g. #FF0000)
bgbackground, bgcolorString-1Background color
size-Int-1Font size (sp)
break-BoolfalseLine break after this row
linedivider, separatorBoolfalseDraw a horizontal divider before this row
align-Stringnormalnormal/center/opposite
linkhrefString""URL to open on click
activitya, intentString""Intent to open on click
photophoto-pathString""Large image (own line, separate from text)
photo-real-sizephoto-original-sizeBoolfalseShow image at real size
photo-gif-numgif-numInt0>0: image is animated (photo_1.png...)
photo-gif-timegif-timeInt300Time per frame (ms)
photo-gif-autoplaygif-autoplayBooltrueAuto-play the GIF
photo-gif-loopphoto-gif-loop-count, gif-loop, gif-loop-countInt0Loop count (0=infinite)
iconicon-pathString""new INLINE small icon (same line, different from photo)
icon-positionicon-posStringbeforebefore / after the text
icon-size-Int0Icon size (dp)
scriptrunString""shell Script to run when the row is clicked
toggletoggle-typeString""checkbox/switch = show a small toggle next to the row
getgetstateBool|ShellfalseToggle initial state. A shell returning exactly "1" = checked (queued into pending states - see section 20)
setsetstateString""shell Script run when the user toggles (receives $state = "1"/"0")
supportvisibleBool|ShelltrueHide/show the row

12.2. Demo

Demo (Android UI)
<Info
Device info
Model: Pixel 7
Android: 14
ro.build.fingerprint=google/raven/...
Updated 2024-08-26
Show notifications
Display system notifications
[[text]]
title = "Device info"

[[text.rows]]
text = "Device info"
bold = true

[[text.rows]]
line = true

[[text.rows]]
text = "Model: Pixel 7"

[[text.rows]]
text = "Android: 14"

[[text.rows]]
text = "ro.build.fingerprint=google/raven/..."
monospace = true

[[text.rows]]
line = true

[[text.rows]]
text = "Updated 2024-08-26"
align = "center"
italic = true

[[text.rows]]
line = true

[[text.rows]]
text = "Show notifications"
toggle = "switch"
get = "settings get global heads_up_notifications_enabled"
set = "settings put global heads_up_notifications_enabled $state"
sh (alias text-sh) of a row is NOT queued into the pending-state system - it is left to the UI layer and evaluated when the row is rendered. get with a shell value IS queued into pendingRowCheckedStates and batched at the end - like switch/picker.

13. [[editor]] - Open file in the text editor

[[editor]] lets the user open a file in the built-in text editor (TextEditorActivity) to view or edit it. If the file does not exist, the editor creates it when the user saves.

13.1. Editor fields

FieldAliasTypeDefaultDescription
filepathString""required Path of the file to open
wrap-BooltrueEnable line wrapping
placeholder-StringnullHint when file is empty
readonly-Bool|ShellfalseRead-only. Accepts shell too!
need-input-BoolfalseWhether the run script uses read
value-String""Initial content (only when file does not exist)
value-sh-String""shell Script that produces initial content (higher priority than value)
+ ClickableNode fields (icon, lock, min-sdk...) and NodeInfoBase (title, desc...)

13.2. Demo

Demo (Android UI)
<CPU
📝
Edit build.prop
/system/build.prop
📝
Create new script
/sdcard/myscript.sh
[[editor]]
title = "Edit build.prop"
desc = "/system/build.prop"
file = "/system/build.prop"
readonly = "test -w /system/build.prop || echo 1"

[[editor]]
title = "Create new script"
desc = "/sdcard/myscript.sh"
file = "/sdcard/myscript.sh"
value = "#!/system/bin/sh\necho hello"
value is only written when the file does not exist. If the file already exists, value/value-sh are ignored - the existing file content is preserved.

14. [[download]] - Download a file via HTTP

[[download]] is a new node type that displays download progress directly in the item (no separate dialog). When download completes, it runs the script with the env var $state = path of the downloaded file (cached, random name). The URL can be static (url) and/or dynamic (url-sh).

14.1. Download fields

FieldAliasTypeDescription
url-StringStatic URL (http/https) to download. Required if url-sh is not set
url-sh-Stringshell Script that returns the URL to download. Runs once, the first time the item is tapped - the result is cached into url for the rest of that page visit (pause/resume/retry reuse the cached value, the script is not re-run). Required if url is not set; if both are set, the result of url-sh overwrites url on that first tap
scriptset, setstateStringScript run after download. Receives $state = path of downloaded file
locklock-stateStringStatic lock: true/1 or the "1|message" format
lock-sh-Stringshell Script to check lock dynamically (returns "1" = locked)
rows-ArrayRich-text rows shown below the item - see section 12
+ all RunnableNode fields: confirm, warn, reload, auto-finish...

14.2. Demo

Demo (Android UI)
<Update
Download update
update.zip - 24 MB / 53 MB
45%
[[download]]
title = "Download update"
url = "https://example.com/update.zip"
script = "unzip -o $state -d /sdcard/update && echo Update installed"

15. [[resource]] - Extract assets

[[resource]] is an invisible node (it returns null after initialization). It only extracts assets from the APK to storage (typically /data/data/<pkg>/files/... or cache). This is how to ship script files, images, etc. from the APK to outside so shell can call them.

You can also put resource-file/resource-dir fields directly inside [[page]]/[[action]]/[[switch]]/[[picker]]/[[text]] - no need to declare a separate [[resource]] entry. (Not available on [[group]].)

15.1. Fields

FieldAliasTypeDescription
resource-file-StringName of a single asset file to extract
resource-dir-StringName of an asset directory to extract entirely
resources-ArrayList of more complex resources - each entry is a table with file and/or dir
[[resource]]
resource-file = "busybox"

[[resource]]
resource-dir = "scripts"

# Or inline on the node that needs it - no separate [[resource]] entry required:
[[action]]
title = "Run bundled script"
resource-file = "run.sh"
script = "sh /data/data/com.tool.tree/files/run.sh"
Extraction is de-duplicated in memory (per app process): each asset name is written only once, repeated declarations reuse the first extracted path. Note that for a node carrying resource-* fields, support = false skips the whole node - its resources are not extracted either.

17. Dependencies (depend-*)

This is the largest feature of [[action.params]]: a param can be hidden/shown (or switched to readonly) based on the value of one (or more) other params in the same action. This is how to build complex dialogs that are still easy to manage - the UI auto-hides irrelevant fields when the user picks a different mode.

17.1. depend-* fields (only for [[action.params]])

FieldTypeDefaultDescription
depend-onStringnullName(s) of parent param(s), separated by |. E.g. "mode|cam". Alias: depend
depend-valueStringnullRequired matching value for each parent. Parents separated by |; within one parent: accepted values OR-separated by comma ,. E.g. "a|b,c"
depend-modeString"show""show": show when matched (default). "hide": hide when matched. Can be declared per-parent, separated by |: "show|hide"
depend-logicString"and"How to combine multiple parents: and / priority (= or LTR) / priority-rtl (= or RTL) / xor / nand. Alias: depend-priority
depend-defaultString"show"Default value when NO condition matches: "show" or "hide"
depend-initialString"auto"Initial state before any evaluation: "auto" / "show" / "hide". Alias: depend-initial-state
depend-negateBoolfalseInvert ALL conditions (NOT logic)
depend-thresholdInt-1Only for and: % of conditions that must match (0-100). E.g. 67 = at least 2/3
depend-include-hiddenBooltruetrue: hidden param still included in result. false: skip hidden param
depend-cascadeBooltruetrue: parent hidden => child hidden too. false: only visible parents are used
depend-onchangeStringnullShell callback name run when this param's hide/show state changes. Aliases: depend-on-change, depend-callback
depend-readonlyBoolfalsenew true: don't hide - just dim and lock interaction
depend-sortBoolfalsenew true: move locked params to the bottom (only effective when depend-readonly=true)

17.2. Demo - simple dependency

Demo - hidden param
<Backup
Backup mode
Single file
Source file
/sdcard/file.zip
Destination folder
(hidden because mode != "folder")
[[action]]
title = "Backup"
script = """
if [ "$mode" = "file" ]; then
  cp "$src_file" /sdcard/backup/
else
  cp -r "$dst_folder" /sdcard/backup/
fi
"""

[[action.params]]
name = "mode"
title = "Backup mode"
type = "spinner"
value = "file"

[[action.params.options]]
title = "Single file"
value = "file"

[[action.params.options]]
title = "Whole folder"
value = "folder"

[[action.params]]
name = "src_file"
title = "Source file"
type = "file"
depend-on = "mode"
depend-value = "file"

[[action.params]]
name = "dst_folder"
title = "Destination folder"
type = "folder"
depend-on = "mode"
depend-value = "folder"
depend-sort = true only works when depend-readonly = true. If you declare depend-sort = true without depend-readonly, the parser forces it to false - because sort only makes sense for items "locked in place" (still visible, just dimmed), not for fully hidden items (View.GONE has no slot to "move down").

17.3. depend-logic reference

LogicMeaningExample
and (default)ALL conditions must matchdepend-on="a|b" => both a and b must match depend-value
priorityleft-to-right, first matching condition winsIf a matches => result follows a's mode; if a doesn't match, check b...
priority-rtlright-to-left, opposite of priorityIf b matches before a
xorEXACTLY ONE condition must matcha matches OR b matches, not both
nandnegation of andNOT all matching => true

17.4. depend-default and depend-initial

By default, when no condition matches, the param is shown. To change the default to hidden:

depend-initial = "auto" (default) auto-determines based on depend-default. Use "show"/"hide" when you want to avoid "flicker" when the dialog first opens - the UI will pin to that state until evaluation completes.

18. Shell-script fields - overview

In Tool-Tree, many fields accept either a static value (string/bool) or a dynamic shell script. The parser runs the script and uses its output as the value. Here is a summary of all shell-style fields:

FieldApplies toDescription
title-shNode, param, optionProduce title dynamically
desc-shNode, paramProduce desc dynamically
summary-shNodeProduce summary dynamically
warn-sh / warning-shRunnableNodeProduce warning dynamically
label-shActionParamInfoProduce label dynamically
placeholder-shActionParamInfoProduce placeholder dynamically
desc-on-shActionParamInfo (type=bool)Produce desc-on dynamically
value-shActionParamInfo, EditorNodeFetch current value
options-sh / option-sh / options-suPickerNode, ActionParamInfo, PageMenuOptionProduce options list dynamically
get / getstateSwitchNode, PickerNode, PageMenuOption (checkbox/spinner), TextRow (toggle)Read current state (batched via pending states at parse time)
set / setstate / scriptActionNode, SwitchNode, PickerNode, DownloadNode, PageMenuOption, TextRow (toggle)Set new state on user interaction
lock / lock-stateClickableNode, PageNode, PageMenuOptionCheck lock state (static, or "state|message" format)
lock-shClickableNode (page, action, switch, picker, download, editor)Check lock state dynamically (returns "1" = locked)
url-shDownloadNodeProduce download URL dynamically - runs once on first tap, cached into url for the rest of that page visit
support / visibleAll nodes, row, paramHide/show (accepts shell too)

19. resolveBoolOrShell

The resolveBoolOrShell() function parses fields that can take either a static boolean or a shell command. If the string evaluates to "true" or "1", it resolves to true. For non-boolean strings, it executes the script immediately via the root shell during parsing; if the command output equals "1", it returns true, otherwise false.

20. Pending states

To maximize performance when parsing large configuration files, the parser does not run every dynamic shell evaluation synchronously. Five queues collect the deferred work while parsing:

QueueCollected fromResult applied to
pendingSwitchStatesswitch getchecked = true when output is "1"/"true" (and not "error")
pendingPickerStatespicker getcurrent value
pendingRowCheckedStatesrow get (toggle)row checked = (output trimmed == "1")
pendingRowVisibleStatesrow support (non-boolean value)row removed from its list when output != "1"
pendingDynamicStringstitle-sh/desc-sh/summary-sh of nodes, warn-sh of runnable nodes, title-sh of picker/param optionsreplaces the static string

After the whole document is parsed, resolvePendingStates() submits ALL collected scripts in a single batch via ScriptEnvironmen.executeMultipleResultRoot() - one shell round-trip instead of N - and then distributes the results to each node before the page is displayed.

21. process = true

Setting process = true on a [[page]] instructs the rendering engine to build and render child nodes progressively on the UI as they are parsed, accompanied by a linear progress indicator, rather than holding display until the entire tree has loaded. Note that a [[group]] is emitted to the UI only after ALL of its children have been collected (group + children appear together), while items outside any group are emitted one by one as soon as they are parsed.

22. Page lifecycle

Sub-pages trigger lifecycle callbacks during initialization and reading. before-load executes before parsing begins, followed by after-load upon parsing completion. Dependent on the parse status, load-ok or load-fail shell scripts execute accordingly.

23. Script output control sequences

While a script (from [[action]], [[switch]], [[download]] ...) is running, its log dialog is not just a plain text viewer. If a running script echos a line matching one of the special tags below, the log dialog intercepts that line (it is not printed as normal output) and performs a UI action instead. This lets a shell script drive dialogs, prompts, progress bars, and even control the app process itself, without leaving the log screen.

All tags follow the same pattern: tagname:[content], printed on its own line via echo. They only work while the log dialog is open and reading the script's live output.

23.1. exit:[kill] / exit:[restart]

Terminates the app process from inside a running script. exit:[kill] simply kills the app process; exit:[restart] relaunches the app first and then kills the old process, giving the effect of a full app restart.

[[action]]
title = "Apply and restart app"
confirm = true
warn = "The app will restart to apply changes"
script = """
setprop persist.sys.my_tweak 1
echo "exit:[restart]"
"""
Note: everything printed by the script after the exit:[...] line is never processed - the process is torn down as soon as the tag is detected.

23.2. choose:[value1|Label1,value2|Label2,...]

Pauses the script and renders a row of buttons directly in the log dialog. Each option is value|Label (the |Label part is optional - if omitted, the value itself is used as the label). When the user taps one, its value is written back to the script's standard input, and the script resumes.

[[action]]
title = "Set governor"
script = """
echo "choose:[performance|Performance,powersave|Power saving,schedutil|Balanced]"
read governor
echo "userspace" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null
for cpu in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
  echo "$governor" > "$cpu"
done
echo "Governor set to: $governor"
"""

23.3. pick:[values] / pickv:[values] / pickh:[values]

Same idea as choose:[...] (comma-separated value|Label list, sent back via stdin on tap), but rendered as tappable links inline in the log text rather than as separate buttons. pickv:[...] (or plain pick:[...]) stacks the choices vertically; pickh:[...] lays them out horizontally.

[[action]]
title = "Choose log level"
script = """
echo "Select a verbosity level:"
echo "pickh:[1|Low,2|Medium,3|High]"
read level
echo "Verbosity set to level $level"
"""

23.4. input:[prompt]

Shows a text input field in the log dialog with prompt as its hint. Whatever the user types and submits is written back to the script's standard input, letting the script keep going with free-form text instead of a fixed list of choices.

[[action]]
title = "Rename backup"
script = """
echo "input:[Enter a name for this backup]"
read name
mv /sdcard/backup/last.zip "/sdcard/backup/${name}.zip"
echo "Saved as ${name}.zip"
"""

23.5. progress:[current/total]

Drives the progress bar shown at the top of the log dialog. Echo this repeatedly while a long-running task advances. current = -1 switches the bar to indeterminate (spinning) mode; current >= total hides the bar (task finished).

[[action]]
title = "Copy large folder"
script = """
echo "progress:[-1/100]"
total=$(ls /sdcard/source | wc -l)
i=0
for f in /sdcard/source/*; do
  cp "$f" /sdcard/target/
  i=$((i+1))
  echo "progress:[$i/$total]"
done
echo "progress:[$total/$total]"
echo "Done: $i files copied"
"""

23.6. am:[...] (send an Android Intent)

Sends an Android Intent directly from a shell script, without needing a separate am start binary call wired up elsewhere. Supported sub-commands are start, startservice, foregroundservice, and broadcast, using the same -a/-d/-n flags as the standard Android am command line tool, plus typed extras.

Extra flagType
--es key valueString
--ei key valueInt
--ez key valueBoolean
--el key valueLong
--ef key valueFloat
--ed key valueDouble
--eu key valueUri
--esa key v1 v2String[]
--eia key v1 v2Int[]
[[action]]
title = "Open developer options"
script = """
echo "am:[start -a android.settings.APPLICATION_DEVELOPMENT_SETTINGS]"
"""

[[action]]
title = "Broadcast a custom event"
script = """
echo "am:[broadcast -a com.example.MY_ACTION --es status ok --ei code 200]"
"""
Echo am:[help] from any script to print the full command/extras syntax straight into the log dialog - handy for testing without leaving the app.

24. Full example

A representative TOML configuration showcasing groups, actions, parameters, switches, and rich text rows:

# Top-level item (declared before the first [[group]]) - shown without a group header
[[text]]
title = "NOTICE"

[[text.rows]]
text = "Every entry below belongs to the nearest [[group]] above it"
italic = true

[[group]]
title = "System Performance"

[[switch]]
title = "Performance Mode"
desc = "Enable performance governor"
get = "getprop sys.perf.mode"
set = "setprop sys.perf.mode $state"
lock-sh = "[ -f /data/adb/.perf-ready ] && echo 1"

[[action]]
title = "Set CPU Frequency"
script = "echo $freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
reload = "perf-mode"

[[action.params]]
name = "freq"
title = "Frequency (kHz)"
type = "spinner"

[[action.params.options]]
title = "Mode 1"
value = "1800000"

[[action.params.options]]
title = "Mode 2"
value = "2200000"

[[action.rows]]
text = "Applies to CPU0 only"
italic = true

[[group]]
title = "Maintenance"

[[action]]
title = "Clear cache"
confirm = true
warn = "Cache will be wiped!"
script = "sync; echo 3 > /proc/sys/vm/drop_caches"
auto-off = true

# Page menu (3-dot) and FAB - collected wherever they appear
[[menu]]
handler = "echo Menu: $key"

[[menu.items]]
title = "Refresh"
type = "refresh"

[[menu.items]]
title = "Performance Mode"
type = "checkbox"
get = "getprop sys.perf.mode"
script = "setprop sys.perf.mode $state"

[[fab]]
[[fab.items]]
title = "Flash zip"
type = "file"
suffix = "zip"
script = "sh $state"

25. Tips & pitfalls

  • Always use double brackets [[node]] across all declarations to prevent TOML table array parser exceptions.
  • Groups are flat - never use dotted [[group.action]] nesting (those children are silently ignored). Declare children flat, right after their [[group]].
  • [[toml]] is a reserved marker for inline-TOML detection (config-sh) - do not use it as a node table name.
  • Use selective refresh via reload = "id1,id2" instead of reloading the entire page after action execution.