Tips & Tricks for Apps in Wolfram

Created Friday March 31, 2023

This document contains an overview of useful features and techniques for implementing apps that leverage the capabilities of Wolfram Notebooks. Several useful conventions are also described.

Background

Notebooks are a flexible document format supporting styled text, code, typeset expressions, hierarchy, images, videos, user-interfaces and more. Because they build-in support for such a wide range of textual, visual, and programmatic functionality, notebooks are a great starting point to build new tools and apps, which immediately benefit from access to so many powerful primitives.

This document is my attempt to record and share some of the things I’ve learned while developing notebook-based applications. Examples of applications I’ve written include:

ConnorGray/Organizer — a TODO and document organization app
ConnorGray/NotebookWebsiteTools — a website authoring tool and static site generator

Styling & Stylesheets

Inspect Settings for a Named Style

Learning how a built-in style works is often a great starting point to adding functionality to styles in your own app. CurrentValue can be used to quickly query the FrontEnd about style options defined for a particular named style:

Creating Custom Stylesheets

Principles & Conventions
Every style provided by your paclet should be prefixed, ideally with your $PublisherID.
This ensures that your styles won’t conflict with styles provided either by other stylesheets, and that they are forwards-compatible with styles that may be added to future Wolfram System versions.
Minimize the number of stylesheets provided by an app. Ideally, only one.
This avoids visual issues that can arise when cutting and pasting cells from one notebook into another notebook that has a different stylesheet. If the pasted cells depend on styles that aren’t present in the stylesheet of the destination notebook, they will likely look and behave incorrectly.
Programmatically Creating Notebooks With A Custom Stylesheet

When programmatically creating a new notebook or setting the StyleDefinitions property of an existing notebook there are two types of values that are typically desirable to use.

1. Named Stylesheet Notebook File

Path to a named stylesheet notebook that is resolved by the FrontEnd at runtime:

StyleDefinitions -> FrontEnd`FileName[
	{"ConnorGray"},
	"NotebookWebsiteTools.nb",
	CharacterEncoding  UTF-8
]
2. Inline Notebook[..] Stylesheet

Inline stylesheet notebook, that may optionally inherit style settings from a named stylesheet notebook:

StyleDefinitions -> Notebook[{
	Cell[StyleData[StyleDefinitions -> "ConnorGray/NotebookWebsiteTools.nb"]],
	(* Inline style definitions here *)
	...
},
	StyleDefinitions -> "PrivateStylesheetFormatting.nb"
]

It is important the stylesheet notebook itself be styled by “PrivateStylesheetFormatting.nb” so that selecting the Format > Edit Stylesheet menu item shows a nicely formatted notebook like:

Instead of an unstyled raw stylesheet notebook like:

FrontEnd & User Interface

Adding Items to the File > New Menu

⚠️ This section makes use of undocumented features. Use these at your own risk.

FrontEndExecute[{
	FrontEnd`AddMenuCommands["New", {
		MenuItem[
			"<Something> Notebook",
			FrontEnd`KernelExecute[
				MessageDialog["It works!"]
			],
			FrontEnd`MenuEvaluator -> Automatic
		]
	}]
}]

Code Conventions

Organizing Package Code

A good notebook-based application should also provide an easy to use and coherent set of corresponding Wolfram Language functionality as well.

Convention: CreateProjectNotebook[..]

Principle: If your app uses notebooks that are specialized in some way, provide a function to create new blank notebooks.

Case Study: Organizer

ConnorGray/Organizer provides a CreateOrganizerNotebook[type, title] function, where values of type include:

“Log”
“Design”
“Tasklist”
Case Study: NotebookWebsiteTools

ConnorGray/NotebookWebsiteTools provides a CreateWebsiteNotebook[type, title] function, where values of type include:

“BlogPost”
Convention: UI Actions ⇔ Code

Principle: Every UI action should correspond to a short piece of Wolfram code, ideally a simple function call.

The convention I follow is to back every interactive elements in the UI, for example buttons or popup menus, with an associated function in the UI` subcontext of my app’s main Wolfram package.

Case Study: NotebookWebsiteTools

ConnorGray/NotebookWebsiteTools has a ConnorGray`NotebookWebsiteTools`UI` context: