Plugin API Reference

Complete reference for the Elysium plugin API. Learn about lifecycle hooks, available interfaces, and how to interact with schedule data.

Lifecycle Hooks

Define these functions in your main.js to respond to plugin lifecycle events.

onLoad()

Called once when the plugin is first loaded. Use this for one-time setup.

function onLoad() {
  console.log("Plugin loaded!");
  // One-time initialization
}

onEnable()

Called when the plugin is enabled. Set up event listeners and start your plugin's functionality.

function onEnable() {
  // Register event listeners
  elysium.events.on("task.completed", handleTaskComplete);
  elysium.events.on("habit.completed", handleHabitComplete);
}

onDisable()

Called when the plugin is disabled. Clean up event listeners and save state.

function onDisable() {
  // Clean up
  elysium.events.off("task.completed", handleTaskComplete);
  elysium.events.off("habit.completed", handleHabitComplete);
}

onUnload()

Called when the plugin is being completely unloaded. Final cleanup before removal.

function onUnload() {
  // Final cleanup
  console.log("Plugin unloaded");
}

Events

Listen for events to react to user actions and schedule changes. Register listeners in onEnable().

Entity Events

// Goals
goal.created, goal.updated
goal.deleted, goal.completed

// Tasks
task.created, task.updated
task.deleted, task.completed

// Habits
habit.created, habit.updated
habit.deleted, habit.completed

// Odysseys
odyssey.created, odyssey.updated
odyssey.deleted, odyssey.completed

// Appointments
appointment.created, appointment.updated
appointment.deleted, appointment.completed

// Reminders
reminder.created, reminder.updated
reminder.deleted, reminder.completed

System Events

// Timer
timer.started
timer.paused
timer.stopped
timer.completed

// App Lifecycle
app.didBecomeActive
app.willResignActive
app.willTerminate

// Schedule
schedule.dayStarted
schedule.itemScheduled

// Settings & Theme
settings.changed
theme.changed
// Listening to events
elysium.events.on("task.completed", (task) => {
  console.log("Task completed:", task.title);
});

elysium.events.on("habit.completed", (habit) => {
  console.log("Habit logged:", habit.title, "Streak:", habit.streak);
});

elysium.events.on("reminder.triggered", (reminder) => {
  console.log("Reminder fired:", reminder.title);
});

Example: Celebrate Habit Streaks

elysium.events.on("habit.completed", (habit) => {
  if (habit.streak > 0 && habit.streak % 7 === 0) {
    elysium.ui.showNotification({
      title: "🔥 Streak Milestone!",
      message: `${habit.streak} day streak on ${habit.title}!`
    });
  }
});

Schedule API

Access and modify schedule data using the elysium API namespaces.

elysium.tasks.*

// Get all tasks
const tasks = await elysium.tasks.list();

// Get tasks with filters
const overdue = await elysium.tasks.list({
  status: "todo",
  overdue: true
});

// Get a specific task
const task = await elysium.tasks.get("task-id");

// Create a new task
await elysium.tasks.create({
  title: "Review pull request",
  due: "2024-03-15",
  tags: ["work", "code-review"]
});

// Update a task
await elysium.tasks.update("task-id", {
  status: "done"
});

elysium.habits.*

// Get all habits
const habits = await elysium.habits.list();

// Get habit with streak info
const habit = await elysium.habits.get("habit-id");
console.log(habit.streak, habit.bestStreak);

// Log a habit completion
await elysium.habits.complete("habit-id");

// Get completion history
const history = await elysium.habits.history("habit-id", {
  days: 30
});

elysium.goals.*

// Get all goals
const goals = await elysium.goals.list();

// Update goal progress
await elysium.goals.updateProgress("goal-id", {
  current: 75,
  target: 100
});

// Get milestones
const milestones = await elysium.goals.milestones("goal-id");

elysium.odysseys.*

// Get all odysseys (multi-step journeys)
const odysseys = await elysium.odysseys.list();

// Get odyssey with steps
const odyssey = await elysium.odysseys.get("odyssey-id");
console.log(odyssey.steps, odyssey.currentStep);

// Update progress
await elysium.odysseys.advanceStep("odyssey-id");

elysium.appointments.*

// Get appointments
const appointments = await elysium.appointments.list();

// Get appointments for a date range
const upcoming = await elysium.appointments.list({
  from: "2024-03-15",
  to: "2024-03-22"
});

// Create an appointment
await elysium.appointments.create({
  title: "Team standup",
  startTime: "2024-03-15T09:00:00",
  endTime: "2024-03-15T09:30:00"
});

elysium.reminders.*

// Get all reminders
const reminders = await elysium.reminders.list();

// Create a reminder
await elysium.reminders.create({
  title: "Take medication",
  time: "2024-03-15T08:00:00",
  repeat: "daily"
});

// Delete a reminder
await elysium.reminders.delete("reminder-id");

elysium.categories.* & elysium.tags.*

// Get all categories
const categories = await elysium.categories.list();

// Get all tags
const tags = await elysium.tags.list();

// Create a category
await elysium.categories.create({
  name: "Work",
  color: "#3b82f6"
});

All API Namespaces

elysium.goals.*         // Goals and milestones
elysium.tasks.*         // Tasks and to-dos
elysium.habits.*        // Habits and streaks
elysium.odysseys.*      // Multi-step journeys
elysium.appointments.*  // Calendar appointments
elysium.reminders.*     // Time-based reminders
elysium.categories.*    // Item categories
elysium.tags.*          // Tags management
elysium.timeline.*      // Timeline access
elysium.schedules.*     // Schedule management
elysium.events.*        // Event listeners
elysium.commands.*      // Register commands
elysium.storage.*       // Plugin data persistence
elysium.settings.*      // App settings access
elysium.theme.*         // Theme customization
elysium.ui.*            // UI components
elysium.notifications.* // System notifications
elysium.console.*       // Debug logging

Theme API

Customize Elysium's appearance with the hierarchical theme token system using elysium.theme.*.

// Get current theme colors
const colors = elysium.theme.getColors();

// Set specific color tokens
elysium.theme.setColor("primary", "#6366f1");
elysium.theme.setColor("goalColor", "#22c55e");

// Apply a complete theme
elysium.theme.apply({
  colors: {
    primary: "#6366f1",
    secondary: "#8b5cf6",
    background: "#0f0f0f",
    surface: "#1a1a1a",
    text: "#ffffff",
    textSecondary: "#a1a1aa",
    success: "#22c55e",
    warning: "#f59e0b",
    error: "#ef4444",
    border: "#27272a"
  }
});

// Reset to default theme
elysium.theme.reset();

Base Color Tokens

primary

secondary

background

surface

text

textSecondary

success

warning

error

border

Entity Color Tokens

goalColor

taskColor

habitColor

odysseyColor

appointmentColor

reminderColor

Customize colors for each item type.

UI Injection

Inject custom UI components into various locations within Elysium. Requires the customize:ui permission.

// Register a widget in the sidebar
elysium.ui.registerWidget({
  id: "my-widget",
  location: "sidebar",
  title: "My Widget",
  render: () => {
    return {
      type: "view",
      children: [
        { type: "text", content: "Hello from my plugin!" }
      ]
    };
  }
});

// Add a custom menu item
elysium.ui.addMenuItem({
  id: "my-action",
  location: "context_menu",
  title: "My Custom Action",
  icon: "star",
  action: () => {
    // Handle the action
  }
});

// Remove injected UI when disabling
elysium.ui.removeWidget("my-widget");

Injection Points

toolbar

sidebar

dashboard

quick_log

entity_detail

settings_panel

menu_bar

context_menu

empty_state

The empty_state slot appears on the Dashboard when no entity is selected.

View Injection

Inject custom card views into specific slots in the app. This is particularly useful for the empty_state slot, which appears on the Dashboard when no entity is selected. Requires the customize:ui permission.

elysium.ui.injectView()

Inject a card view into a slot. Calling again with the same id replaces the existing view.

// Inject a view into the empty state area
elysium.ui.injectView({
  id: "daily-stats",
  slot: "empty_state",
  view: {
    type: "card",
    title: "Today's Progress",
    content: "3 of 5 tasks completed"
  }
});

// Update an existing injected view (same id replaces it)
elysium.ui.injectView({
  id: "daily-stats",
  slot: "empty_state",
  view: {
    type: "card",
    title: "Today's Progress",
    content: "5 of 5 tasks completed!"
  }
});

elysium.ui.removeView()

Remove an injected view by its ID. Each plugin can only remove its own views.

elysium.ui.removeView("daily-stats");

elysium.ui.getViewsForSlot()

Get all views currently injected into a specific slot.

const emptyStateViews = elysium.ui.getViewsForSlot("empty_state");

View Definition Fields

FieldTypeDescription
typeStringView type (currently "card")
titleStringOptional title displayed at the top of the card
contentStringOptional body text displayed in the card

Notes

  • Each plugin's views are namespaced — a plugin can only remove its own views
  • The empty_state slot appears alongside motivational quotes (if the user has them enabled)
  • Views update in real-time; call injectView again with the same id to refresh content
  • Clean up views in your onDisable() lifecycle hook

Settings & Storage

Store plugin data that persists across sessions. Settings sync automatically if the user has cloud sync enabled.

// Store values
await elysium.storage.set("apiKey", "sk-...");
await elysium.storage.set("syncEnabled", true);
await elysium.storage.set("config", { theme: "dark", limit: 10 });

// Retrieve values
const apiKey = await elysium.storage.get("apiKey");
const syncEnabled = await elysium.storage.get("syncEnabled") ?? false;

// Remove a value
await elysium.storage.remove("apiKey");

// Clear all plugin storage
await elysium.storage.clear();

Plugin Settings Schema

Define user-configurable settings in your manifest.json that appear in the plugin's settings panel. Settings are automatically saved to plugin storage.

"settings": [
  {
    "id": "enableStreakCelebration",
    "type": "boolean",
    "title": "Celebrate Streaks",
    "description": "Show celebration messages for streak milestones",
    "default": true
  },
  {
    "id": "quoteCategory",
    "type": "select",
    "title": "Quote Category",
    "description": "Choose the type of quotes to display",
    "default": "motivation",
    "options": [
      { "value": "motivation", "label": "Motivation" },
      { "value": "productivity", "label": "Productivity" }
    ]
  },
  {
    "id": "customMessage",
    "type": "string",
    "title": "Custom Message",
    "description": "A custom message to display",
    "default": ""
  },
  {
    "id": "maxNotifications",
    "type": "number",
    "title": "Max Notifications",
    "description": "Maximum notifications per day",
    "default": 5
  },
  {
    "id": "dataPath",
    "type": "folder",
    "title": "Data Folder",
    "description": "Select a folder for plugin data"
  }
]

Setting Types

TypeDescriptionProperties
booleanToggle switchdefault: boolean
stringText inputdefault: string
numberNumeric inputdefault: number
selectDropdown menudefault: string, options: [{ value, label }]
folderFolder picker

Access settings values via the Storage API: elysium.storage.get("settingId")

Commands

Declare commands in your manifest.json for discoverability, then register handlers in your code.

Manifest Declaration

"commands": [
  {
    "id": "show-quote",
    "name": "Show Daily Quote",
    "description": "Display today's motivational quote"
  },
  {
    "id": "reset-stats",
    "name": "Reset Statistics",
    "description": "Reset all plugin statistics"
  }
]

Registering Handlers

function onLoad() {
  elysium.commands.register("show-quote", "Show Daily Quote", showQuoteHandler);
  elysium.commands.register("reset-stats", "Reset Statistics", resetStatsHandler);
}

function showQuoteHandler() {
  elysium.ui.showNotification({
    title: "Daily Quote",
    message: "The only way to do great work is to love what you do.",
    type: "info"
  });
}

Notifications

Show notifications to users for important events or confirmations.

// Simple notification
elysium.ui.showNotification({
  title: "Sync Complete",
  message: "12 items synced successfully"
});

// With custom duration
elysium.ui.showNotification({
  title: "Saved",
  message: "Your changes have been saved",
  duration: 3000  // milliseconds
});

// Success/error variants
elysium.ui.showSuccess("Task completed!");
elysium.ui.showError("Something went wrong");

Permissions

Plugins must declare all permissions they need in manifest.json. Request only what you need—users see these when installing.

Read Permissions

read:goals

read:tasks

read:habits

read:odysseys

read:appointments

read:reminders

read:categories

read:tags

read:timeline

read:settings

read:schedules

Write Permissions

write:goals

write:tasks

write:habits

write:odysseys

write:appointments

write:reminders

write:categories

write:tags

write:timeline

write:settings

write:schedules

Special Permissions

notifications — Show system notifications

shortcuts — Register keyboard shortcuts

network — Make network requests

clipboard — Access clipboard

filesystem — Read/write files

theme — Customize appearance

customize:ui — Inject custom UI

Permission Example

"permissions": [
  "read:tasks",
  "write:tasks",
  "read:habits",
  "notifications",
  "network"
]

Only request permissions your plugin actually needs.

Full Access Mode

By default, Elysium enforces that plugins can only use APIs for which they declared permissions. Users can enable Full Access Mode in Settings → Plugins & Recipes to bypass these checks.

When Full Access Mode is Enabled

  • Plugins can call any API without declaring the permission
  • The JavaScript sandbox remains in place (plugins still can't access the filesystem outside their sandbox or execute arbitrary code)
  • Useful for plugin developers testing without updating manifests
  • Users who fully trust all their installed plugins can enable this for convenience

Note for developers: Even though Full Access Mode exists, you should still declare all permissions your plugin needs in the manifest. This ensures your plugin works for users who don't enable Full Access Mode, and helps users understand what your plugin does.