Skip to content

General

This page covers common values and services available to Scripted Decorations. TaskManSync embeds Lua 5.5.1 with the base, math, string, table, and utf8 libraries. It does not provide ordinary io, os, debug, or coroutine libraries, or load, dofile, and loadfile. Use the typed time, fs, and process APIs instead of os.date, io.open, or os.execute. Pure-Lua module loading is installed separately as described below.

APIPurpose
log.debug(message)Debug diagnostic
log.info(message)Informational diagnostic
log.warning(message)Warning diagnostic
log.error(message)Error diagnostic
time.now()Current system date/time
time.dateTime{...}Construct a date/time value
time.duration.milliseconds/seconds/minutes/hours(value)Construct signed durations
time.instant.milliseconds(value)Reconstruct an instant from signed milliseconds
time.instant.toMilliseconds(instant)Convert an instant for persistence
time.duration.toMilliseconds(duration)Convert a duration for persistence
time.localDateTime(instant)Convert an instant to local calendar fields
json.encode(value)Encode structured data as JSON
json.decode(text)Decode JSON
json.nullPreserve explicit JSON null
json.object()Construct an explicitly empty JSON object
requestUpdate()Request one coalesced Decoration update
scheduler.at(deadline, "callbackName")Schedule one callback
require(name)Load a user-owned pure-Lua module
package.pathConfigure pure-Lua module search paths

Structured values are bounded. The common limits are 32 nested levels, 4,096 total values, 256 KiB strings, and 1 KiB object keys. JSON input and output are each limited to 256 KiB.

The bounds apply to copied API data, not all Lua variables. TaskManSync intentionally imposes no arbitrary per-script Lua-heap quota. Actual allocation failures can still occur, and execution time/instruction budgets can fault runaway callbacks. Native operations, queues, and resources retain their own limits. This is trusted local-user scripting, not an untrusted-code sandbox.

Time and Values

time.now() returns a system-clock instant. time.dateTime{ year=2030, month=1, day=1, hour=12, minute=30, second=0 } constructs a local-system instant. Instants and durations are semantic values, not ordinary millisecond numbers: add a duration to an instant, subtract two instants to obtain a duration, and compare values of the same kind. Duration arithmetic also supports addition/subtraction and numeric scaling.

Calendar constructors require integer year, month, and day; optional hour, minute, second, and millisecond default to zero. Invalid dates/times raise a Lua error. Duration constructors take signed integers in their named units; for half a second, use time.duration.milliseconds(500), not time.duration.seconds(0.5). Arithmetic overflow and division by zero are errors rather than silently wrapped values.

local deadline = time.now() + time.duration.minutes(5)
local remaining = deadline - time.now()
if remaining > time.duration.seconds(0) then
	local seconds = time.duration.toMilliseconds(remaining) / 1000
	decoration.text = string.format("Remaining: %.0f s", seconds)
end

storage.set("deadline", time.instant.toMilliseconds(deadline))
local restored = time.instant.milliseconds(storage.get("deadline"))

Store signed milliseconds, then reconstruct the corresponding instant or duration. Storage/JSON do not accept semantic time values themselves. time.instant.toMilliseconds is an absolute instant, not local calendar fields. For display, use time.localDateTime(instant): it returns year, month, day, hour, minute, second, millisecond, and weekday (Monday 1 through Sunday 7) in the OS-configured timezone. There is no public timezone-management API. Scheduling explains restoring deadlines and effects of clock changes.

Copied structured values can be nil, booleans, signed 64-bit integers, finite numbers, valid UTF-8 strings, contiguous one-based arrays, or string-keyed objects. Sparse/mixed tables, non-integer numeric keys, cycles, invalid UTF-8, NaN/infinity, functions, threads, and arbitrary userdata/handles are rejected. Each API states whether it accepts these values or a more specialized type.

json.encode(value) and json.decode(text) are synchronous computation returning the result or nil, error. The error has operation ("encode" or "decode"), code, message, and sometimes a zero-based UTF-8 byte offset for malformed input. An unmarked empty table encodes as []; use json.object() for {}. Use json.null to keep a null member, because assigning Lua nil removes a table field. Decoded objects keep object shape even after their members are removed. Numeric spelling and arbitrary precision are not preserved.

local payload = { title = "Reminder", detail = json.null, settings = json.object() }
local text, encodeError = json.encode(payload)
if not text then
	log.error(encodeError.message)
else
	local decoded, decodeError = json.decode(text)
	if decodeError then
		log.error(decodeError.message)
	elseif decoded.detail == json.null then
		log.info("Detail was explicitly null")
	end
end

JSON conversion neither writes storage nor starts a network request. Concepts covers persistence and diagnostics; Desktop combines HTTP with checked JSON decoding.

Pure-Lua Modules

The pure-Lua module system starts with:

./?.lua;./?/init.lua

It does not load native modules. package.loadlib, package.cpath, native searchers, dofile, and loadfile are unavailable.

The package surface is package.loaded, package.preload, package.path, package.searchers, and package.searchpath. require("format.status") substitutes format/status into each ? pattern, then loads text source through the filesystem API. Relative patterns use the imported-source directory or the captured host working directory; see Files and Modules. Set absolute patterns when sharing modules across definitions. Change /home/alex/tms-lua below to your own directory:

package.path = "/home/alex/tms-lua/?.lua;/home/alex/tms-lua/?/init.lua"
local formatter = require("format.status")

function onLoad()
	decoration.text = formatter.count(3)
end

Create the corresponding /home/alex/tms-lua/format/status.lua file separately:

local formatter = {}

function formatter.count(value)
	return "Count: " .. value
end

return formatter

These are separate files, not one combined script. Modules run in the same Lua state and capabilities as their caller, not in isolated runtimes. package.loaded caches truthy results by exact module name; a module returning no result becomes true. Updating a module file does not replace a cached module automatically: reload the Decoration to start a fresh module state. There is no native loader, module whitelist, package manifest, or TaskManSync library folder. User-provided Lua preload functions and pure-Lua searchers are supported.

package.searchpath(name, path[, sep[, rep]]) only substitutes and normalizes paths and returns the first candidate. Unlike standard Lua filesystem search, it does not check that a file exists. Use require to actually load a module. Missing files, invalid source, or failed module initialization can raise a Lua error; Import/Save compile-only checks do not execute require or validate module contents.