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. Nodes can be nested (e.g. [[group.action]] is an action inside a group) to build a hierarchical tree.

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; can be nested
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. Nesting = dotted path

A child node of a group is declared using the dotted path [[group.type]] (or [[group.subgroup.type]] for deeper nesting). Child nodes always belong to the most recently declared group above them in the file.

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: resolveBoolOrShell does NOT run immediately during page parsing for readonly/support of action or checked of switch - these 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.

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-numInt>0: image is an animated GIF (photo_1.png, photo_2.png...)
photo-gif-timegif-timeIntTime per frame (ms; default 300)
photo-gif-autoplaygif-autoplayBooltrue (default): auto-play the GIF
photo-gif-loopphoto-gif-loop-countIntLoop count (0 = infinite)
icon-gif-num-IntSame as photo-gif-num but for the small icon
icon-gif-time-IntTime per frame for the icon
icon-gif-autoplay-BoolAuto-play icon GIF
icon-gif-loopicon-gif-loop-countIntLoop count for the icon GIF
bgbg-pathStringBackground image for the item/dialog
locklock-state, lockedBooltrue: lock this item (no interaction allowed)
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-StringShell script content (alias of script for actions)
bg-taskbackground-task, async-taskBoolRun in background (no log dialog)
scriptset, setstateStringScript to set state (for switch/picker - see dedicated sections)
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 entire group. Accepts shell too!

5.2. Demo

Demo (Android UI)
<Settings
CPU
Optimize CPU
Set governor to performance
Governor
performance
Memory
🗑
Clear cache

5.3. Multi-level nesting

Groups can be nested inside other groups to build deep trees:

The parser supports arbitrary nesting depth, but in practice don't go beyond 3 levels - it becomes hard to read and maintain.

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-stateStringshell Shell to check the lock state
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
config-sh returns one of two things: either a .toml file path (ending with .toml), or inline TOML content (whose first or second non-empty line starts with [[group]] or [[group.). The parser auto-detects - see PageConfigSh.looksLikeInlineToml().

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-stateStringshell Script to check lock. If it returns "1", the action is locked (not clickable)
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
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). Accepts shell too!
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)
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

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
setsetstate, scriptStringrequired Script to set state. Receives $state = "1"/"0"
locklock-stateStringshell Script to check lock
+ all fields of RunnableNode, ClickableNode, NodeInfoBase

9.2. Demo

Demo (Android UI)
<Display
Display
Dark mode
Enable dark UI
Auto brightness
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
setsetstate, scriptStringrequired 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-stateStringshell Script to check lock
+ all fields of RunnableNode, ClickableNode, NodeInfoBase

10.2. Demo

Demo (Android UI)
<CPU
Governor
performance
Max freq
2400000 kHz
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

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

This is the most common display component: used in [[text]], [[action]], [[page]], [[download]] and [[group.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
sh-String""shell Script that produces text dynamically
boldbBoolfalseBold
italiciBoolfalseItalic
underlineuBoolfalseUnderline
strikethroughline-through, delBoolfalseStrikethrough
monospacemono, codeBoolfalseMonospace font (for code/log)
letter-spacingletterspacing, spacingFloat0Letter spacing (em units)
line-heightlineheight, row-heightFloat0Line height multiplier (1.5 = +50%)
margin-topspacing-topInt0Top margin (dp)
margin-bottomspacing-bottomInt0Bottom 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-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
checkedchecked-sh, checkBool|ShellfalseToggle state. Accepts shell too!
onchange-shon-change-sh, toggle-sh, set-shString""shell Script to run when the user toggles (receives $state)
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
sh for a row runs IMMEDIATELY during page parse (synchronous, not pending). checked when shell 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
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).

14.1. Download fields

FieldAliasTypeDescription
url-Stringrequired URL (http/https) to download. Only static URLs are supported - no url-sh
scriptset, setstateStringScript run after download. Receives $state = path of downloaded file
locklock-stateStringshell Script to check lock
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%
The parser does NOT support url-sh (script generating URL dynamically). The URL must be a constant in the TOML file.

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 [[group]]/[[action]]/[[page]]/[[switch]]/[[picker]]/[[text]] - no need to declare a separate [[resource]] entry.

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
ExtractAssets.extractResource() and extractResources() check if the file already exists before re-extracting. To avoid re-extracting on every run, use support = "test -f ..." to only extract when needed.

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"
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
depend-defaultString"show"Default value when NO condition matches: "show" or "hide"
depend-initialString"auto"Initial state before any evaluation: "auto" / "show" / "hide"
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
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")
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, option, rowProduce 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(spinner)Read current state (batched via pending states at parse time)
set / setstate / scriptSwitchNode, PickerNode, ActionNode, DownloadNode, EditorNode, PageMenuOptionSet new state on user interaction
lock / lock-stateClickableNode, PageNode, PageMenuOptionCheck lock state
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 shell script in the background; if the command output equals "1", it returns true, otherwise false.

20. Pending states

To maximize performance when parsing large configuration files, dynamic shell evaluations for nodes (such as get scripts for switches or lock checks) are queued into pending state batches rather than executed synchronously one by one. Once reading finishes, all pending states run asynchronously to update node UI values in a single pass.

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.

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. Full example

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

[[group]]
title = "System Performance"

[[group.switch]]
title = "Performance Mode"
desc = "Enable performance governor"
get = "getprop sys.perf.mode"
set = "setprop sys.perf.mode $state"

[[group.action]]
title = "Set CPU Frequency"
script = "echo $freq > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"

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

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

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

24. Tips & pitfalls

  • Always use double brackets [[node]] across all declarations to prevent TOML table array parser exceptions.
  • Keep nesting structures under 3 levels deep to maintain responsive UI performance and code clarity.
  • Use selective refresh via reload = "id1,id2" instead of reloading the entire page after action execution.