Widget template for newbies like me

Hi all. I’m still fumbling my way around Javascript, so I kept getting confused by all these very cool widget examples — especially on my phone (where I want widgets!). It was hard to separate the basic widget code, which I wanted to adapt to my own purposes, from the magic that got the widget contents.

Anyway, by trial and error, I pared down @mzeryck’s very nice calendar widget to the bare essentials, which I can now build back out into something more useful. In case it’s helpful for anyone else, here’s a very simple Hello World widget for adapting to your own uses.


// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: calendar-alt;

// Adapted from calendar widget created by Max Zeryck @mzeryck

// Store current datetime
const date = new Date()

// If we're running the script normally, go to the Calendar.
if (!config.runsInWidget) {
 const appleDate = new Date('2001/01/01')
 const timestamp = (date.getTime() - appleDate.getTime()) / 1000
 const callback = new CallbackURL("calshow:"+timestamp)
 callback.open()
 Script.complete()
 
// Otherwise, create the widget.  
} else {

 let widget = new ListWidget()
 
 widget.addText("hello world")
 
 // Finalize widget settings
 widget.setPadding(16,16,16,0)
 widget.spacing = -3
 
 Script.setWidget(widget)
 widget.presentSmall()
 Script.complete() 
}

9 Likes

Thanks! I was looking at all the examples and felt a bit overwhelmed, but after toying around with this one first I started to figure out the more complex stuff :slight_smile:

Excellent! That was my experience as well!

it says calender not allowing but i dont get any prompt :face_with_monocle:

Hm. I thought I took out the parts requiring calendar access. You might try opening it inside Scriptable and then running it there. Iirc, it should prompt you for access to your calendar.

If I’ve understood the Scriptable APIs and expectations correctly, there are a few things that can be improved for use as a script starter template.

While @mzeryck’s examples have been amazing learning resources, for something to pare down to essentials, I’d recommend looking at the widget example provided with Scriptable itself, the News in Widget.js script that you can find in the Scriptable directory. Differences from the above template include:

  • Use of await and async.
  • Separation of widget presentation, and the actual widget construction.
  • Optimized use of the widget lifecycle functions (presentXYZ(), setWidget(), complete())

I’ve taken News in Widget.js, and converted it into (1) an even simpler example, Widget-HelloWorld.js below, and (b) an even further simplified widget “shell” or template, Widget-Template.js (next post).

I’ve also attempted to add some additional comments to explain the structure.

HTH!

Widget-HelloWorld.js

// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: red; icon-glyph: hand-paper;

// This script was extracted from the "News in Widget.js"
// example script included with Scriptable.
// Simplification and comments by Michael Alderete

// These are "asynchronous" functions, which is a fancy way of
// saying that they can take some time, and you need to wait
let myData = await loadData()
let myWidget = await createWidget(myData)

// If the script isn't running as a Widget, 
// show a debugging preview of the widget, 
// in Scriptable itself
if ( ! config.runsInWidget) {
    await myWidget.presentMedium()
}

// Finalize widget for display on Home screen
// This is the "real" way to present a widget
Script.setWidget(myWidget)
Script.complete()
// End of script execution


///
/// Implementation functions
/// 

// "Slow" data loading function
async function loadData() {
    // This function loads essential data, e.g., via web request
    // See real example in "News in Widget.js"
    let items = [
        {
        title: 'Hello world!',
        pubDate: new Date('1984-01-24T12:00-0800') // a good day
        }
    ]
    return items
}

// Widget creation, contents, layout, etc.
// This is all the "what goes inside" the widget.
async function createWidget(items) {

    // Processing and other calculations for determining the 
    // content of the widget
    let item = items[0] // the first (and here, only) item
    
    // Convert a JavaScript Date object to a string for display
    const dateLocale = 'en-US'
    const dateFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }
    pubDateForDisplay = item.pubDate.toLocaleDateString(dateLocale, dateFormatOptions)

    // Create the widget object
    let widget = new ListWidget()

    // Set gradient for widget background
    let gradient = new LinearGradient()
    gradient.locations = [0, 1]
    gradient.colors = [
        new Color("#b00a0fe6"),
        new Color("#b00a0fb3")
    ]
    widget.backgroundGradient = gradient

    // Add "real" content to the widget

    // First, add a flexible spacer above content 
    // to center it vertically
    widget.addSpacer()

    // Add a headline
    let titleTxt = widget.addText(item.title)
    titleTxt.font = Font.boldSystemFont(24)
    titleTxt.textColor = Color.white()
 
    // Add a little _fixed_ spacing below headline
    widget.addSpacer(8)

    // Add an article date
    let dateTxt = widget.addText(pubDateForDisplay)
    dateTxt.font = Font.mediumSystemFont(16)
    dateTxt.textColor = Color.white()
    dateTxt.textOpacity = 0.9

    // Add flexible spacer below content 
    // to center it vertically
    widget.addSpacer()

    // Return the fully composed widget object to the main script
    // The main script is responsible for actually presenting the widget
    return widget
}
2 Likes

And here’s the template.

Widget-Template.js

// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: light-gray; icon-glyph: copy;

// This is a minimal Widget script template
// See Widget-HelloWorld.js for something slightly more interesting

// These are "asynchronous" functions, which is a fancy way of
// saying that they can take some time, and you need to wait
let myData = await loadData()
let widget = await createWidget(myData)

// If the script isn't running as a Widget, 
// show a debugging preview of the widget, 
// in Scriptable itself
if ( ! config.runsInWidget) {
    await widget.presentMedium()
}

// Finalize widget for display on Home screen
// This is the "real" way to present a widget
Script.setWidget(widget)
Script.complete()
// End of script execution


///
/// Implementation functions
/// 

// "Slow" data loading function
async function loadData() {
    // This function loads essential data, e.g., via web request
    return 'Hello world'
}

// Widget creation, contents, layout, etc.
// This is all the "what goes inside" the widget.
async function createWidget(theMessage) {

    // Create the widget object
    let widget = new ListWidget()
    widget.backgroundColor = new Color("#4a525a")  // a nice gray

    // Add content to the widget

    // Flexible spacer above content to center it vertically
    widget.addSpacer()

    // Add a line of text
    let titleTxt = widget.addText(theMessage)
    titleTxt.font = Font.boldSystemFont(24)
    titleTxt.textColor = new Color("#eeeeee")  // almost white
 
    // Flexible spacer below content to center it vertically
    widget.addSpacer()

    // Return the fully composed widget object to the main script
    // The main script is responsible for actually presenting the widget
    return widget
}
3 Likes

That’s great! It would be neat if there were a library of templates for different kinds of scripts or widgets. I found the RSS widget example to be pretty easy to adapt, but others were too specific to a particular use (at least for me to adapt easily).

If you haven’t seen this post on Reddit yet, here’s a link to what is by far the best written JavaScript code I’ve seen for a Scriptable-based widget.

The code is available on GitHub, here:

This is a great example to start working from, much better than any I’ve seen elsewhere, and even my own examples above. (This despite the typo in the repo name!)

2 Likes