Build a Scriptable Script Launch Page ... in Scriptable

In reference to the ‘Keep Scripts order across devices’ post and based on the same idea as discussed in the 'Calling a scriptable from a scriptable, here’s an example script that when run displays lists of scripts that can be run (or edited). You can easily build a custom order and grouping and since this is in and of itself a script it will be automatically sync’d across devices.

The example below is one I put together that simply links to some scripts that I have on my devices. You must modify the content of the script for your device.

It could of course be genericised to use a file of data, but for the simplicity of sharing a starting point for people I’ve put the data in situ at the beginning of the script.

Select to reveal the script
//This defines the lists of scripts
//	name = text to display in the list
//	parent = name of any parent list item (matches against 'name')
//	script = exact name of script.  If blank, list item will be a heading
//	linebreak = true if a line break should be added before the list item. Onl applies to headers

const scriptData = [
{
	"name": "Default Scripts",
	"parent": "",
	"script": "",
	"linebreak": true
},
{
	"name": "Utility",
	"parent": "",
	"script": "",
	"linebreak": true
},
{
	"name": "Welcome Script",
	"parent": "Default Scripts",
	"script": "Welcome to Scriptable"
},
{
	"name": "Examples",
	"parent": "",
	"script": "",
	"linebreak": true
},
{
	"name": "Select File Path",
	"parent": "Examples",
	"script": "Select File Path Example"
},
{
	"name": "Alerts",
	"parent": "Examples",
	"script": "",
	"linebreak": false
},
{
	"name": "Alert 1",
	"parent": "Alerts",
	"script": "Alert Example"
},
{
	"name": "Alert 2",
	"parent": "Alerts",
	"script": "Alert Example"
},
{
	"name": "Library",
	"parent": "Examples",
	"script": "Library Example"
},
{
	"name": "Base64",
	"parent": "Utility",
	"script": "Base64"
}
];

//A bit of HTML wrapper content for our list
const strHTMLStart = `<html>
<head>
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto'>
<style>
html,body{font-family:"Roboto", sans-serif;font-size:15px;line-height:1.5; background-color: #ebf5d6;}html{overflow-x:hidden}
h1{font-size:36px; background-color: #5c7b1e; width: 98%; padding-left: 5px; color: white;}
h2{font-size:30px}h3{font-size:24px}
h1,h2{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}
.highlight {background-color: yellowgreen; padding-left: 5px; padding-right: 5px }
</style>
</head>
<body>
<h1>Script Menu</h1>
Select a script to run it, or the edit link next to it to open it for editing.  You can modify the lists by editing the JSON in <code>scriptData</code> in this script.
<hr/>
`;

const strHTMLEnd = `
<br/><hr/>
</body>
</html>`;


//Use a function to build indented lists
function buildLists(p_parent)
{
	return scriptData.filter(function(node)
	{
		return (node.parent === p_parent);
	}).map(function(node)
	{
		let exists = scriptData.some(function(childNode)
		{
			return childNode.parent === node.name;
		});
		let subList = (exists) ? 'n<ul>n' + buildLists(node.name).join('') + '</ul>n' : "";
		let strItem = '<li>';
		
		if(node.script === "")
		{
			//If specified, add a line break before the list item - we'll also highlight these entrie
			if(node.linebreak)
			{
				strItem = '<br/><li><strong class="highlight">' + node.name + '</strong>';
			}
			else
			{
				strItem += '<strong>' + node.name + '</strong>';
			}
		}
		else
		{
			//Add a little cog to this line
			strItem += '&#9881;&nbsp;';
			//Script - build the links
			strItem += '<a href="scriptable:///run?scriptName=' + encodeURI(node.script) + '">' + node.name + '</a>';
			strItem += '&nbsp;&nbsp;&nbsp;[<a href="scriptable:///open?scriptName=' + encodeURI(node.script) + '">edit</a>]';
		}
		strItem += subList + '</li>n';
		return strItem;
	});
}

//Compose and display the HTML
strHTML = strHTMLStart + '<ul>n' + buildLists('').join('') + '</ul>' +  strHTMLEnd;
console.log(strHTML);
WebView.loadHTML(strHTML);
Select to reveal a screenshot of how this looks on my iPad

Obviously if you want to do something similar outside of Scriptable, apps like Launcher, Launch Center Pro, etc. are the way to go. But I thought this was an interesting way that people might want to use to manage launches in app.

Hope people find this useful.

1 Like

Ha, that’s a lot of fun! :star_struck: Well done!

You could use the new APIs in FileManager to iterate over your scripts on disk and automatically generate the list. Then you could use a naming convention to group them in “Default Scripts”, “Utility” and “Examples”. E.g. “[E] Alert 1” goes into “Examples” and “[U] Base64” goes into utilities. You know, just for fun :blush:

1 Like

Thanks for the suggestion. This was just an example to give people to give them a base idea; but it is worth noting that just reading in the file name would not allow you to get ‘any’ ordering that you want, which was part of the reason for creating the example (as per the discussion thread on the custom ordering across devices). There is an obvious way around this in terms of also adding in a sequence number, but it has an issue which I’ll cover below in covering a broader approach which potentially has other applications.

Another option for something like the approach above would be to start adding file specific attributes into the script files that you could then parse in. Effectively, plain text custom meta data.

e.g.

/*
AUTHOR: Stephen Millard
LAST UPDATED: 2018-09-02
CATEGORY: Utilities/Alerts
CATEGORY ORDER: 2
TAG: Example, UI, Basic
/*

In ths way the script could also build the categories and sub categories automatically, using the order attribute to manage the order.

It would be marginally slower than just reading the file name, but I don’t think it would be noticeably so unless you have huge quantities of scripts. My experience of file access in Scriptable is it’s so quick as to be imperceptibly slower.

In effect this is adding plain text meta data to the script and there’s all sorts of scope for doing stuff with such easily accessible data. Tagging for dynamic filtering, listing all scripts updated this month and showing scripts where the author is @simonbs spring to mind :wink:

But, and here’s the rub, then you have to remember what order you want everything to appear in and if you want to insert something between two things the you have to amend each script to amend that order. In this particular case I think having the positional aspect being determined by the order you add the data set into a file (as in the example) is easier to maintain. Paticularly for longer lists of scripts where there could be many scripts to edit if you wanted to put a script near the top. You have to not only remember all the scripts to edit, but also make all of those edits to each script. This situation of course applies to the file naming approach as well.

When the ordering information is managed centrally and by position this then becomes a cut and paste exercise which is massively faster to maintain.

There are of course lots of options. You could use a menu system to load different pages built in different ways, for if you say wanted to have a custom ordered list of a subset of scripts and another option where all available scripts are listed alphabetically.

End of the day, I think the sorts of people using Scriptable and who might be interested in a script like this will want to customise it significantly. Hopefully we’ll see some variations posted back on this thread :smile:

2 Likes