Creating Your First Addon

A practical step-by-step guide to creating, configuring, and testing your first addon with the Mr. Tick SDK.

Creating Your First Addon

This guide walks through building a complete Mr. Tick addon from scratch with TypeScript and the official @mr-tick/sdk package.


1. Project Setup

Create a directory for your plugin and initialize the project with its dependencies:

mkdir mr-tick-meu-addon
cd mr-tick-meu-addon
npm init -y
npm install @mr-tick/sdk
npm install -D typescript tsup @types/node

Configure tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "declaration": true,
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

2. Addon Structure

Every Mr. Tick addon has a manifest.yaml metadata file and a default-exported class implementing IAddon.

2.1 Manifest (manifest.yaml)

Create manifest.yaml in your addon root:

id: meu-primeiro-addon
name: Meu Primeiro Addon
version: 1.0.0
description: Exemplo prático de extensão para o Mr-tick
author: Seu Nome ou Empresa

2.2 Main Code (src/index.ts)

Create src/index.ts:

import type { IAddon, AddonContext } from '@mr-tick/sdk'

export default class MeuPrimeiroAddon implements IAddon {
  // Ciclo de Vida: Executado quando o plugin é ativado no Mr-tick
  public async activate(context: AddonContext): Promise<void> {
    console.log('[MeuPrimeiroAddon] Addon ativado com sucesso!')

    // Registrando formulário de configurações dinamicamente com abas e grupos
    context.contributions.settings.register([
      {
        id: 'geral',
        label: 'Geral',
        groups: [
          {
            id: 'conexao',
            label: 'Parâmetros de Conexão',
            fields: [
              {
                id: 'servidorUrl',
                label: 'URL do Servidor',
                type: 'text',
                scope: 'configuration',
                required: true,
                description: 'Endereço da API ou instância corporativa.',
              },
              {
                id: 'apiKey',
                label: 'Chave de Acesso / Token',
                type: 'password',
                scope: 'credential',
                required: true,
                description: 'Token de autenticação gerado nas preferências da sua conta.',
              },
              {
                id: 'notificarEventos',
                label: 'Exibir Notificações',
                type: 'boolean',
                defaultValue: true,
              },
            ],
          },
        ],
      },
    ])

    // Registrando comandos e ações disponíveis
    context.contributions.commands.register('testar-conexao', async () => {
      await context.host.notifications.success('Conexão testada com sucesso!', 'Mr-tick Addon')
      return { isSuccess: true }
    })

    // O workspace vem do evento; nunca da seleção implícita da UI.
    context.host.events.on('timer:start', async (data) => {
      if (data.workspaceId === undefined) return
      const value = await context.host.vault.get(
        { kind: 'workspace', workspaceId: data.workspaceId },
        'notificarEventos',
      )
      if (value.isFailure()) return
      if (value.success !== 'true') return
      await context.host.notifications.info('Timer iniciado', 'Meu addon')
    })
    context.host.events.on('timer:stop', (data) => {
      console.log('Timer finalizado. Duração:', data.currentSeconds)
    })
  }

  // Ciclo de Vida: Executado ao desativar o addon; troca de workspace não o desativa
  public async deactivate(): Promise<void> {
    console.log('[MeuPrimeiroAddon] Addon desativado. Limpando recursos...')
  }
}

3. Dynamic Settings (context.contributions.settings.register)

Calling context.contributions.settings.register(schema) in activate defines preference forms without writing interface code. Mr. Tick generates and validates the fields natively.

Supported Field Types:

  • text: Plain text input.
  • password: Masked input for tokens, passwords, and keys.
  • number: Numeric input.
  • boolean: A true/false toggle.
  • select: A dropdown with { label, value } pairs.
  • button: An action button.

4. Addon Context (AddonContext)

When activating an addon, Mr. Tick injects AddonContext with the available APIs and tools:

PropertyDescription
context.contributions.settingsDeclarative registration of addon form and settings schemas.
context.contributions.menusRegistration of sidebar or timerbar menu items.
context.contributions.dataSourcesRegistration of external data sources (Tasks, Time Entries, Members, Metadata).
context.contributions.commandsRegistration and execution of universal Mr. Tick commands.
context.host.vaultSecure key-value storage isolated by workspace.
context.host.eventsReal-time system events (timer start/stop, idle, workspace changes).
context.host.notificationsSystem toasts and notifications.
context.core.timerReading and controlling the active timer.
context.host.oauthSecure OAuth 2.0 PKCE authentication without secrets.

5. Building the Addon

Add a build script to package.json:

{
  "scripts": {
    "build": "tsup src/index.ts --format esm,cjs --dts --clean"
  }
}

Run the build:

npm run build

The resulting compiled file can be imported directly in Mr. Tick's Addon settings.