Your First Plugin
What You’ll Build
Section titled “What You’ll Build”In this tutorial, you’ll create a simple plugin that adds a custom tab to work unit detail views. This tab will display a welcome message and demonstrate the basics of plugin development.
Prerequisites
Section titled “Prerequisites”- Aether development environment set up (Setup Guide)
- Basic TypeScript/React knowledge
- Familiarity with terminal/command line
Step 1: Generate Plugin Structure
Section titled “Step 1: Generate Plugin Structure”Use the built-in generator to create your plugin:
npm run create:pluginWhen prompted:
- Plugin name:
hello-world - Description:
My first Aether plugin
This creates:
packages/plugins/hello-world/├── package.json├── tsconfig.json└── src/ ├── index.ts ├── manifest.ts └── components/Step 2: Create Your Tab Component
Section titled “Step 2: Create Your Tab Component”Create packages/plugins/hello-world/src/components/HelloTab.tsx:
import { Card, Text, Title, Stack } from '@mantine/core';
export function HelloTab() { return ( <Card shadow="sm" padding="lg" radius="md" withBorder> <Stack gap="md"> <Title order={2}>Hello from your first plugin!</Title> <Text> Congratulations! You've successfully created and registered a custom plugin tab in Aether. </Text> <Text c="dim"> This tab appears on all work unit detail views where this plugin is enabled. </Text> </Stack> </Card> );}Step 3: Define Your Manifest
Section titled “Step 3: Define Your Manifest”Update packages/plugins/hello-world/src/manifest.ts:
import { definePlugin, ExtensionType } from '@aether/plugins-core';import { IconWaveSawTool } from '@tabler/icons-react';import { HelloTab } from './components/HelloTab';
export const helloWorldPlugin = definePlugin({ slug: 'hello-world', name: 'Hello World', version: '1.0.0', description: 'My first Aether plugin', author: 'Your Name',
extensions: [ { point: 'work-unit.detail.tabs', plugin: 'hello-world', priority: 100, extension: { type: ExtensionType.TAB, id: 'hello-tab', label: 'Hello', icon: IconWaveSawTool, component: HelloTab, }, }, ],});Step 4: Export Your Plugin
Section titled “Step 4: Export Your Plugin”Update packages/plugins/hello-world/src/index.ts:
export { helloWorldPlugin } from './manifest';export { HelloTab } from './components/HelloTab';Step 5: Register Your Plugin
Section titled “Step 5: Register Your Plugin”The manifest registry is auto-generated, so just regenerate it:
npm run generate:manifestsThis updates apps/web/src/lib/plugins/manifest-registry.generated.ts to include your plugin.
Step 6: Test Your Plugin
Section titled “Step 6: Test Your Plugin”-
Start the dev server:
Terminal window npm run dev -
Navigate to Settings > Plugins
-
Enable your plugin:
- Find “Hello World” in the plugin list
- Toggle it on
- Save settings
-
View your tab:
- Go to any work unit (create one if needed)
- Look for the “Hello” tab in the detail view
- Click it to see your component!
Understanding What You Built
Section titled “Understanding What You Built”Extension Points
Section titled “Extension Points”work-unit.detail.tabs is an extension point - a specific location where plugins can add functionality. Aether has many extension points throughout the platform.
Plugin Manifest
Section titled “Plugin Manifest”The manifest is your plugin’s configuration file. It tells Aether:
- What your plugin is called
- What extensions it provides
- Where those extensions should appear
- What components to render
Priority
Section titled “Priority”priority: 100 controls tab ordering. Higher numbers appear first. The built-in tabs use priorities like:
- Overview: 0
- Activities: 10
- Timer: 20
Next Steps
Section titled “Next Steps”Now that you’ve built your first plugin, try:
-
Add Context Data:
interface HelloTabProps {context: {workUnit: any;workUnitId: string;organizationId: string;};}export function HelloTab({ context }: HelloTabProps) {return (<Card><Text>Work Unit: {context.workUnit?.name}</Text><Text>ID: {context.workUnitId}</Text></Card>);} -
Add an Action Button: Create an ACTION extension at
work-unit.detail.actions -
Fetch Data with tRPC: Add a server router and fetch data in your component
-
Add Conditional Rendering: Use the
matchproperty to show your tab only for specific business types
Explore More
Section titled “Explore More”- Plugin System Overview
- Extension Types
- Timer Plugin Source - Real-world example
- Plugin Developer Guide - Comprehensive reference