There’s a brand new version of WP Cron Pixie available to install, and with luck, even though it’s changed immensely under the hood, you’ll not see any difference compared to the previous version! 😄

WP Cron Pixie is a little WordPress dashboard widget to view the WordPress cron, and run an event now rather than later.
Gleam Front End
The front end has been rewritten in Gleam, a “friendly language for building type-safe systems that scale“!
Back in May 2023 I got interested in Gleam after seeing Kris Jenkins interview Louis Pilfold on the Developer Voices podcast, I loved the idea of a type safe functional language with a clean but familiar syntax, that compiles to Erlang to run on the BEAM, or to JavaScript to run basically everywhere.
I poked around at Gleam now and then, not really getting anywhere, and decided I needed to write some project in it, that’s usually the best way for me to pick up a language. So back in April 2024 I started rewriting WP Cron Pixie’s front end from Elm to Gleam as part of my YouTube channel.
I recorded a few episodes, but alas, life got in the way, and I stopped recording videos for my YouTube channel while distracted by some personal and work projects, including moving house.
It’s only recently that things have settled down a little, and I’ve started to have a bit of time to start working on personal software projects again. Unfortunately my new home office is a horrible echo producing machine, so I can’t currently record any videos. So, until I can shoe-horn some soft surfaces into my small office to dampen the sound, I’m just enjoying working on projects without someone watching over my shoulder. 😉
After messing about with Kamal and Concourse CI, and then experimenting with how to test a Gleam project with that setup, I had the itch to get back into writing Gleam again.
Since I first started this project over a year ago, a bunch of things have improved in the world of Gleam, including decoders, which is what I’d been working on at the time I stopped. This meant I had to change quite a bit of the Gleam code I’d already written for decoding the data loaded by the widget when first shown, but to be fair, I hadn’t really got much done yet anyway.
I found the new decoder setup really nice, if still a little tricky to get my head around at first, but after a bit of help via the super nice folks in the Discord community, I was off and running. I built a bunch of data decoding into a data module, using TDD to make sure it all worked even though I had no UI yet to view the decoded data.
And of course, although I used watchexec
to continuously run the tests and build and install the plugin into my local test site every time I saved changes, I also set up a concourse-ci.yml file to make sure any commits I pushed up to the repo got tested by my dinky little Concourse CI server.

With the initial data being correctly decoded as flags for the Lustre based UI, I found it relatively quick and easy to build out the UI given that I could effectively copy the same layout as I’d used in the Elm version.
I started off the UI rendering conversion by simply grabbing the output HTML from WP Cron Pixie .1.4.4, and using Louis’ HTML to Lustre Converter to get a static rendering using Lustre syntax. From there, I chopped up the pieces into separate views to handle converting the data Model into things like a list of cron schedules, each with a list of cron jobs, while retaining the same classes that the existing CSS used. In the end, I don’t think I changed the CSS at all.
Compared to how I’d structured the Elm code, I did make some small improvements along the way while building out the UI in Gleam and Lustre, but having the Elm code for reference definitely sped up the development time. The UI code is looking pretty sweet if you ask me! 😊
One thing that is different in Gleam and Lustre compared to Elm is how you make a timer, like what I needed to trigger the auto-refresh of the UI every 5 seconds. In Elm you have subscriptions, and can subscribe to a time tick. However, with Lustre, there is no built in subscription feature, but it does still have effects, and Gleam has a very powerful foreign function interface (FFI) mechanism. This meant I could create a very simple JavaScript timer:
/**
* Start a periodic timer that fires a callback on a regular cadence.
*
* @param {int} interval Seconds between each tick.
* @param {object} callback A function to be called on each tick.
*/
export function set_interval(interval, callback) {
window.setInterval(callback, interval * 1000);
}
And then use that in the UI:
@external(javascript, "./internal/tick.mjs", "set_interval")
fn set_interval(_interval: Int, _callback: fn() -> a) -> Nil {
Nil
}
/// Start a timer that ticks every given number of seconds.
fn start_timer(seconds interval: Int) -> effect.Effect(Msg) {
use dispatch <- effect.from
use <- set_interval(interval)
dispatch(Tick)
}
/// Handle tasks every time the timer ticks.
fn handle_tick(model model: Model) -> #(Model, effect.Effect(Msg)) {
case model.auto_refresh {
True -> #(
Model(..model, refreshing: True),
get_schedules(model.admin_url, model.nonce),
)
False -> #(model, effect.none())
}
}
It was then easy to hook into the dispatched Tick
message within the update function:
fn update(model: Model, msg: Msg) -> #(Model, effect.Effect(Msg)) {
case msg {
Tick -> handle_tick(model)
RefreshSchedules -> #(
Model(..model, refreshing: True),
get_schedules(model.admin_url, model.nonce),
)
...
}
The timer is actually started from the init function:
fn init(flags: String) -> #(Model, effect.Effect(Msg)) {
...
#(model, start_timer(model.timer_period))
}
Hooking up the change events in the form fields in the widget’s settings footer, and handling clicks was super easy with Lustre, and Bob’s yer Uncle, I had a fully working WordPress widget using Gleam for building the UI.
Back End Improvements
Having built out the UI with Gleam, I then set about improving the back end PHP code.
I made quite a few changes, fixed a couple of bugs, improved security, separated the AJAX handing code from the underlying handling code in case I decide to switch to a WP-REST-API mechanism, and generally cleaned things up.
To aid with the cleanup, I added PHPStan and PHP_CodeSniffer to the mix to perform static analysis and code style checks respectively.
PHPStan is currently passing at level 9, just one step down from the max of 10, which is unfortunately impossible to get to due to some of the referenced WordPress functions, and with only a couple of ignored rules that are due to the way a couple of WP cron functions need to be used.
Likewise, the PHP_CodeSniffer checks are passing all the extended WordPress rules, with just an exclusion because the plugin needs to create its own cron schedule, and the ruleset is currently using a deprecated rule that naturally needs to be skipped.
That Was Fun
All in all, I had a lot of fun rewriting WP Cron Pixie’s UI in Gleam, and improving the PHP side of things too.
I am now thinking about what improvements I can make to WP Cron Pixie, might have a crack at using Birdie to snapshot test the UI, and wondering what my next Gleam based project will be as I’ve definitely got the “Gleam ALL THE THINGS” bug!