Browse API 1 documentation
GroveAPI 1preview

Build your first Sprig

Build and load a small channel Sprig.

Mango 3.1+View Markdown

This guide builds a small channel Sprig with no framework or build step. It receives messages from the channel in which it runs and lets the user send a message through Mango.

If you are not sure whether your idea should have a pane, appear temporarily above the composer, or run without its own UI, start with Choose an extension type. The Starter recipes also contain minimal composer and headless examples.

Create the package directory

Create a folder with these two files:

text
hello-grove/
├── manifest.json
└── index.html

manifest.json declares Grove API 1, a stable package identity, one visible surface, and the capabilities the example uses:

json
{
  "grove": 1,
  "id": "com.example.grove.hello",
  "name": "Hello Grove",
  "version": "0.1.0",
  "description": "A small channel companion.",
  "entry": "index.html",
  "presentation": {
    "surfaces": ["channelPane"],
    "defaultSurface": "channelPane",
    "defaultSize": { "width": 340, "height": 420 }
  },
  "capabilities": ["channel.read", "channel.send"],
  "hostPermissions": []
}

Use a reverse-DNS identifier that you control. The identifier is the permanent namespace for grants, storage, secrets, and store updates; do not change it between releases of the same extension.

Add the entry page

index.html can use the injected window.grove object as soon as its script runs:

html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Hello Grove</title>
</head>
<body>
  <p id="scope"></p>
  <ol id="messages"></ol>
  <form id="form">
    <input id="message" autocomplete="off" placeholder="Message">
    <button>Send</button>
  </form>
  <script>
    const scope = document.querySelector('#scope')
    const messages = document.querySelector('#messages')
    const form = document.querySelector('#form')
    const input = document.querySelector('#message')

    scope.textContent = `Running in ${grove.info.scope.target}`

    grove.on('message', message => {
      const item = document.createElement('li')
      item.textContent = `${message.from}: ${message.text}`
      messages.append(item)
    })

    form.addEventListener('submit', event => {
      event.preventDefault()
      const text = input.value.trim()
      if (!text) return
      grove.send(text)
      input.value = ''
    })
  </script>
</body>
</html>

Use DOM text APIs such as textContent for IRC data. Do not insert nicknames or message text with innerHTML; IRC input is untrusted.

Mango also injects the active palette as --grove-* CSS custom properties and updates them without reloading the page. Add Styling and host appearance when the Sprig needs to blend into Mango across themes, sizes, and platforms.

Load it in Mango

In a development build of Mango on macOS:

  1. Connect to a network and open the channel that should bind the Sprig.
  2. Open the puzzle-piece menu above the transcript.
  3. Choose Load Unpacked (Dev)… and select hello-grove.
  4. Review and grant the requested channel capabilities.

The unpacked development path runs from the selected folder. Stop and start the Sprig after changing files when a change is not already reflected by the page runtime.

On iOS and iPadOS, use Install from Folder… or install a .grove archive. Load Unpacked is macOS-only because a live development folder requires access for the lifetime of the extension.

Debug it

Use grove.log('message'), console.log, or an uncaught JavaScript error while developing. Mango captures extension console and host-loading messages in the extension’s filtered Console view in Grove Manager. The Grove Console button in Mango’s status bar combines live output from every loaded extension and identifies its source, which is useful when several Roots or Sprigs interact.

If no messages arrive, verify that the instance is bound to the expected channel and that channel.read appears in both the manifest and the accepted grant. If sends do not work, check channel.send and the Console for a native rejection.

Add a build system only when useful

Grove does not require a JavaScript framework. A Vue, React, Svelte, or TypeScript project should emit a self-contained static directory whose manifest.json and entry document are in the packaged output. Do not rely on a development server at runtime; Mango serves installed assets itself.

hello-vue v0.1.0 is a complete public Vue 3 and TypeScript Sprig. The complete public examples cover composer utilities, Roots, filters, OAuth, and upload providers.

Continue with Package and manifest before distributing the extension.