Docs/Quickstart

Quickstart

Universal Developer

Quickstart

1. Scaffold

EXAMPLE.BASH
pnpm dlx create-tuff-plugin my-plugin
cd my-plugin
pnpm install

The scaffold includes the Manifest, index.js Prelude, optional UI directory, and baseline build configuration.

2. Manifest

Start with plugin identity, entrypoint, SDK version, category, permissions, and a CoreBox feature in manifest.json.

EXAMPLE.JSON
{
  "id": "com.example.my-plugin",
  "name": "my-plugin",
  "version": "0.1.0",
  "author": "Example",
  "sdkapi": 260626,
  "category": "utilities",
  "description": "Return a copy action from CoreBox.",
  "main": "index.js",
  "dev": { "enable": true },
  "permissions": {
    "required": [],
    "optional": ["clipboard.write"]
  },
  "permissionReasons": {
    "clipboard.write": "Copy processed text to the clipboard"
  },
  "features": [
    {
      "id": "my-plugin.copy",
      "name": "Copy Input Text",
      "desc": "Type text in CoreBox and copy it",
      "keywords": ["copy", "text"],
      "push": true,
      "acceptedInputTypes": ["text"],
      "commands": [
        { "type": "over", "value": ["copy"] }
      ]
    }
  ]
}

3. Prelude Script

index.js runs in the plugin sandbox and reads context APIs from globalThis.

EXAMPLE.JAVASCRIPT
const { clipboard, box, TuffItemBuilder } = globalThis
const PLUGIN_NAME = 'my-plugin'
const COPY_ACTION_ID = 'copy'

function getQueryText(query) {
  return typeof query === 'string' ? query : query?.text ?? ''
}

module.exports = {
  async onFeatureTriggered(featureId, query) {
    const text = getQueryText(query).trim()
    if (!text) {
      return []
    }

    return [
      new TuffItemBuilder('my-plugin.copy-result')
        .setSource('plugin', 'plugin-features', PLUGIN_NAME)
        .setTitle('Copy Input Text')
        .setSubtitle(text.slice(0, 80))
        .setMeta({
          pluginName: PLUGIN_NAME,
          featureId,
          defaultAction: COPY_ACTION_ID,
        })
        .createAndAddAction(COPY_ACTION_ID, 'copy', 'Copy', text)
        .build(),
    ]
  },

  async onItemAction(item) {
    const action = item.actions?.find(action => action.id === COPY_ACTION_ID || action.type === 'copy')
    if (action?.payload) {
      clipboard.writeText(action.payload)
    }
    box?.hide?.()
  },
}

4. Debug

  • pnpm core:dev keeps the plugin hot-reloaded.
  • Logs show up in DevTools and logs/plugins/<id>.log.
  • Use logger for plugin logs, and avoid writing sensitive parameters to logs.

5. Validate And Build

EXAMPLE.BASH
tuff validate --strict
tuff build

tuff build creates dist/build/ and a .tpex package. Before publishing, run tuff publish --dry-run to preview the upload.

Continue with Plugin Development Workflow.