Simplifies creating Alfred workflows using Scala 3.
After creating your Alfred workflow, create a workflow.sc
file with the following content:
#!/usr/bin/env -S scala-cli shebang
//> using dep "com.alejandrohdezma::alfred4s:2.0.1"
import alfred4s.syntax.*
alfred4s.app(args) {
case "hello" :: Nil =>
item("Hello world!")
case "urls" :: environment :: Nil =>
items(item("https://example.com/1"), item("https://example.com/2"))
}
Then call it from your Alfred's Script Filter like:
./workflow.sc hello
Remember to run
chmod + workflow.sc
!
If you want to avoid cold JVM starts you can also create a GraalVM native image and execute the generated binary instead. See Packaging as GraalVM native images in Scala CLI documentation.
To create result rows to be shown in Alfred's main window you can use alfred4s.items
.
Then you pass in as many Item
as you want using alfred4s.item
. The item's title
is mandatory, but all of the other fields are mandatory. These are the available fields
and methods:
import alfred4s.syntax.*
// Changes the title displayed in the result row
item("Original title").title("This is title that will be displayed")
import alfred4s.syntax.*
// A unique identifier for the item. It allows Alfred to learn about the item
// for subsequent sorting and ordering of the user's actioned results.
//
// It is important that you use the same UID throughout subsequent executions of
// your script to take advantage of Alfred's knowledge and sorting. To show
// results in the order you return them from your script, exclude the UID field.
item("This will be the title").uid("1234")
import alfred4s.syntax.*
// The subtitle displayed in the result row
item("This will be the title").subtitle("This will be the subtitle")
import alfred4s.syntax.*
// Enables you to define what Alfred matches against when the workflow is set to
// 'Alfred Filters Results'.
//
// If match is present, it fully replaces matching on the title property.
//
// The match field is always treated as case insensitive, and intelligently
// treated as diacritic insensitive. If the search query contains a diacritic,
// the match becomes diacritic sensitive.
item("This will be the title").matching("alfred will match user input on this string")
import alfred4s.syntax.*
// The argument which is passed through the workflow to the connected output action.
//
// While optional, it's highly recommended that you populate arg as it's the
// string which is passed to your connected output actions. If excluded, you
// won't know which result item the user has selected.
item("This will be the title").arg("Zelda")
import alfred4s.syntax.*
// The icon displayed in the result row. Workflows are run from their workflow
// folder, so you can reference icons stored in your workflow relatively.
item("This will be the title").icon("icons/kratos.png")
import alfred4s.syntax.*
// Adds a new modifier to this item. Modifiers gives you control over how the
// modifier keys react. It can alter the looks of a result (e.g. subtitle, icon)
// and output a different arg or session variables.
item("This will be the title").mod("cmd")(alfred4s.mod.arg("false"))
import alfred4s.syntax.*
// Adds a new variable to this item. Variables are passed out of the Script
// Filter object and remain accessible throughout the current session as
// environment variables if the associated result item is selected in Alfred's
// results list.
item("This will be the title").variable("item_id", "1234")
import alfred4s.syntax.*
// Whether the item is valid or not. If an item is valid then Alfred will action
// it when the user presses return. If the item is not valid, Alfred will do
// nothing. This allows you to intelligently prevent Alfred from actioning a result
// based on the current `{query}` passed into your script.
//
// By default, Alfred assumes your item is valid.
item("This will be the title").validIf(userId.nonEmpty)
import alfred4s.syntax.*
// Defines the text the user will get when displaying large type with ⌘L
item("This will be the title").largetype(someLargeString)
import alfred4s.syntax.*
// If the item should be hidden from results or not
item("This will be the title").hideWhen(isInvalid)
As seen in Usage, you can use alfred4s.app
to create a simple executable
script for your Alfred Script Filters. This method will take the arguments array
as a parameter and lets you specify your script's behaviour using a partial function
to match individual arguments:
alfred4s.app(args) {
case "hello" :: Nil =>
item("Hello world!")
case "urls" :: environment :: Nil =>
items(item("https://example.com/1"), item("https://example.com/2"))
}
You can indistinctly return Item
, Seq[Item]
or Items
inside the main method.
You can also return Unit
, in case you want to use the same file for the "Run Script"
components of your workflow.
This library provides a convenient alfred4s.notify
method, that can be used as
an alternative to using Apple Script (or Alfred's workflow component) to send
notifications. The main difference is that notifications created with this method
will keep the workflow's icon. It mainly uses the same approach than
vitorgalvao/notificator.
Creates a small specialised app for the workflow, so we can send MacOS notifications with the workflow's icon.
The first time this method is called it creates an app in the workflow's cache folder and then use it to send the provided notification.
Subsequents runs of this method will used the already provided app (unless workflow's cache is cleared).
If your workflow is hosted on GitHub, you can use alfred4s.appWithNewVersionCheck
instead of alfred4s.app
to create your main body. This method will receive
the same arguments than app
but will also request a GitHub owner & repository
(and a valid GitHub token) and will check if the workflow has a new version on a
schedule. By default it will run a check the first time the workflow is executed
per day. If a new version is found, it will open the latest release page so your
user can download the latest version.
alfred4s.appWithNewVersionCheck(args, "my-org", "my-workflow", gitHubToken) {
case "hello" :: Nil =>
item("Hello world!")
case "urls" :: environment :: Nil =>
items(item("https://example.com/1"), item("https://example.com/2"))
}
Alfred offers some useful variables
prefixed with alfred_
with metadata and information about Alfred's instance as
well as the running workflow.
You can access these variables from alfred4s.workflow
and alfred4s.alfred
.
Alfred offers out-of-the-box caching since Alfred 5.5.
This cache can be enabled by calling cache
method on an Items
instance:
import alfred4s.syntax.*
import scala.concurrent.duration.*
items(item("My item")).cache(10.minutes)
The TTL passed to the cache
method must be between 5 seconds and 24 hours. The
method will throw an exception if the value is not within those boundaries.
Once a cache TTL has been established, you can enable "loosereload" by calling
enableLooseReload
. This asks Alfred's Script Filter to try to show any cached
data first. If it's determined to be stale, the script runs in the background
and replaces results with the new data when it becomes available.
Important! Take into account this cache cannot be used in Script Filters whose
output depends on the input, since only the first output will be cached and
subsequent calls will use that value, even if the input is different. For those
cases this library offers another method for caching your filters: alfred4s.cached
.
This method can be called to wrap any Items
or Seq[Item]
calculation for a
specific period of time, using a key. The data will be serialized into the
workflow's cache folder under a file named as the key and will be read in subsequent
calls until the file becomes stale:
def sayHello(name: String): Seq[Item] = ???
def getUrlsFor(environment: String): Seq[Item] = ???
alfred4s.app(args) {
case "hello" :: name :: Nil =>
alfred4s.cached(s"hello_$name", 5.days)(sayHello(name))
case "urls" :: environment :: Nil =>
items(getUrlsFor(environment)).cache(12.hours).enableLooseReload
}
alejandrohdezma |