# Ahoy, world!

Build Command Bar commands for yourself, or to share with others.

Welcome to the [Slapdash](https://slapdash.com) platform. To start, we're opening up a way to extend the functionality of the Slapdash Command Bar, letting you write your own commands.

You can start by learning [how the Command Bar works](/command-bar-101/how-it-works), pause to flip through the [core terminology](/command-bar-101/core-terminology) and then have yourself a good sleep to make sure you retain what you read.

Otherwise, we fully encourage you to throw caution to the wind and just [start building your first command](/command-bar-101/setup-your-first-command). Don't think, just recklessly copy-paste these examples to your editor and see what comes out on the other side.

#### **Get Help**

We're here to help and if you want to reach us, just click `?` in Slapdash or the chat icon at the bottom right corner to send us a message. You can also email us at <hello@slapdash.com>.


# How it Works

The Command Bar is a keyboard-centric interface for controlling your computer and cloud apps; with an emphasis on agency, speed and usability. The name is a combination of *command* line and search *bar*.

Like a command-line shell (i.e. bash, zsh), you can type your way to perform any task. Similarly, it’s also customizable and programmable.

At the same time, it's as easy to use as a search bar. You don't need to learn anything: just type what you want in the Command Bar as you would in Google.

## Opening the Command Bar

If you've ever used Mac's Spotlight feature, opening the Command Bar will be very familiar. It can be open from any application with one keyboard shortcut (**`⌘ J`** is the default for Slapdash). The Command Bar will appear on top of the application you are in. Clicking anywhere outside of it or hitting Esc will hide the Command Bar. To bring it back, just press the shortcut again.

## Doing Something

When the Command Bar is open, you interact with it by typing. As you type, the Command Bar will show you interactive options, narrowing them down as you type.

Hit **`Enter`** or click on an option to do something. Hitting **`Enter`** on a search result will open it. Hitting **`Enter`** on a command will run it.

More precisely, hitting **`Enter`** runs the Action associated with the selected option. Every option will always have an Action.

![The Command Bar has returned some commands and search results that match keywords.](/files/-MbsdRSbvJGL9YsSJg11)

## Moving Somewhere

If you can open the Command Bar, type something and hit **`Enter`**, that's all you really need to know.

However, there is one more important dimension to the Command Bar: *location*.

Like a web page, the Command Bar has an address it's open to. And you can move to a different location by hitting **`Tab`**. It allows you to continue the experience in the Command Bar: like clicking a link to a different part of the app.

Hitting **`Tab`** on a search result will give you new options like **Copy URL** or **Add to Space**, for example.

The Location is visualized with pill-like tokens in the input of the Command Bar. If this was a URL, the path equivalent to the screenshot below would be: `/search?filter=github`

![Here the Command Bar is open to the Search command, with a Github filter applied.](/files/-Mbse1qJmlfiQQcJjrVY)

## Putting it Together

In the below demonstration, the Command Bar is opened and "show" is typed into the input.

The Command Bar view displays **Show Calendar Schedule** as one of the available options and selects it as the best match.

Hitting **`Enter`** moves the Command Bar to the **Show Calendar Schedule** command's location. Here, **`Tab`** would have gotten you to same place.

With the movement, the Command Bar now shows a "Show Calendar Schedule" token, which means you are running the "Show Calendar Schedule" command. The command shows your upcoming meetings as options.

Hitting **`Tab`** on an event (one of the options) moves to a new location in the Command Bar that shows event details.

**`Shift+Tab`** reverses the movement and brings you back to the list of events.

Hitting **`Enter`** on the event option runs the **Action**, which in this case is to open the Zoom app directly to the meeting video chat.

![](/files/-Mbsed4Fn9WLlEv8MvYt)


# Core Terminology

## Input and View

When the Command Bar is open, there are two parts: the **Input** and the **View**. The Input is where you type and the View shows the UI, which reacts to what’s typed inside the input.

Here’s an example of a Command Bar that has an Input, a math expression, and a View, the calculated result of the expression.

![](/files/-MbsfJglo4ZNuP0Ua8h0)

## Options

Although the View can technically be anything, most often it is built with a collection of UI components called an **Option**.

Each Option has an Action associated with, which is performed by hitting **`Enter`** with the Option selected. An action can be something like:

* Open a URL
* Copy text to Clipboard
* Open an application
* Run a script on the computer

Traditionally, the set of Options displayed in the view are based on the Input. By default, the Command Bar will fuzzy search the displayed Options, showing only ones that match.

Every Option has an Action, but some options may also have a **Move Action**. The Move Action is activated by hitting **`Tab`** and it moves the person to a different location in the Command Bar.

## Location

The Command Bar has a notion of location, or an address. Similar to how a browser has the address of which page is open. For every address, there is a Command that is responsible for generating the View.

Even when you first open the Command Bar, it points to a special location: root. Under the hood, there is a command, managed by Slapdash, that is responsible for generating the UI for this location.\
\
In the case of the root location, Slapdash will match all your available commands, search connected applications and even parse natural language expressions.

We don't have an addressing scheme yet, but the Command Bar location is visualized with pill-like tokens in the Input.

## Commands

Commands are the atomic units of functionality in Slapdash. [You can think of them as functions.](/command-bar-101/commands)\
\
Every command you add to Slapdash will get its own unique address and show up automatically in the root view as an Option.\
\
To run a command: open the Command Bar and just type a part of its name and hit **`Enter`**.


# Commands

The easiest way to think about commands is as functions. As a function, it can accept arguments as input and can return some output.

```
Command(Input) -> Output
```

Like a function, a command doesn't have to return anything. Imagine a command to put your computer to sleep: there is no output that's needed.\
\
However, most commands have an output, which tells Slapdash what to do or show in the Command Bar.\
\
This output is a JSON-serialized data structure that conforms to the [Command Response Specification](/reference/command-response).

You can use it to tell Slapdash to [copy something](/reference/command-response-action#actioncopy) to the clipboard, [open a URL](/reference/command-response-action#actionopenurl) in a browser, [show a form](/reference/command-response-view-form) to someone, or present an [interactive list view](/reference/command-response-view-list).

### Local vs. Cloud Commands

There are two types of commands: [Local Commands](/command-bar-101/local-commands), which are scripts that run on your computer, and [Cloud Commands](/command-bar-101/cloud-commands), which are commands that are hosted on the web.

They are mostly the same, but they do offer some trade-offs:

| Local Commands                    | Cloud Commands                    |
| --------------------------------- | --------------------------------- |
| Script on your computer           | Accessible by URL                 |
| Can do whatever your computer can | No direct access to your computer |
| Not shareable with team           | Shareable with team               |
| Great for development             | Great for production deployment   |
| Works offline                     | Unlimited compute                 |

When developing a new command, Local Commands are the fastest and simplest way to get things going.

### How Commands are Run

Local Commands are just scripts that are run on your computer. Slapdash uses stdin to send arguments to the command and interprets the stdout, expecting it to be a JSON [Command Response](/reference/command-response).\
\
Cloud Commands are run by making a request to an HTTPS endpoint. Slapdash passes arguments to the command using RESTful conventions. Slapdash interprets the HTTPS response, expecting it to be a JSON Command Response.

![](/files/-Mbsin-vZao1KKKZ1zSl)


# Local Commands

Commands that run scripts on your computer.

Local commands are just scripts that run on your computer. Slapdash knows how to run scripts in the most popular languages.

Even if Slapdash doesn't support your language out of the box, you can just [use shebang syntax](https://github.com/slapdash/platform/blob/main/templates/custom-shebang-not-bash.sh#L1), or create a bash wrapper to call into your program.

## Create Local Command

To create a local command, run **Create New Command** in the Command Bar, choose **Local Script** as a type, select the script file on your computer, give your command a name and press **Create Command**.

![](/files/-MhZAG77W2c6w6E_LG1T)

## Language Support

We try to support as many languages as possible. Below you'll find a list of languages you can use to build Slapdash commands.

In practice, you can use a shebang directive to point to an interpreter of your choice.

* AppleScript
* bash / zsh
* JavaScript
* Perl
* PowerShell
* Python
* Ruby
* PHP
* TypeScript

### AppleScript

Support for AppleScript is baked into macOS. Just create a `command.applescript` file anywhere and start experimenting!

Here's an example of an AppleScript command which [toggles the Mac OS system dark mode](/command-tutorials/toggle-dark-mode).

```applescript
#!/usr/bin/osascript

tell application "System Events"
  tell appearance preferences
    set dark mode to not dark mode
  end tell
end tell
```

### JavaScript

Slapdash runs JavaScript commands using [Node](https://nodejs.org).

Here is an example of a JavaScript command that displays a list with two options:

```javascript
// command.js

const response = {
  view: {
    type: "list",
    options: [
      {
        title: "Open Slapdash",
        action: {
          type: "open-url",
          url: "https://slapdash.com"
        }
      },
      {
        title: "Copy Heart Emoji",
        action: {
          type: "copy",
          value: "❤️"
        }
      }
    ]
  }
};

console.log(JSON.stringify(response));
```

When you run the command in the Command Bar, Slapdash will execute the underlying script:

```bash
node command.js
```

### TypeScript

Slapdash runs TypeScript commands using [NodeJS](https://nodejs.org) and [ts-node](https://www.npmjs.com/package/ts-node).

If you are building a command inside some package, Slapdash will also look inside its `node_modules` for the `ts-node` executable. If it can't find `ts-node` there it will look for `ts-node` in the global node modules location on your computer.

```typescript
// command.ts

// Optionally, import our npm package to get TS types for Command Response.
import { CommandResponse } from "@slapdash/command-response-types";

const response: CommandResponse = {
  view: {
    type: "list",
    options: [
      {
        title: "Open Slapdash",
        action: {
          type: "open-url",
          url: "https://slapdash.com",
        },
      },
      {
        title: "Copy Heart Emoji",
        action: {
          type: "copy",
          value: "❤️",
        },
      },
    ],
  },
};

console.log(JSON.stringify(response));
```

When you run the command in the Command Bar, Slapdash will execute the underlying script:

```bash
ts-node command.ts
```

### Bash

You already have [bash](https://en.wikipedia.org/wiki/Bash_\(Unix_shell\)) (or [zsh](https://en.wikipedia.org/wiki/Z_shell)) on your Mac. No need to install anything. Just create e.g. `command.sh` file anywhere and start bashing:

```bash
#!/bin/bash

echo '{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Slapdash",
        "action": {
          "type": "open-url",
          "url": "https://slapdash.com"
        }
      },
      {
        "title": "Copy Heart Emoji",
        "action": {
          "type": "copy",
          "value": "❤️"
        }
      }
    ]
  }
}'
```

### Python

macOS has both Python v2 and Python v3 pre-installed, so you can just use it by default. Put the following to e.g. `command.py`:

```python
#!python3
# -*- coding: utf-8 -*-
import json

print(json.dumps({
    "view": {
      "type": "list",
      "options": [
          {
              "title": "Open Slapdash",
              "action": {
                  "type": "open-url",
                  "url": "https://slapdash.com"
              }
          },
          {
              "title": "Copy Heart Emoji",
              "action": {
                  "type": "copy",
                  "value": "❤️"
              }
          }
      ]
      }
}))
```

You can use Python v2 too: just change the [shebang line](https://en.wikipedia.org/wiki/Shebang_\(Unix\)) to `#!python2` as usual.

### Ruby

macOS should have Ruby pre-installed (or you can install it with `brew install ruby`). Put the following to e.g. `command.rb`:

```ruby
#!/usr/bin/env ruby
require 'json'

print JSON.generate({
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Slapdash",
        "action": {
          "type": "open-url",
          "url": "https://slapdash.com"
        }
      },
      {
        "title": "Copy Heart Emoji",
        "action": {
          "type": "copy",
          "value": "❤️"
        }
      }
    ]
  }
})
```

### Perl

Perl is conveniently pre-installed on macOS. If you still remember how to write in Perl put the following to `command.pl`:

```perl
#!/usr/bin/perl -w
use utf8;
use JSON;

print encode_json({
  "view" => {
    "type" => "list",
    "options" => [
      {
        "title" => "Open Slapdash",
        "action" => {
          "type" => "open-url",
          "url" => "https://slapdash.com"
        }
      },
      {
        "title" => "Copy Heart Emoji",
        "action" => {
          "type" => "copy",
          "value" => "❤️"
        }
      }
    ]
  }
})
```

### PHP

Some version of PHP is pre-installed on macOS, so it should work out of the box. Put the following to e.g. `command.php`:

```php
#!php
<?php
echo json_encode([
  "view" => [
    "type" => "list",
    "options" => [
      [
        "title" => "Open Slapdash",
        "action" => [
          "type" => "open-url",
          "url" => "https://slapdash.com"
        ]
      ],
      [
        "title" => "Copy Heart Emoji",
        "action" => [
          "type" => "paste",
          "value" => "❤️"
        ]
      ]
    ]
  ]
]);
```

### PowerShell

[PowerShell](https://en.wikipedia.org/wiki/PowerShell) is already pre-installed on Windows. Just create a file with a `.ps1` extension. Here's an example that will empty your recycling bin.

```
Clear-RecycleBin -Force
```

## Locating Language Binaries

When searching for a programming language binary to execute the command script, we use the following approach:

1. We check the first line of the script, the same way it's done in Unix-like systems by looking for the shebang directive. If the first line looks like `#!/path/to/binary` or just `#!binary`, Slapdash calls the referenced interpreter to execute the command.
2. If there is no shebang directive, Slapdash will try to infer the language binary from the file's extension. For example, if it's a `*.rb` file, Slapdash will look for the installed Ruby binary.
3. If the script is within an npm package (JavaScript/TypeScript), Slapdash will check the current directory's `node_modules/.bin` folder to look for tools like `ts-node`.
4. Finally, Slapdash will use the OS's `PATH` environment variable to resolve the binary.


# Cloud Commands

Commands that are hosted on the web.

Cloud commands are hosted at URL endpoints. Slapdash makes HTTPS requests to the endpoint and interprets the JSON-encoded [Command Response](/reference/command-response).\
\
Commands that run in the cloud can't make any changes to your computer (it's just JSON over HTTPS after all), so they are a safe way to share Command Bar functionality with others.

## Create Cloud Command

To create a cloud command, run **Create Command** in the Command Bar, choose **Cloud** as a type, paste the **Endpoint URL** of your command, give it a name and press **Create Command**.

![](/files/-MhZ9TTopfTP20s6gLzI)

## Developing & Deploying with Replit

One of our favourite ways to host and develop cloud commands is with [Replit](https://replit.com).\
\
We like it because we can use any programming language to write the command, and we can do the development directly in the browser. It's a super quick way to get an HTTPS endpoint up and running with just a few clicks.\
\
**Fork Example**

The fastest way to start is to fork another Repl. Here are a few examples you can choose from:

* [Basic Python Example](https://replit.com/@slapdash/slapdash-command-python)
* [Send Slack Message](https://replit.com/@slapdash/send-slack-message)

**Run Command**

After forking the Repl, hit the **Run** button to make sure the associated HTTPS endpoint is accessible.\
\
Find the endpoint URL in the top-right panel. This is the URL you will use to create the command inside Slapdash.

![](/files/-McWAUxOOwjcOM2Bx0PF)

\
**Deploying & Sharing Command**\
By default, Repls aren't always running. If you plan to use the command often or to share it with others, you'll need to make sure the Repl is set to be **Always On.**

## Deploying with Vercel

You can also use Vercel to deploy a command as a [Serverless Function](https://vercel.com/docs/serverless-functions/introduction).

Start by creating a **New Project** inside Vercel and choose the **NextJS template**.

Create a new file in the project under `pages/api/ahoy-world.js`.

Then, add the following sample code and push to your repository to deploy it.

```javascript
module.exports = async (req, res) => {
  const response = {
    view: {
      type: "list",
      options: [
        {
          title: "Open Slapdash",
          action: {
            type: "open-url",
            url: "https://slapdash.com"
          }
        },
        {
          title: "Copy Heart Emoji",
          action: {
            type: "copy",
            value: "❤️"
          }
        }
      ]
    }
  };
  res.setHeader("Access-Control-Allow-Headers", "*"); // for config headers
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.json(response);
}
```

At this stage, you should have a functioning URL like `https://nextjs-***.vercel.app/api/ahoy-world` that you can use to create a command in Slapdash.


# Build Your First Command

Slapdash works great in the browser, but for the best development experience, we recommend using the [Slapdash desktop app](https://slapdash.com/download).

There are [two types of commands](/command-bar-101/commands#local-vs-cloud-commands): **Local Commands**, which are scripts that run on your computer, and **Cloud Commands**, which are commands that are hosted on the web. Developing is easier and faster with files on your computer, so that's the type of command we'll be writing here.

This example command will be written in JavaScript, but in practice, you can use [whatever language](/command-bar-101/local-commands#language-support) you are comfortable with.

### Create Command

* Create an empty file with a `.js` extension (for example: `ahoy-world.js`)
* Run **Create New Command** in the Command Bar and choose **Local Script**
* Choose the file you created by clicking the **Select File button.**
* Give the command a name, for example, "Run Demo"

### Do Something with the Command

For the Command to do something or show something in the Command Bar, it just needs to print some text. Specifically, the text needs to be JSON that matches the shape of the [Command Response Specification](/reference/command-response).

The most basic thing a command can do is return an Action as the [Command Response](/reference/command-response). An Action just tells the Command Bar to do some operation and exit. The simplest one is to just open a URL.

#### **Make Your Command Open a URL**

```javascript
const response = {
  action: {
    type: "open-url",
    url: "https://slapdash.com/"
  }
}

// Print the response as JSON string
console.log(JSON.stringify(response));
```

Another type of Action is to add something to your clipboard. You can see other Actions in the [Command Response Reference](/reference/command-response-action).

#### **Make Your Command Add Some Text to Your Clipboard**

```javascript
const response = {
  action: {
    type: "copy",
    value: "Ahoy world!"
  }
}

// Print the response as JSON string
console.log(JSON.stringify(response));
```


# Publish Command

Share your command with the Slapdash community

Once you're happy with your command you may want to share it with others.

A published command will appear as a "Community Command" on the [Slapdash developer site](https://slapdash.com/developers), allowing anyone to discover it and add it to their Slapdash account.

## Publish Cloud Command

To publish a command, you need to fork the [slapdash/platform](https://github.com/slapdash/platform) repository, create a new folder inside [commands](https://github.com/slapdash/platform/tree/main/commands) and then open a Pull Request. Once your PR is merged, your command will appear on the Slapdash site, allowing anyone to install it.\
\
Each command gets its own folder and needs to follow some simple conventions. Here's an example of how a new command might be added to the repository.

Please name your command folder using the [kebab case style](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles) (lowercase and hyphen used as a separator).

```
commands
└── my-command-name
    ├── command.toml
    ├── icon.svg
    └── screenshot.json
```

Each command folder needs to have three files: `command.toml`, `icon.svg` and `screenshot.json`. You can copy other commands as examples, or read about the role of each file below.

#### `command.toml`

This is a simple [TOML](https://toml.io) file that contains metadata about your command.

| Name              | Details                                                                                                                |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
| name              | **Required.** Name of the command, prefer \<Verb> \<Noun> convention                                                   |
| description       | **Required**. Short, one-sentence description                                                                          |
| endpointURL       | **Required**. Publicly accessible HTTPS endpoint for your command                                                      |
| author.name       | **Required**. Full name of the command's author                                                                        |
| author.profileURL | **Required**. Link to the author's public profile. For example, a link to a Github or Twitter profile.                 |
| categories        | List of categories the command belongs to. You can grab them from the [list below](#categories) or put something else. |
| readme            | Long-form explanation of the command. You can use Markdown if you want.                                                |
| language          | Language that was used to build the command. For example, "TypeScript" or "Python".                                    |
| sourceCodeURL     | Link to the source code of your command. For example, a link to a public Github repository.                            |

#### `icon.svg` or `icon.monochrome.svg`

This is the icon that will be used alongside your command, when it appears in the Command bar. If you want Slapdash to automatically change the icon's color based on the selected theme, use `icon.monochrome.svg`.

We recommend a 64x64 px icon with no padding around it. If you need help finding an icon, you can try using the [Search Iconfinder](https://slapdash.com/commands/search-iconfinder) command.

#### `screenshot.json`

This file is added by Slapdash employees so you don't need to provide it. It is a simple JSON file that contains one of the [Command Responses](/reference/command-response) that your command returned as well as some other metadata to help visualize the command on the Slapdash site.

## Publish Local Command

There is currently no way to publish a Local Command but we're actively working on it. It should be available very soon. Stay tuned!

## Categories

If you're not sure what category to put your command in, don't sweat it, we'll help categorize it. Here are some potential categories you might want to consider (or, just make up your own).

* Automation
* Books
* Business
* Developer Tools
* Education
* Entertainment
* Finance
* Food & Drink
* Games
* Health & Fitness
* Design
* Lifestyle
* Kids
* Magazines & Newspapers
* Medical
* Music
* Navigation
* News
* Photo & Video
* Productivity
* Shopping
* Social Networking
* Sports
* Travel
* Utilities


# Hooks

Choose where your command appears

By default, all commands appear at the Command Bar root. However, in some cases, you may want your command to show up in another context, e.g. when there is an active web page in focus.

Hooks let you achieve this. There is currently only one hook supported – URL Hook.

## URL Hook

URL hook allows you to "attach" your command to the "Active Tab" group in the Command Bar. This group appears at the top of the Command Bar root when there is a browser tab in focus.

!["Active Tab" group in the Command Bar](/files/Wp2wWKPZamqtSYufIBmf)

When your command is run, the page URL will be passed in the `url` parameter.

A URL Hook is a JSON object with the following properties:

| Property | Description                                                                                                                                                                                                                                                                                                                                                             |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| type     | **required**. The type of the hook. Must be `"url"`.                                                                                                                                                                                                                                                                                                                    |
| url      | The URL match pattern. Allows showing the command only when the page URL matches this pattern. For example, `https://`github`.com/*` matches HTTPS URLs only at "github.com", with any URL path and URL query string. Check out [WebExtensions Match Pattern](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns) for more examples. |

#### Examples

{% tabs %}
{% tab title="Any URL" %}
Show the command for any URL

```json
{
  "type": "url"
}
```

{% endtab %}

{% tab title="Github URLs only" %}
Show the command for Github URLs only

```json
{
  "type": "url",
  "url": "https://github.com/*"
}
```

{% endtab %}
{% endtabs %}

## Define Hooks

You can define Hooks using HTTP Headers for [Cloud Commands](/command-bar-101/cloud-commands) or code comments for [Local Commands](/command-bar-101/local-commands). In both cases, the hooks must be string representations of the JSON object. You can define a single Hook or multiple Hooks using an array.

### Cloud Commands

Your endpoint can define Hooks by sending a custom HTTP header – `X-Slapdash-Hooks`. For example, in NodeJS this can look like:

```javascript
async function myCommand(req, res) {
  // Your command logic is here.
  res.setHeader("Access-Control-Expose-Headers", "X-Slapdash-Hooks");
  res.setHeader("X-Slapdash-Hooks", JSON.stringify({type: "url"}));
}
```

Note, you also need to set `Access-Control-Expose-Headers` to the list of custom hooks that you want to expose. This ensures Slapdash can access `X-Slapdash-Hooks` when making a request to your endpoint.

### Local Commands

To specify Hooks for Local Commands, you can use code comments in the following format:

{% code title="command.js" %}

```javascript
// @slapdash.hooks '{"type":"url"}'

// You command logic is here.
```

{% endcode %}


# Toggle Dark Mode

A Mac OS command that uses AppleScript

![Run Toggle Dark Mode in Command Bar](/files/ihkzuuNpnBMXhqiTdaGJ)

This is an example that will only work on macOS because it uses AppleScript. It's an example of a command that doesn't return a response: it just does its thing and exits.

When the command is run, it will toggle the dark mode system preference setting.

Paste the following code in a new file with a `.applescript` extension and then [create a Local Command](/command-bar-101/local-commands#create-local-command) to try it out in Slapdash.

```javascript
tell application "System Events"
  tell appearance preferences
    set dark mode to not dark mode
  end tell
end tell
```

[See on Github](https://github.com/slapdash/platform/tree/main/tutorials/toggle-dark-mode)


# Emoji Paster

Search & paste emojis

![](/files/-Mdrd6q7PKyz1zprpFuM)

This command illustrates how to return an interactive [List view](/reference/command-response-view-list). In this case, we visualize emojis and their names. As you type, the command matches the emoji name. When you hit **Enter**, the command pastes the selected emoji in the active app.

In this example, you'll also notice that you can use an emoji as a custom icon for an [Option](/reference/command-response-view-list#listoption).

```javascript
/**
 * A command that shows a selection of emojis and pastes
 * the selected emoji on pressing Enter.
 */
const response = {
  view: {
    type: "list",
    options: [
      {
        title: "Heart",
        action: {
          type: "paste",
          value: "❤️",
        },
        icon: "❤️",
      },
      {
        title: "Star",
        action: {
          type: "paste",
          value: "⭐️",
        },
        icon: "⭐️",
      },
      {
        title: "Lightning",
        action: {
          type: "paste",
          value: "️⚡️",
        },
        icon: "⚡️",
      },
    ],
  },
};

console.log(JSON.stringify(response));
```

[See on Github](https://github.com/slapdash/platform/tree/main/tutorials/emoji-paster)


# Copy Special Character

Find a special character, then copy the character or its hex value

![](/files/-MdrheMligah4ddSbWk_)

This command illustrates an [interactive List](/reference/command-response-view-list), as well as using **Tab** to navigate to another location within the command.

In this case, hitting **Enter** on an Option will copy the special character, while hitting **Tab** will show another view letting someone choose to copy the hex value instead.

```javascript
/*
 * A command that shows special characters. Hitting Enter
 * will copy character, hitting Tab will give other ways
 * to copy the character: HTML hex code, for example.
 */

/**
 * Gets arguments passed in to script, like: --character=control
 * and stores it into an array, like: args['character'] = "control".
 */
const args = process.argv.slice(2).reduce((agg, arg) => {
  const match = arg.match(/^--(?<key>\w+)=(?<value>.+)$/);
  return match ? { ...agg, [match.groups.key]: match.groups.value } : agg;
}, {});

let response;
if (args["character"]) {
  response = showOptionsForCharacter(args["character"]);
} else {
  response = showAllCharacters();
}
console.log(JSON.stringify(response));

/*
 * The view that is shown when someone runs the command.
 */
function showAllCharacters() {
  const response = {
    view: {
      type: "list",
      options: [
        {
          title: "Command",
          action: {
            type: "copy",
            value: "⌘",
          },
          moveAction: {
            type: "add-param",
            name: "character",
            value: "command",
          },
        },
        {
          title: "Option",
          action: {
            type: "copy",
            value: "⌥",
          },
          moveAction: {
            type: "add-param",
            name: "character",
            value: "option",
          },
        },
        {
          title: "Control",
          action: {
            type: "copy",
            value: "️️⌃",
          },
          moveAction: {
            type: "add-param",
            name: "character",
            value: "control",
          },
        },
      ],
    },
  };
  return response;
}

/**
 * The view that is shown when someone Tabs on a character.
 */
function showOptionsForCharacter(character) {
  const charactersToHex = {
    command: "&#8984;",
    option: "&#8997;",
    control: "&#8963;",
  };
  return {
    tokens: [
      {
        paramName: "character",
        label: character,
        icon: "🎹",
      },
    ],
    view: {
      type: "list",
      options: [
        {
          title: "Copy Hex",
          action: {
            type: "copy",
            value: charactersToHex[character],
          },
        },
      ],
    },
  };
}
```

[See on Github](https://github.com/slapdash/platform/tree/main/tutorials/copy-special-character)


# Send Slack Message

Show list of Slack users and quickly jump to DM thread

![](/files/-MdrmrYBFbNp6KHb3M-j)

Some commands may require some configuration, which you might not want to add to your command source code. For example, you might need an API key.

Slapdash provides an interface for your command to collect configuration data, which is then sent alongside each command run.

The first time you run the command, it asks for the Slack Bot authentication token, then shows the list of users in Slack. If a user is selected, the Slack desktop is opened directly to the DM thread of the selected user.

![The command asks to provide the Slack Bot Token.](/files/-Mdrn4Tkd3P5G3GcKyxp)

This is an example of a [Cloud Command](/command-bar-101/cloud-commands), a command that runs on a server.

With a Cloud Command, you can share its URL with anyone, and each person will be able to use the command with their own authentication tokens.

{% hint style="info" %}
See [Cloud Commands](https://developers.slapdash.com/command-bar-101/cloud-commands) for details on how to create and deploy cloud commands.
{% endhint %}

```python
#!python
# -*- coding: utf-8 -*-
from flask import Flask, jsonify, request
from requests import get

MIN_TOKEN_LEN = 10
HELP_TEXT = """
To obtain the token,\n
- Create a Slack App at [Slack Apps page](https://api.slack.com/apps) (from scratch);
- Click **Bots**, then **Review Scopes to Add**;
- Add **users:read** Bot Token scope and then click **Install to Workspace** above;
- Copy bot token and paste in this field.
"""

app = Flask(__name__)

def get_name(member):
    return member["profile"]["real_name"] or member["profile"]["display_name"]

@app.route("/", methods=["GET", "POST"])
def command():
  # Read config field. If it's not yet entered by the user, show them the input
  # form. Slapdash will save the entered value and not re-request again.
  token = request.headers.get("slack-token")
  if not token or len(token) < MIN_TOKEN_LEN:
    return jsonify({
      "config": {
        "form": {
          "fields": [
            {
              "type": "text",
              "id": "slack-token",
              "label": "Slack Bot Token",
              "placeholder": "xoxb-***-***-***",
              "helpText": HELP_TEXT,
              "defaultValue": token,
              "error": "Invalid token" if token and len(token) < MIN_TOKEN_LEN else None
            }
          ]
        }
      }
    })

  # We have the token. Send Slack API request.
  res = get(
    url="https://slack.com/api/users.list?pretty=1",
    headers={"Authorization": "Bearer " + token}
  )

  # Build CommandResponse from the Slack response.
  return jsonify({
    "view": {
      "type": "list",
      "options": [
        {
          "title": "@" + member["name"] +
            (" — " + get_name(member) if get_name(member) else ""),
          "icon": member["profile"]["image_48"],
          "action": {
            "type": "open-url",
            "url": "slack://user?team=%s&id=%s" % (member["team_id"], member["id"])
          }
        }
        for member in res.json()["members"]
        if not member["is_bot"] and not member["deleted"]
      ]
    }
  })

@app.after_request
def add_header(response):
  response.headers["Access-Control-Allow-Headers"] = "*" # for config headers
  response.headers["Access-Control-Allow-Origin"] = "*"
  return response

if __name__ == "__main__":
  app.run(host="0.0.0.0", port=8080, debug=True)
```


# Command Response

A JSON-encoded data structure used to describe the behavior and UI of a Slapdash command.

A command should return a JSON that tells what the Command Bar should show or do. The JSON needs to conform to a certain format, which can be described with the TypeScript definition below.

```typescript
interface CommandResponse {
  /**
   * Optional.
   * A side effect of the Command when it's run.
   */
  action?: Action;

  /**
   * Optional.
   * The View the Command Bar should display
   * (List, Form etc.).
   */
  view?: View;

  /**
   * Optional.
   * A way to configure the command before it can
   * be used. For example, to collect an API key.
   */
  config?: Config;

  /**
   * Optional.
   * A way to customize how Command Bar tokens
   * that are visualized.
   */
  tokens?: Token[];

  /**
   * Optional.
   * The placeholder text in the Command Bar's input.
   */
  inputPlaceholder?: string;
}
```

## Quick Examples

The simplest way to experiment with commands is to copy one of the examples below to a file with `*.js` extension and then create a [Local Command](/command-bar-101/local-commands) in the [Slapdash desktop app](https://slapdash.com/download).

{% tabs %}
{% tab title="Open URL Action" %}

```javascript
// https://github.com/slapdash/platform/blob/main/docs/reference/open-url.js
console.log(
  JSON.stringify({
    action: {
      type: "open-url",
      url: "https://slapdash.com/",
    },
  })
);
```

When selected in the Command Bar, the command just opens a particular URL in the browser:

![](/files/-MbyGeZCZZgv6nIjWD2A)
{% endtab %}

{% tab title="List View" %}

```javascript
// https://github.com/slapdash/platform/blob/main/docs/reference/list-view.js
console.log(
  JSON.stringify({
    view: {
      type: "list",
      options: [
        {
          title: "Open Google",
          action: {
            type: "open-url",
            url: "https://www.google.com/",
          },
        },
        {
          title: "Open Bing",
          action: {
            type: "open-url",
            url: "https://www.bing.com/",
          },
        },
      ],
    },
  })
);
```

The command shows the list of options. Each option has an associated Action (in this example, the actions are to open URLs):

![](/files/-MbyGIN02sQi1edX2biD)
{% endtab %}

{% tab title="Masonry View" %}

```javascript
// https://github.com/slapdash/platform/blob/main/docs/reference/masonry-view.js
console.log(
  JSON.stringify({
    view: {
      type: "masonry",
      options: [
        {
          imageURL:
            "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
          action: {
            type: "copy",
            value: "Moon",
          },
        },
        {
          imageURL:
            "https://images.unsplash.com/photo-1614642264762-d0a3b8bf3700",
          action: {
            type: "copy",
            value: "Sun",
          },
        },
        {
          imageURL:
            "https://images.unsplash.com/photo-1512361180836-1ecddb33f2dd",
          action: {
            type: "copy",
            value: "Sky",
          },
        },
      ],
    },
  })
);
```

Similar to the List View, but shows the images in a Masonry grid:

![](/files/-MbyG8x1MuP3xHT88A24)
{% endtab %}

{% tab title="Form View" %}

```javascript
// https://github.com/slapdash/platform/blob/main/docs/reference/form-view.js
console.log(
  JSON.stringify({
    view: {
      type: "form",
      title: "Order Drink",
      submitLabel: "Order",
      fields: [
        {
          type: "text",
          id: "name",
          label: "Your Name",
        },
        {
          type: "select",
          id: "drink",
          label: "Drink",
          options: ["Cappuccino", "Latte", "Green Tea", "Coke"],
        },
      ],
    },
  })
);
```

The command shows a form and allows the user to enter some data. When the form is submitted, the command is run again, and the entered data is passed as JSON to its STDIN.

![](/files/-MbyG465wOuv4jwl6xoc)
{% endtab %}

{% tab title="Custom Config" %}

```javascript
// https://github.com/slapdash/platform/blob/main/docs/reference/custom-config.js
console.log(
  JSON.stringify({
    config: {
      form: {
        fields: [
          {
            type: "text",
            id: "api-key",
            label: "API Key",
            placeholder: "GIPHY API key",
          },
        ],
      },
    },
  })
);
```

Emitting `config` property allows the command to request some private configuration data from the user when it is first executed. Later, the configuration is passed back to the command via process environment variables.

![](/files/-MbyFmQSlK-4H870f7KC)
{% endtab %}
{% endtabs %}


# Action

Actions tell the Command Bar to perform a side effect (e.g. open a URL or copy something to the clipboard). They can be used in two places:

* at the root of [Command Response](/reference/command-response) as `CommandResponse.action`
* in the Option of the [List View](/reference/command-response-view-list) or [Masonry View](/reference/command-response-view-masonry) as `CommandResponse.view.options[].action`

```typescript
export type Action =
  | ActionOpenURL
  | ActionPaste
  | ActionCopy
  | ActionShowToast
  | ActionMove;
  
type ActionMove = ActionMoveAddParam;
```

## ActionOpenURL

Opens a given URL using the system's default handler.

* **type:** `"open-url"`
* **url:** The URL to open. You can use any valid URI schema. For example, "https\://", "file://", "ssh://", "slack://" (native app). Provide a string to open a single URL or an array of strings to open multiple URLs at once.

{% tabs %}
{% tab title="Open a URL in the browser" %}

```javascript
{
  "action": {
    "type": "open-url",
    "url": "https://slapdash.com/",
  }
}
```

{% endtab %}

{% tab title="Open multiple URLs at once" %}

```
{
  "action": {
    "type": "open-url",
    "url": [
        "https://google.com/",
        "https://bing.com/"
    ]
  }
}
```

{% endtab %}

{% tab title="List View: open Google and Bing URLs" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Google",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      },
      {
        "title": "Open Bing",
        "action": {
          "type": "open-url",
          "url": "https://www.bing.com/"
        }
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Open a local folder in Finder" %}

```typescript
{
  "action": {
    "type": "open-url",
    "url": "file:///Users/Johny/Downloads",
  }
}
```

{% endtab %}

{% tab title="Open a Slack channel in the Slack desktop app" %}

```typescript
{
  "action": {
    "type": "open-url",
    "url": "slack://channel?team=TA4PV0NH4&id=CR7EDED9Q"
  }
}
```

{% endtab %}
{% endtabs %}

## ActionPaste

Pastes some text to the active app.

* **type:** `"paste"`
* **value:** The string that will be pasted to the active app.

{% tabs %}
{% tab title="Paste text to the active app" %}

```typescript
{
  "action": {
    "type": "paste",
    "value": "Hello, world!"
  }
}
```

{% endtab %}

{% tab title="List View: paste an email address to the active app" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Paste My Email",
        "action": {
          "type": "paste",
          "value": "my-personal-email@gmail.com"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## ActionCopy

Copies some text to the clipboard.

* **type:** `"copy"`
* **value:** The string that will be copied to the clipboard.

{% tabs %}
{% tab title="Copy text to clipboard" %}

```typescript
{
  "action": {
    "type": "copy",
    "value": "Hello, world!"
  }
}
```

{% endtab %}

{% tab title="List View: option to copy a URL to clipboard" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Copy Google URL",
        "action": {
          "type": "copy",
          "value": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## ActionShowToast

Shows a message in a toast (an ephemeral message displayed on the screen).

* **type:** `"show-toast"`
* **message:** The message that will be displayed in a toast.

{% tabs %}
{% tab title="Show a confirmation message in a toast" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Show a Message",
        "action": {
          "type": "show-toast",
          "message": "The task has been successfully completed!"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## ActionMoveAddParam

Allows to change the [location of the Command Bar](/command-bar-101/core-terminology#location). Currently, there is only one Move Action supported – add a param. Location parameters are then passed to the Command when it's run.

Typically, the "add-param" Action is used in the `moveAction` property of some [Option](/reference/command-response-view-list#listoption) that can be triggered by pressing **`Tab`**.

* **type:** `"add-param"`
* **name:** The name of the parameter.
* **value:** The value of the parameter.

{% tabs %}
{% tab title="Masonry View: option with Main and Move Actions" %}

```typescript
{
  "view": {
    "type": "masonry",
    "options": [
      {
        "imageURL": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
        "action": {
          "type": "open-url",
          "url": "https://images.unsplash.com/photo-1481819613568-3701cbc70156"
        },
        "moveAction": {
          "type": "add-param",
          "name": "image",
          "value": "moon"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# View

Views define, what's shown to the user when the command is executed. The view can be provided at the root of the [Command Response](/reference/command-response) using the `CommandReponse.view` property.

The simplest view one can use is text. Simply set the view property to some text and it will be shown in the Command Bar.

```javascript
{
  "view": "Ahoy, world!"
}
```

Read on about other views that can be used in the Command Response: [List](/reference/command-response-view-list#list), [Masonry](/reference/command-response-view-masonry) and [Form](/reference/command-response-view-form).


# List View

The List View response tells the Command Bar to display a list of options.

* **type:** `"list"`
* **options:** An array of [ListOption](#listoption) objects.
* **groups:** Optional. An array of [Group](#group) objects (or strings). Can be used to define the order in which they appear and customize how they are displayed.
* **ranking:** Optional. The default value is `true`, i.e the Command Bar's default ranking will be used. If you wish to return different options as the user types in the Command Bar, set `ranking` to `false`. Then Slapdash will run your command with a special parameter `keywords` that you can use to decide what options to return back.

{% tabs %}
{% tab title="Options to open, copy, paste a URL or show it in a toast" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Google",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      },
      {
        "title": "Copy Google URL",
        "action": {
          "type": "copy",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Paste Google URL",
        "action": {
          "type": "paste",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Show Google URL",
        "action": {
          "type": "show-toast",
          "message": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Grouped options" %}

```typescript
{
  "view": {
    "type": "list",
    "groups": ["Clipboard", "Browser", "Misc"],
    "options": [
      {
        "title": "Open Google",
        "group": "Browser",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      },
      {
        "title": "Copy Google URL",
        "group": "Clipboard",
        "action": {
          "type": "copy",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Paste Google URL",
        "group": "Clipboard",
        "action": {
          "type": "paste",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Show Google URL",
        "group": "Misc",
        "action": {
          "type": "show-toast",
          "message": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## ListOption

Property `CommandResponse.view.options` is the list of options that are displayed in the List View.

* **title:** The title for the option.
* **action:** Option's [Main Action](#options-main-action). This Action is executed when **`Enter`** is pressed on the Option (or when the option is clicked).
* **moveAction:** Optional. Option's [Move Action](#options-move-action) object. This Action is executed when Tab is pressed on the Option.
* **icon**: Optional. The [Icon](/reference/command-response-icon) for the option.
* **subtitle:** Optional. The subtitle for the option. Can be provided as a string or a list of strings.
* **group:** Optional. The [Group](#group) this option belongs to.

{% tabs %}
{% tab title="List View: options with a custom icon and subtitle" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Copy Home Number",
        "subtitle": [
          "Mobile",
          "Emergencies"
        ],
        "group": "Phone Numbers",
        "icon": "🏠",
        "action": {
          "type": "copy",
          "value": "+44123456789"
        }
      },
      {
        "title": "Copy Work Number",
        "subtitle": [
          "Stationary",
          "9-5"
        ],
        "group": "Phone Numbers",
        "icon": "💼",
        "action": {
          "type": "copy",
          "value": "+44987654321"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## Group

Property `CommandResponse.view.groups` allows to display options in the List View in groups. Each `Group` can be a string or an object. Provide Group as an object if you want to customize its appearance (e.g. change its title).

{% tabs %}
{% tab title="List View: custom order for groups" %}

```typescript
{
  "view": {
    "type": "list",
    "groups": [
      "misc",
      "browser",
      "clipboard"
    ],
    "options": [
      {
        "title": "Open Google",
        "group": "browser",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      },
      {
        "title": "Copy Google URL",
        "group": "clipboard",
        "action": {
          "type": "copy",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Paste Google URL",
        "group": "clipboard",
        "action": {
          "type": "paste",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Show Google URL",
        "group": "misc",
        "action": {
          "type": "show-toast",
          "message": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}

{% tab title="List View: custom title for a group" %}

```typescript
{
  "view": {
    "type": "list",
    "groups": [
      {
        "id": "misc",
        "title": "Miscellaneous"
      },
      "browser",
      "clipboard"
    ],
    "options": [
      {
        "title": "Open Google",
        "group": "browser",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      },
      {
        "title": "Copy Google URL",
        "group": "clipboard",
        "action": {
          "type": "copy",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Paste Google URL",
        "group": "clipboard",
        "action": {
          "type": "paste",
          "value": "https://www.google.com/"
        }
      },
      {
        "title": "Show Google URL",
        "group": "misc",
        "action": {
          "type": "show-toast",
          "message": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## Option's Main Action

The Main Action for an Option can be provided as the plain [Action](/reference/command-response-action) object or as a special object that allows to customize how the action is visualised by the Command Bar.

```typescript
type OptionMainAction =
  | Action
  | {
      /** The default Action object. */
      action: Action;
      /** The label for this action. By default it will be inferred
       * from the action property. */
      label?: string;
      /** The tooltip for this action. By default it will be inferred
       * from the action property. */
      tooltip?: string;
      /** The icon for this action. Either an emoji or an Image URL.
       * By default it will be inferred from the action property. */
      icon?: Icon;
    };
```

{% tabs %}
{% tab title="List View: custom affordances for the option" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Google",
        "action": {
          "label": "Open Browser",
          "icon": "🌎",
          "tooltip": "Open Google in the Browser",
          "action": {
            "type": "open-url",
            "url": "https://www.google.com/"
          }
        }
      }
    ]
  }
}
```

{% endtab %}

{% tab title="List View: default affordances for the option" %}

```typescript
{
  "view": {
    "type": "list",
    "options": [
      {
        "title": "Open Google",
        "action": {
          "type": "open-url",
          "url": "https://www.google.com/"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## Option's Move Action

Property `CommandResponse.view.options[].moveAction` allows providing a [Move Action](/reference/command-response-action#actionmoveaddparam) to change the location of the Command Bar.

{% tabs %}
{% tab title="Masonry View: option with " %}

```typescript
{
  "view": {
    "type": "masonry",
    "options": [
      {
        "imageURL": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
        "action": {
          "type": "open-url",
          "url": "https://images.unsplash.com/photo-1481819613568-3701cbc70156"
        },
        "moveAction": {
          "type": "add-param",
          "name": "image",
          "value": "moon"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# Masonry View

The Masonry View response tells the Command Bar to displays options in the Pinterest-like layout.

* **type:** `"masonry"`
* **options:** An array of [MasonryOption](#masonryoption) objects.

```javascript
{
  "view": {
    "type": "masonry",
    "options": [
      {
        "imageURL": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
        "action": {
          "type": "copy",
          "value": "Moon"
        }
      },
      {
        "imageURL": "https://images.unsplash.com/photo-1614642264762-d0a3b8bf3700",
        "action": {
          "type": "copy",
          "value": "Sun"
        }
      }
    ]
  }
}
```

## MasonryOption

For MasonryView, property `CommandResponse.view.options` contains the list of MasonryOption objects.

* **imageURL:** The image URL for this option.
* **action:** Option's [Main Action](/reference/command-response-view-list#options-main-action) object.
* **moveAction:** Optional. Option's [Move Action](/reference/command-response-view-list#options-move-action) object.

```javascript
{
  "view": {
    "type": "masonry",
    "options": [
      {
        "imageURL": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
        "action": {
          "type": "open-url",
          "url": "https://images.unsplash.com/photo-1481819613568-3701cbc70156"
        },
        "moveAction": {
          "type": "add-param",
          "name": "image",
          "value": "moon"
        }
      },
      {
        "imageURL": "https://images.unsplash.com/photo-1512361180836-1ecddb33f2dd",
        "action": {
          "type": "copy",
          "value": "Sky"
        }
      }
    ]
  }
}
```


# Form View

Form View allows to request some data from the user. When the form is submitted, the command will receive all values in the respective parameters.

The delivery method depends on the type of your command. For [Local Commands](/command-bar-101/local-commands), the form data is sent using stdin, and for [Cloud Commands](/command-bar-101/cloud-commands), it's sent using a GET or POST request.

## Form

Property `CommandResponse.view` of type `Form` allows showing a custom form in the Command Bar.

* **type:** `"form"`
* **fields:** An array of [FormField](#formfield) objects. By default, each field will be displayed in a separate row. If you want some fields to be displayed in the same row – put them in a nested array, the available space will be evenly split between them. See examples below for more details.
* **title:** Optional. Title of the form.
* **submitLabel:** Optional. Text label for the submit button.
* **cancelLabel:** Optional. Text label for the cancel button.
* **method:** Optional. `"get"` or `"post"` (default). HTTP request method that will be used to submit the form. This is only relevant to [Cloud Commands](/command-bar-101/cloud-commands).
* **error**. Optional. The error message to show for the whole form.

{% tabs %}
{% tab title="Simple form to order a drink" %}

```javascript
{
  "view": {
    "type": "form",
    "title": "Order Drink",
    "submitLabel": "Order",
    "fields": [
      {
        "type": "text",
        "id": "name",
        "label": "Your Name"
      },
      {
        "type": "select",
        "id": "drink",
        "label": "Drink",
        "options": [
          "Cappuccino",
          "Latte",
          "Green Tea",
          "Coke"
        ]
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Multiple fields on the same row" %}

```javascript
{
  "view": {
    "type": "form",
    "fields": [
      [
        {
          "type": "text",
          "id": "firstName",
          "label": "First Name"
        },
        {
          "type": "text",
          "id": "lastName",
          "label": "Last Name"
        },
        {
          "type": "toggle",
          "id": "subscribe",
          "label": "Subscribe to Newsletter"
        }
      ],
      {
        "type": "textarea",
        "id": "notes",
        "label": "Notes"
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

## FormField

Property `CommandResponse.view.fields` defines an array of fields in the Form View. There are multiple different form field types that allow creating sophisticated forms.

```typescript
type FormField = TextField | ToggleField | SelectField | DateField;
```

### TextField

A plain-text input, similar to the HTML `<input/>` tag, defines a field where a user can enter free-form data.

* **type:** `"text"`
* **id:** ID of the field. Must be unique in the Form view.
* **label:** Label for the field.
* **required:** Optional, `true` or `false`. Whether this field is required.
* **defaultValue:** Optional. The initial text value for this field.
* **error:** Optional. If set, shows an error message under the field.
* **helpText:** Optional. A Markdown text which will be displayed under the field.
* **placeholder:** Optional. Placeholder for this field, displayed inside the field in gray.
* **multiline:** Optional, `true` or `false`. Whether the field should be displayed as a large textarea.

{% tabs %}
{% tab title="Single line text field" %}

```javascript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "text",
        "id": "name",
        "label": "Name",
        "required": true,
        "defaultValue": "Steve",
        "placeholder": "Tell us your name"
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Large textarea field" %}

```typescript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "text",
        "id": "notes",
        "label": "Your Notes",
        "placeholder": "What is on your mind?",
        "multiline": true
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

### ToggleField

Defines a field that allows the user to enable or disable something.

* **type:** `"toggle"`
* **id:** ID of the field. Must be unique in the Form View.
* **label:** Label for the field.
* **required:** Optional, `true` or `false`. Whether this field is required.
* **defaultValue:** Optional, `true` or `false`. The default value for this field.
* **error:** Optional. If set, shows an error message under the field.
* **helpText:** Optional. A Markdown text which will be displayed under the field.

{% tabs %}
{% tab title="Simple toggle field" %}

```typescript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "toggle",
        "id": "subscribe",
        "label": "Subscribe to Newsletter",
        "defaultValue": true
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

### DateField

Defines a field which lets the user easily select a date from a calendar style UI.

* **type:** `"date"`
* **id:** ID of the field. Must be unique in the Form View.
* **label:** Label for the field.
* **required:** Optional, `true` or `false`. Whether this field is required.
* **defaultValue:** Optional. Default value for this field as a Date string.
* **error:** Optional. If set, shows an error message under the field.
* **helpText:** Optional. A Markdown text which will be displayed under the field.
* **timeSelect:** Optional, `true` or `false` (default). Whether the field allows selecting the time too.

{% tabs %}
{% tab title="Simple date field" %}

```typescript
{
  "view": {
    "type": "form",
    "title": "When is your birthday?",
    "fields": [
      {
        "type": "date",
        "id": "dateOfBirth",
        "label": "Your Date of Birth"
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

### SelectField

Defines a field that allows the user to select one or multiple options.

* **type:** `"select"`
* **id:** ID of the field. Must be unique in the Form View.
* **label:** Label for the field.
* **options:** A list of strings or [SelectOption](#selectoption) objects that a user can select from.
* **required:** Optional, `true` or `false`. Whether this field is required.
* **defaultValue:** Optional. Default value for this field. If the field allows selecting multiple values (see `multiple`), an array of strings can be provided.
* **error:** Optional. If set, shows an error message under the field.
* **helpText:** Optional. A Markdown text which will be displayed under the field.
* **multiple:** Optional, `true` or `false`. Whether the field allow selecting single or multiple options.
* **placeholder:** Optional. Placeholder of the field, shown inside in gray.

#### SelectOption

Allows to customize options for [SelectField](#selectfield).

* **label:** Label for the option.
* **value:** Value of the option.

{% tabs %}
{% tab title="Select field that allows to pick only one option" %}

```typescript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "select",
        "id": "country",
        "label": "Country of Residence",
        "options": [
          "Ukraine",
          "Spain",
          "Germany"
        ]
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Select field with custom options" %}

```typescript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "select",
        "id": "country",
        "label": "Country of Residence",
        "options": [
          {
            "value": "ukr",
            "label": "Ukraine"
          },
          {
            "value": "s",
            "label": "Spain"
          },
          {
            "value": "g",
            "label": "Germany"
          }
        ]
      }
    ]
  }
}
```

{% endtab %}

{% tab title="Select field that allows to pick multiple options" %}

```typescript
{
  "view": {
    "type": "form",
    "fields": [
      {
        "type": "select",
        "id": "country",
        "label": "Where do you want to travel to?",
        "multiple": true,
        "placeholder": "Select Countries",
        "options": [
          "Ukraine",
          "Spain",
          "Germany"
        ]
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# Tokens

Tokens allow customizing how the command's parameters are displayed in the input of the Command Bar.

* **paramName:** The name of the parameter associated with this token. These parameters are typically added with [ActionMoveAddParam](/reference/command-response-action#actionmoveaddparam).
* **label:** Optional. The label for the token. By default, the parameter's name is used.
* **icon:** Optional. The [Icon](/reference/command-response-icon) for the token.

{% tabs %}
{% tab title="Masonry View: custom label and icon for a token" %}

```javascript
{
  "tokens": [
    {
      "paramName": "image",
      "label": "Moon",
      "icon": "🌜"
    }
  ],
  "view": {
    "type": "masonry",
    "options": [
      {
        "imageURL": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
        "action": {
          "type": "open-url",
          "url": "https://images.unsplash.com/photo-1481819613568-3701cbc70156"
        },
        "moveAction": {
          "type": "add-param",
          "name": "image",
          "value": "moon"
        }
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# Icon

The icon can be provided in different ways to help you fine-tune its appearance.

```typescript
type Icon =
  | string
  | {
      light: string;
      dark: string;
    }
  | {
      monochrome: string;
    };
```

Slapdash supports loading icons over `http://` , `https://`, using[ Data URLs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) or providing the raw icon content inline.

```typescript
// Emoji can be provided inline. All Unicode Emoticons are supported.
icon: "🧛‍♂️"

// Icons accessible over HTTP/HTTPS can be provided as an absolute URL.
icon: "https://slapdash.com/favicon.ico"

// SVGs can be provided inline.
icon: '<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" fill="none" stroke="#000" stroke-width="2" ><circle cx="12" cy="12" r="10"/></svg>'

// Icons can be encoded as Data URLs and provided inline. The following
// formats are supported: image/png, image/jpeg, image/gif, image/svg+xml.
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKBJREFUeNpiYBjpgBFd4P///wJAaj0QO9DEQiAg5ID9tLIcmwMYsDgABhqoaTHMUHRxpsGYBv5TGqTIZsDkYWLo6gc8BEYdMOqAUQeMOoAqDgAWcgZAfB9EU63SIAGALH8PZb+H8v+jVz64KiOK6wIg+ADEArj4hOoCajiAqMpqtDIadcCoA0YdQIoDDtCqQ4KtBY3NAYG0csQowAYAAgwAgSqbls5coPEAAAAASUVORK5CYII="
```

### Customize

By default, Slapdash will display icons "as is" but you can choose to render different icons depending on the selected theme:

```typescript
icon: {
  light: "🌞",
  dark: "🌔"
}
```

Or, you can tell Slapdash to automatically adjust the icon color based on the current theme:

```typescript
icon: {
  monochrome: "https://example.com/icon.svg"
}
```

In this case, Slapdash will replace all non-transparent pixels of the icon with the appropriate color from the selected theme.


# Config

Config allows displaying a "one-time" form in the Command Bar. All values from this form will be securely stored on the server and sent to the command any time it is run. Useful when a command requires some configuration (e.g. API key) before it can be run.

To see a real example, check out the [Send Slack Message](/command-tutorials/send-slack-message) command.

## Config

{% tabs %}
{% tab title="Simple Config Form" %}

```json
{
  "config": {
    "form": {
      "fields": [
        {
          "type": "text",
          "id": "api-key",
          "label": "API Key",
          "placeholder": "GIPHY API key"
        }
      ]
    }
  }
}
```

{% endtab %}

{% tab title="Config Form with Error" %}

```json
{
  "config": {
    "form": {
      "error": "This API Key has expired",
      "fields": [
        {
          "type": "text",
          "id": "api-key",
          "label": "API Key",
          "placeholder": "GIPHY API key"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Show Config Conditionally

You will likely want to show the configuration form to the user conditionally, e.g. when the user hasn't entered the values yet or when the values are incorrect. See the [Send Slack Message](/command-tutorials/send-slack-message) on how to do it.


