Send to the shared area
When one item or a multi-selection is active, the shared-area button can copy the selected part from the workspace, group, true/false action branch, dialog, and variables surfaces into the device-local library.
No-Code Workspace — Block Reference
Tracks whether a detected color changes position
Documented for app version 1.0.50Updated:
Block Preview
Captured from the in-app Visual Builder block card in the English UI.

Block Preview
Captured from the English Visual Builder block card with the Advanced/extra settings panel expanded.

Finds each selected colour inside the region and tracks it during a short observation window. It goes TRUE when any colour meaningfully changes position or footprint, and FALSE when it stays still or is never found. This block answers "did the color move?" rather than only "does the color exist?".
IMAGE, TEXT, COLOR and the other search blocks that use the Search Regions editor can define several search regions with + Add Region; OCR_VALUE tries its read regions in order. Inside a search region, Exclude Region skips noisy areas such as a HUD or a badge, and it applies only to the search region it belongs to. From 1.0.51 one search region can hold several excluded regions, each with its own shape and its own variable or Dialog binding.Coming soonv1.0.51
You can now assign a sticky anchor to each search region. This brings Region.sticky into the no-code surface so the region can stay aligned to anchors such as top_left, bottom_right, or center when screen geometry changes.
Search Duration (ms) defines one search window; 0 means a single look. Retry Mode in the Retry & Wait section is Off, Infinite Loop, Retry On True, Retry On False, Wait Until Found, or Wait Until Gone, so a block becomes a one-shot check, a controlled polling step, or a step that waits for a target to appear or to disappear. Retry Timeout (ms, 0 = unlimited) bounds the loop; Scan Rate (Hz) sets scans per second.
mScore, scale, and scan engine choices matter across themes, display sizes, and rendering differences. Smaller regions plus the right engine dramatically improve builder stability.
Use it when:
You need to know whether a moving target, icon, or colored fragment is actually shifting
You want to wait while the color is stable and act only after movement starts
You need to track a bright moving marker in a game or app
Color is enough and you want a faster movement check than IMAGE
Add "Moving Color (MOVING_COLOR)"
Pick a color or enter a HEX value
SCAN REGION: Limit it to the area where motion can happen; narrower is more stable
SCAN ENGINE: cluster_color is the default and safest choice; tolerant_color is the next best
OBSERVE WINDOW (ms): The window during which movement is expected (default 2000)
CHANGE THRESHOLD (px): Minimum displacement used to ignore small jitter (default 6)
MIN MOVING FRAMES: Requires consecutive motion instead of a one-frame jump (default 2)
OBSERVE SCAN RATE (Hz): How often the region is sampled (12 on a new block, up to 60)
Configure TRUE/FALSE branches based on the motion result
Colour(s) to track. Pick from screen or enter manually; each colour is tracked on its own
Area where movement is observed. Narrower is more stable
cluster_color (default), tolerant_color, exact_color, gradient_color, dominant_color
Observation window used while waiting for the color to move (default 2000)
Minimum pixel displacement counted as meaningful motion (default 6)
Number of consecutive moving frames required to confirm motion (default 2)
Samples per second while observing: 1–60 Hz, 12 on a new block. At 0 the block's Scan Rate is used, and if that is also 0, 12 Hz (~83 ms)
Shared wait and timeout controls used by other search blocks
Writes the 0.0–1.0 confidence to the variable named in Advanced; COMPARE it to drop weak matches (same as IMAGE)
Writes the match box into your own variables such as matchX/Y/W/H; CLICK {matchX},{matchY} to act there (same as IMAGE)
Same as IMAGE: a Condition in each entry's lightbulb; several exclusions per region
| Parameter | Description |
|---|---|
| Color (HEX) | Colour(s) to track. Pick from screen or enter manually; each colour is tracked on its own |
| Scan Region | Area where movement is observed. Narrower is more stable |
| Scan Engine | cluster_color (default), tolerant_color, exact_color, gradient_color, dominant_color |
| Observe Window (ms) | Observation window used while waiting for the color to move (default 2000) |
| Change Threshold (px) | Minimum pixel displacement counted as meaningful motion (default 6) |
| Min Moving Frames | Number of consecutive moving frames required to confirm motion (default 2) |
| Observe Scan Rate (Hz) | Samples per second while observing: 1–60 Hz, 12 on a new block. At 0 the block's Scan Rate is used, and if that is also 0, 12 Hz (~83 ms) |
| Match / Wait / Timeout | Shared wait and timeout controls used by other search blocks |
| Score Variable (Advanced) | Writes the 0.0–1.0 confidence to the variable named in Advanced; COMPARE it to drop weak matches (same as IMAGE) |
| Match Position (matchX/Y/W/H) (Advanced) | Writes the match box into your own variables such as matchX/Y/W/H; CLICK {matchX},{matchY} to act there (same as IMAGE) |
| Item conditions and multiple exclusionsComing soonv1.0.51 | Same as IMAGE: a Condition in each entry's lightbulb; several exclusions per region |
Scenario 1: Wait for a moving marker
MOVING_COLOR → choose the marker color
Scan region: Panel where the marker travels
Observe Window: 1500 ms
Change Threshold: 8 px
TRUE: CLICK on the match
FALSE: WAIT briefly and retry
Scenario 2: Ignore a warning until it starts moving
MOVING_COLOR → select the yellow warning dot
FALSE branch: LOG "warning is stable"
TRUE branch: Continue with SYSTEM or SWIPE after motion starts
MOST COMMON COMBINATIONS:
MOVING_COLOR + CLICK: Click when motion is confirmed
MOVING_COLOR + WAIT: Wait when there is no motion, then retry
MOVING_COLOR + IMAGE: Detect motion quickly by color, then verify with image
MOVING_COLOR + LOG: Record whether movement started or not
MOVING_COLOR + SWIPE: Continue the screen flow after motion is detected
Tracks whether a detected color changes position
WARNING:
If the same color appears in many places, the block may track the wrong target; keep the scan region narrow
gradient_color and dominant_color are less stable for motion tracking; start with cluster_color
A very low threshold can treat one-frame jitter as real motion
If the color is never found, the block returns FALSE; that path also covers the "no motion" case
The same block at three levels: a quick start, the real options, and professional techniques.
The MOVING_COLOR block first finds the target colour in the selected region, then watches whether that same match shifts position within a short observation window. If the colour moves meaningfully it goes TRUE; if it stays still or is never found it goes FALSE. So this block answers "did the colour move?" rather than "does the colour exist?". Simplest use: pick the moving marker's colour, shrink the region to the panel it travels in, and act on the TRUE branch when motion is detected.
Three calibration levers work together. Observe Window (movingColorObserveTimeoutMs, default 2000ms) is the window you wait for the colour to move; enlarge it when motion is rare/slow, keep it small when motion is frequent/fast to speed up the decision. Change Threshold (movingColorThresholdPx, default 6px) is the minimum displacement needed to ignore jitter; raise it to 8–12px if the screen shakes slightly. Min Moving Frames (movingColorMinMovedFrames, default 2) requires consecutive moving frames so a one-frame jump is not counted as real motion; set 2–3 in a noisy scene. Practical calibration order: raise the threshold first (cuts false positives), then increase min-frames (drops one-frame noise), and finally tune the timeout to the natural period of the motion. Observe Scan Rate (movingColorScanRateHz) is the samples per second during observation (1–60 Hz, 12 on a new block; at 0 the block's Scan Rate is used, and if that is also 0, 12 Hz ≈ 83 ms): a high Hz catches fast motion but keeps the CPU busy throughout the observation window, so the battery/heat cost on older devices is noticeable; balance smoothness against battery by experimenting. For the Scan Engine, cluster_color is the safest choice and tolerant_color is the next best. ADVANCED → Score Variable (scoreVar) is critical here too: write the tracked colour match's confidence into a variable and reject a weak match with COMPARE — that way the block does not chase a false/faint colour blob as if it were the real target.
The block measures motion by tracking the nearest same-colour match; if the colour appears in many places it may follow the wrong target, so keep the region as narrow as possible. gradient_color and dominant_color are unstable for motion tracking; start with cluster_color. If the threshold is too low it mistakes one-frame jitter for real motion; balance this by raising movingColorMinMovedFrames to 2–3 and setting the threshold to 6–10px. Use scoreVar + COMPARE to drop weak colour matches and avoid following the wrong target. If the colour is never found the block returns FALSE, which is the same branch as "no motion"; if you need to separate them, add a COLOR test on the FALSE branch to check whether the colour exists at all.
Small, reproducible examples that combine several blocks — build them straight into your own macro.
This block is useful on its own — but it shines when paired with the right partners.
When motion is confirmed, click the colour's last position via contextX/Y; you catch the roaming target.
When a movement direction is detected, SWIPE in that direction to build a flow that follows the moving target.
Detect motion quickly by colour, then verify visually with IMAGE that the right element actually moved.
When there is no motion, WAIT briefly on the FALSE branch and re-observe; you avoid wasting CPU.
Write the tracked colour match's confidence with ADVANCED scoreVar and reject a weak blob via COMPARE; the block will not mistake a noisy colour patch for the real moving target.
Leaving the Change Threshold too low (e.g. 1px); small jitter is mistaken for motion. Fix: set the threshold to 6–10px and Min Moving Frames to 2–3.
Tracking the wrong target when many copies of the same colour exist in a wide region. Fix: shrink the region to only the area where motion happens.
Choosing gradient_color/dominant_color for motion tracking and getting unstable results. Fix: try cluster_color first, then tolerant_color if needed.
Raising movingColorScanRateHz catches fast motion but increases CPU load on older devices; balance smoothness against battery by experimenting.
To disambiguate the FALSE branch, follow it with a COLOR block: colour present but no motion means "stationary", colour absent means "gone".
Each selected colour is tracked on its own; the block goes TRUE when any of them meaningfully changes position or footprint. One block can watch several markers in the same panel.
Coming soonv1.0.51Each colour can get a Condition from its lightbulb (same rules as IMAGE): a colour whose condition is off is not tracked and its previous position is discarded; with no active colour the observation pass ends.
The Lua below is what the Code Editor generates for this block with sample settings. Adding cases, branches, actions or loops, or changing settings, extends the generated code accordingly. Study it to see what the block does under the hood, to learn Lua, or to copy and adapt it. Use the Copy button (top-right) to paste it into the Code Editor, or “Try in Editor →” to run it in the live editor.
local function _pickMovingColor1_0(_refX, _refY, _refW, _refH)
local _best = nil
local _bestCost = nil
local _bestScore = nil
local _bestArea = nil
do
local _reg2 = Region():noScale()
local _list2 = _reg2:findMulti(Asset.color("#00FF00"):mScore(0.8), {engine="cluster_color"})
local _i2 = 1
while _i2 <= #_list2 do
local _cand2 = _list2[_i2]
local _pt2 = _cand2 and _cand2:getPoint() or nil
if _pt2 ~= nil then
local _px2 = _pt2:getX()
local _py2 = _pt2:getY()
local _pw2 = math.max(1, _cand2:getW() or 1)
local _ph2 = math.max(1, _cand2:getH() or 1)
local _pScore2 = _cand2:getScore() or 0
if _refX == nil or _refY == nil then
local _pArea2 = _pw2 * _ph2
if _best == nil or _pScore2 > (_bestScore or -1) or (_pScore2 == (_bestScore or -1) and _pArea2 > (_bestArea or 0)) then
_best = _cand2
_bestScore = _pScore2
_bestArea = _pArea2
end
else
local _dw2 = _refW ~= nil and math.abs(_pw2 - _refW) or 0
local _dh2 = _refH ~= nil and math.abs(_ph2 - _refH) or 0
local _sizeCost2 = (_dw2 * _dw2) + (_dh2 * _dh2)
local _dx2 = _px2 - _refX
local _dy2 = _py2 - _refY
local _dist2 = (_dx2 * _dx2) + (_dy2 * _dy2)
local _cost2 = _dist2 + (_sizeCost2 * 0.35)
if _best == nil or _cost2 < (_bestCost or _cost2 + 1) or (_cost2 == _bestCost and _pScore2 > (_bestScore or -1)) then
_best = _cand2
_bestCost = _cost2
_bestScore = _pScore2
end
end
end
_i2 = _i2 + 1
end
end
return _best
end
local _movingTimeout1 = math.max(1, math.floor(1000))
local _movingThresholdRef1 = math.max(0, math.floor(5.0))
local _mvsOk1, _mvsX1, _mvsY1 = pcall(function() local _o = Point(2, 2); local _ux = Point(3, 2); local _uy = Point(2, 3); local function _bad(_p) return _p == nil or (type(_p) == "table" and _p.__mh_point_unresolved == true) end; if _bad(_o) or _bad(_ux) or _bad(_uy) then error("scale probe failed") end; return math.abs(_ux:getX() - _o:getX()), math.abs(_uy:getY() - _o:getY()) end)
if not _mvsOk1 or type(_mvsX1) ~= "number" or _mvsX1 <= 0 then _mvsX1 = 1 end
if type(_mvsY1) ~= "number" or _mvsY1 <= 0 then _mvsY1 = 1 end
local _movingMinFrames1 = math.max(1, math.floor(2))
local _movingDelay1 = (math.max(1, math.floor(1000 / 12)))
local _movingSearchDeadline1 = System.currentTime() + _movingTimeout1
local _movingObserveDeadline1 = nil
local _movingActiveTrackerIndex1 = nil
local _movingTrackers1 = {
{ helper = _pickMovingColor1_0, color = "#00FF00", baselineX = nil, baselineY = nil, baselineW = nil, baselineH = nil, lastX = nil, lastY = nil, lastW = nil, lastH = nil, movedFrames = 0, hasDetectedMotion = false },
}
local m1 = nil
local _movingDetectedColor1 = nil
local _movingDetectedDx1 = 0
local _movingDetectedDy1 = 0
local _movingDetectedDw1 = 0
local _movingDetectedDh1 = 0
local _movingDone1 = false
_lastMovingColorMotion = nil
while not _movingDone1 and ((_movingObserveDeadline1 == nil and System.currentTime() <= _movingSearchDeadline1) or (_movingObserveDeadline1 ~= nil and System.currentTime() <= _movingObserveDeadline1)) do
Snap.screenRefresh()
local _movingTrackerIndex1 = 1
while not _movingDone1 and _movingTrackerIndex1 <= #_movingTrackers1 do
local _movingTracker1 = _movingTrackers1[_movingTrackerIndex1]
local _movingCandidate1 = _movingTracker1.helper(_movingTracker1.lastX, _movingTracker1.lastY, _movingTracker1.lastW, _movingTracker1.lastH)
if _movingCandidate1 ~= nil then
local _movingPoint1 = _movingCandidate1:getPoint()
if _movingPoint1 ~= nil then
local _movingX1 = _movingPoint1:getX()
local _movingY1 = _movingPoint1:getY()
local _movingW1 = math.max(1, _movingCandidate1:getW() or 1)
local _movingH1 = math.max(1, _movingCandidate1:getH() or 1)
if _movingTracker1.baselineX == nil or _movingTracker1.baselineY == nil then
_movingTracker1.baselineX = _movingX1
_movingTracker1.baselineY = _movingY1
_movingTracker1.baselineW = _movingW1
_movingTracker1.baselineH = _movingH1
_movingTracker1.lastX = _movingX1
_movingTracker1.lastY = _movingY1
_movingTracker1.lastW = _movingW1
_movingTracker1.lastH = _movingH1
if _movingActiveTrackerIndex1 == nil then
_movingActiveTrackerIndex1 = _movingTrackerIndex1
_movingObserveDeadline1 = System.currentTime() + _movingTimeout1
end
else
_movingTracker1.lastX = _movingX1
_movingTracker1.lastY = _movingY1
_movingTracker1.lastW = _movingW1
_movingTracker1.lastH = _movingH1
local _movingSignedDx1 = _movingX1 - _movingTracker1.baselineX
local _movingSignedDy1 = _movingY1 - _movingTracker1.baselineY
local _movingDx1 = math.abs(_movingSignedDx1)
local _movingDy1 = math.abs(_movingSignedDy1)
local _movingDw1 = math.abs(_movingW1 - (_movingTracker1.baselineW or _movingW1))
local _movingDh1 = math.abs(_movingH1 - (_movingTracker1.baselineH or _movingH1))
local _movingRDx1 = _movingDx1 / _mvsX1
local _movingRDy1 = _movingDy1 / _mvsY1
local _movingRDw1 = _movingDw1 / _mvsX1
local _movingRDh1 = _movingDh1 / _mvsY1
local _movingDist1 = math.sqrt((_movingRDx1 * _movingRDx1) + (_movingRDy1 * _movingRDy1))
local _movementDetected1 = false
if _movingThresholdRef1 <= 0 then
_movementDetected1 = _movingDx1 > 0 or _movingDy1 > 0 or _movingDw1 > 0 or _movingDh1 > 0
else
_movementDetected1 = _movingRDx1 >= _movingThresholdRef1 or _movingRDy1 >= _movingThresholdRef1 or _movingRDw1 >= _movingThresholdRef1 or _movingRDh1 >= _movingThresholdRef1 or _movingDist1 >= _movingThresholdRef1
end
if _movementDetected1 then
_movingTracker1.movedFrames = _movingTracker1.movedFrames + 1
if _movingTracker1.movedFrames >= _movingMinFrames1 then
_movingTracker1.hasDetectedMotion = true
end
else
_movingTracker1.movedFrames = 0
end
if _movingTracker1.hasDetectedMotion then
_movingDetectedColor1 = _movingTracker1.color
_movingDetectedDx1 = _movingSignedDx1
_movingDetectedDy1 = _movingSignedDy1
_movingDetectedDw1 = _movingDw1
_movingDetectedDh1 = _movingDh1
m1 = _movingCandidate1
_lastMovingColorMotion = {
match = _movingCandidate1,
color = _movingTracker1.color,
startX = _movingTracker1.baselineX,
startY = _movingTracker1.baselineY,
endX = _movingX1,
endY = _movingY1,
dx = _movingSignedDx1,
dy = _movingSignedDy1,
dw = _movingDw1,
dh = _movingDh1,
}
_movingDone1 = true
end
end
else
_movingTracker1.movedFrames = 0
end
else
_movingTracker1.movedFrames = 0
end
_movingTrackerIndex1 = _movingTrackerIndex1 + 1
end
if not _movingDone1 then
wait(_movingDelay1)
end
end
if m1 ~= nil then
System.log("MovingColor[cluster_color] change detected for " .. tostring(_movingDetectedColor1 or "?") .. " dx=" .. tostring(_movingDetectedDx1) .. ", dy=" .. tostring(_movingDetectedDy1) .. ", dw=" .. tostring(_movingDetectedDw1) .. ", dh=" .. tostring(_movingDetectedDh1))
else
System.log("MovingColor[cluster_color] no change detected within " .. tostring(_movingTimeout1) .. " ms")
end
_lastMatch = m1Note: generated code can evolve across versions; helper names (e.g. _reg1, m1) and internal optimizations may change. The logic and the called APIs reflect the block’s behavior.
The guidance below is not identical for every block. It summarizes the professional controls that most often repeat in the selected block family.
Keep the search close to the target. Smaller regions are faster and more stable.
Exclude static noise, badges, animations, or reflective areas to reduce false matches.
Timeout and scan rate define whether the block acts as a single check or controlled polling step.
mScore, scale, and engine choice must be tuned together for theme, resolution, and rendering differences.
OCR_VALUE writes the number it reads to its result variable (default ocr_val); TEXT writes the text it passed through its find/replace step to the Save Read Value variable (default ocr_val). In Match Variables, the score variable (0.0–1.0 confidence) and the X/Y/W/H variables carry the match confidence and position into later blocks: branch on the score with COMPARE, and in CLICK bind the coordinates to them with {x} or turn on Click on match. _lastMatch is the last match that Click on match uses; _lastOcrText is kept only for older CUSTOM_CODE scripts.
TOUCH and TOUCH_BREAK cover recipe-style touch flows. Raw pointer choreography such as Touch.down, Touch.move, Touch.up, Touch.dispatch, Touch.reset, Touch.breakAll, and Touch.releaseAfter is not exposed as a first-class builder block.
Touch.downTouch.moveTouch.upTouch.dispatchTouch.resetTouch.breakAllTouch.releaseAfterThe DIALOG block and dialog designer cover a bounded field set in the builder: Info Text, Text Input, Checkbox, Description, Radio Group, Dropdown, Multi Select, Tab Group, Date Time, Number Range, Slider, Image Picker, Color Picker, File Picker, Recorder Picker, Tag, Signature, and Spacer. The buttons are designed too: the Positive button and Negative button tabs set each button’s text, background, and border color, corner radius, and minimum height next to a preview. Freer Setting.builder composition, multi-step wizard flows, and mixed HUD/dialog choreography still do not map 1:1 into the builder.
Coming soonv1.0.51Two more fields: Agent Detect references (agent_detect_editor) lets the user pick a saved reference set or add and edit references, and Save confirms the selection; Navigation setup editor (navigation_editor) edits a saved map, marker and route from the Dialog and returns the setup and route selection without moving the character.
Setting.builderDialogHudTextViewAgentDetectEditorNavigationEditorHTTP_GET, HTTP_POST, and HTTP_PUT cover common fixed-method flows; HTTP_REQUEST covers one bounded request with method, header lines, body, content type, query parameters (GET and DELETE), timeout, and a bounded retry. These blocks never throw; they write the status code (-1 on a network failure). Cookie handling, chained request objects, and lower-level client flows still need the code editor.
Request()Request.setCookieRequest.getCookiesCUSTOM_CODEThe MAP, LIST, JSON_PARSE, REGEX_MATCH, and METRICS blocks cover the everyday cases: map actions, a static list, reading a value from a JSON path, regex match, find, replace, and split, and metrics actions. The rest of the Map, Array, JSON, Regex, and Metrics APIs (for example Array sort, filter, and map) is broader than the builder recipe model and stays on the CUSTOM_CODE and code editor side.
MapArrayJSONRegexMetricsRuntime helpers, raw Request objects, Lua built-ins such as print/pcall/xpcall, and free-form object chaining patterns are represented through CUSTOM_CODE or the code editor.
RuntimeRequestprintpcallxpcallCUSTOM_CODEEach block card now exposes an independent breakpoint toggle. When enabled, the breakpoint preference is stored with the block.
Breakpoints are only emitted into runtime code when Debug Mode is enabled from Visual Builder settings. When Debug Mode is off, breakpoints stay saved but do not pause execution.
Use ASSERT for fail-fast validation, and use breakpoints for step-by-step tracing and controlled pauses in the same flow.
Variables defined with SET_VARIABLE are accessible throughout the macro (global scope). All blocks can read/write the same variable.
However, a variable defined inside a GROUP will be 'nil' (undefined) until the GROUP executes. Blocks outside the GROUP using this variable may produce unexpected results.
FOR_EACH loop variables (item and index) only carry valid values inside the loop body. Outside the loop they are outside local scope and should not be used.
TRY_CATCH error variable is only valid within the catch block. If the try block succeeds, the catch branch is not entered and the error variable remains undefined.
TIP: Before using a variable inside a GROUP, assign a default value with SET_VARIABLE outside the GROUP. This way the variable won't be nil even if the GROUP has not run.
You can embed variable values and calculations into text fields using {{ expression }} syntax. Expressions inside double curly braces are evaluated as Lua code and the result is inserted into the text.
{{ expression }}Skor: {{ score + 1 }} → "Skor: " .. tostring(score + 1){{ name }} kazandı! → tostring(name) .. " kazandı!"X:{{ x }} Y:{{ y }} → "X:" .. tostring(x) .. " Y:" .. tostring(y)Expressions are checked by the sandbox policy. Security-sensitive calls like loadstring, require, debug are automatically blocked.
Keep scan regions as small as possible — improves speed, reduces false matches.
Always add an exit condition when using infinite loops (BREAK, timeout, or conditional exit).
Use network operations and error-prone steps inside TRY_CATCH.
If you need global error handling, use the Error Handler (ERROR_HANDLER) block as a singleton in the project.
Small coordinate offsets (CLICK/SWIPE) improve tap accuracy across different DPI and screen scales.
Customize macros with Dialog block — different parameters each run.
Define repeating steps once with GROUP_CALL, call from many places.
Cross-macro local library
The Local Shared Macro Area moves blocks, variables, dialog fields, gallery assets, and imported media dependencies between macros on the same device. It is device-local, not cloud sync, not Firebase sharing, and not a replacement for public macro sharing.
Professional usage model: put a reusable login flow, common scan region, shared dialog form, variable group, template image, color profile, replay recording, or imported PLAY_SOUND media file into the shared area from one macro, then import it from the relevant + menu in another macro.
VISUAL_BLOCKSVARIABLESDIALOG_FIELDSTEMPLATE_IMAGECOLOR_SWATCHREPLAY_RECORDMEDIA_FILEWhen one item or a multi-selection is active, the shared-area button can copy the selected part from the workspace, group, true/false action branch, dialog, and variables surfaces into the device-local library.
The shared-area entry under the + menu imports the selected item into the surface that opened it. Main workspace, group, action branch, dialog, and variables each receive the compatible item type.
Gallery items include template images, color swatches, replay records, and imported audio/media files. They can move between macros on the same device so repeated templates, colors, recordings, and PLAY_SOUND media assets do not need to be captured again.
The shared area is not a permanent dumping ground. Delete stale shared items from the shared-library panel; imports create a copy in the target macro and do not rewrite the source macro flow.
AI assistant
Tell MH AI what you want in plain language; it proposes the right block sequence and lands it in the workspace after you approve. In the current release it works with cloud providers such as Gemini, Claude, and OpenAI; on-device local models are on the roadmap.