Extending Your App: Power User Guide to Code Customization

Authors
Published on
Published:

Your productivity app includes a powerful code editor that lets you extend functionality, create custom automations, and build personalized features. This guide will help you unlock the full potential of your app through code customization.

Getting Started with the Code Editor

Accessing the Code Editor

  1. Navigate to Settings > Advanced > Code Editor
  2. Enable Developer Mode if prompted
  3. Choose your editing environment:
    • In-app editor: Built-in code editor with syntax highlighting
    • External editor: Connect your preferred IDE
    • Web editor: Browser-based development environment

Understanding the Extension Architecture

App Structure

Your app is built with a modular architecture:

app/
├── core/                 # Core application logic
├── plugins/             # Official and user plugins
├── themes/              # Visual customizations
├── automations/         # Workflow automations
├── integrations/        # Third-party service connections
└── user-scripts/        # Your custom code

Extension Types

  • Plugins: Full-featured extensions with UI components
  • Scripts: Automation and utility functions
  • Themes: Visual appearance modifications
  • Integrations: Connections to external services
  • Workflows: Custom productivity flows

Basic Customizations

Custom Task Templates

Creating Smart Templates

// Smart meeting follow-up template
class MeetingFollowupTemplate {
  constructor(meetingTitle, attendees, date) {
    this.meetingTitle = meetingTitle;
    this.attendees = attendees;
    this.date = date;
  }

  generate() {
    return [
      {
        title: `Send meeting notes: ${this.meetingTitle}`,
        context: '@Computer',
        due: this.addDays(this.date, 1),
        project: 'Meeting Follow-ups'
      },
      {
        title: `Follow up with ${this.attendees.join(', ')} on action items`,
        context: '@Email',
        due: this.addDays(this.date, 3),
        project: 'Meeting Follow-ups'
      }
    ];
  }

  addDays(date, days) {
    const result = new Date(date);
    result.setDate(result.getDate() + days);
    return result;
  }
}

Template Variables

Use dynamic placeholders in templates:

const projectTemplate = {
  title: "{{projectName}} - {{taskType}}",
  description: "Part of {{projectName}} project, estimated {{duration}}",
  tags: ["{{projectName}}", "{{priority}}"],
  context: "@{{workLocation}}"
};

Custom Automation Rules

Smart Task Categorization

// Automatically categorize tasks based on content
function categorizeTask(task) {
  const title = task.title.toLowerCase();

  // Email-related tasks
  if (title.includes('email') || title.includes('reply')) {
    task.context = '@Email';
    task.estimatedTime = 10; // minutes
  }

  // Meeting-related tasks
  if (title.includes('meeting') || title.includes('call')) {
    task.context = '@Phone';
    task.project = 'Meetings';
  }

  // Creative work
  if (title.includes('design') || title.includes('write') || title.includes('create')) {
    task.energyLevel = 'high';
    task.tags.push('creative');
  }

  return task;
}

// Register the automation
app.automations.register('task-created', categorizeTask);

Context-Aware Scheduling

// Automatically schedule tasks based on context and time
function smartScheduling(task) {
  const now = new Date();
  const hour = now.getHours();

  // Schedule @Computer tasks during work hours
  if (task.context === '@Computer' && hour >= 9 && hour <= 17) {
    task.scheduledFor = getNextAvailableSlot('computer-work');
  }

  // Schedule @Phone tasks for dedicated call blocks
  if (task.context === '@Phone') {
    task.scheduledFor = getNextCallBlock();
  }

  // Schedule creative tasks during peak energy hours
  if (task.tags.includes('creative')) {
    task.scheduledFor = getNextHighEnergySlot();
  }
}

Advanced Plugins

Custom View Plugin

Creating a Productivity Dashboard

class ProductivityDashboard extends Plugin {
  constructor() {
    super('productivity-dashboard');
    this.name = 'Productivity Dashboard';
    this.version = '1.0.0';
  }

  initialize() {
    this.addMenuItem({
      label: 'Dashboard',
      icon: 'dashboard',
      route: '/dashboard',
      component: this.DashboardComponent
    });
  }

  DashboardComponent() {
    return `
      <div class="productivity-dashboard">
        <div class="stats-grid">
          <div class="stat-card">
            <h3>Today's Progress</h3>
            <div class="progress-bar" data-progress="${this.getTodayProgress()}"></div>
          </div>
          <div class="stat-card">
            <h3>Weekly Velocity</h3>
            <div class="velocity-chart" data-tasks="${this.getWeeklyTasks()}"></div>
          </div>
          <div class="stat-card">
            <h3>Context Distribution</h3>
            <div class="context-pie-chart" data-contexts="${this.getContexts()}"></div>
          </div>
        </div>
        <div class="quick-actions">
          <button onclick="this.quickCapture()">Quick Capture</button>
          <button onclick="this.weeklyReview()">Weekly Review</button>
          <button onclick="this.focusMode()">Focus Mode</button>
        </div>
      </div>
    `;
  }

  getTodayProgress() {
    const todayTasks = app.tasks.getByDate(new Date());
    const completed = todayTasks.filter(t => t.completed).length;
    return (completed / todayTasks.length) * 100;
  }
}

// Register the plugin
app.plugins.register(new ProductivityDashboard());

AI-Powered Task Enhancement

Smart Task Suggestions

class TaskAI extends Plugin {
  constructor() {
    super('task-ai');
  }

  async enhanceTask(task) {
    // Use local AI to improve task descriptions
    const enhancements = await this.analyzeTask(task);

    return {
      ...task,
      suggestedContext: enhancements.context,
      estimatedTime: enhancements.duration,
      suggestedTags: enhancements.tags,
      breakdownSuggestion: enhancements.subtasks
    };
  }

  async analyzeTask(task) {
    // Simple keyword-based analysis (replace with actual AI)
    const keywords = task.title.toLowerCase().split(' ');

    let context = '@General';
    let duration = 30;
    let tags = [];

    if (keywords.some(w => ['email', 'reply', 'send'].includes(w))) {
      context = '@Email';
      duration = 10;
      tags.push('communication');
    }

    if (keywords.some(w => ['call', 'phone', 'meeting'].includes(w))) {
      context = '@Phone';
      duration = 30;
      tags.push('communication');
    }

    if (keywords.some(w => ['research', 'read', 'study'].includes(w))) {
      context = '@Computer';
      duration = 60;
      tags.push('learning');
    }

    return { context, duration, tags };
  }
}

Custom Integrations

Building Service Integrations

Custom API Integration

class CustomServiceIntegration extends Integration {
  constructor() {
    super('custom-service');
    this.apiKey = this.getSetting('apiKey');
    this.baseUrl = 'https://api.customservice.com';
  }

  async syncTasks() {
    try {
      const remoteTasks = await this.fetchRemoteTasks();
      const localTasks = app.tasks.getAll();

      // Sync logic
      for (const remoteTask of remoteTasks) {
        const localTask = localTasks.find(t => t.remoteId === remoteTask.id);

        if (!localTask) {
          // Create new local task
          app.tasks.create(this.mapRemoteTask(remoteTask));
        } else if (remoteTask.updated > localTask.lastSync) {
          // Update existing task
          app.tasks.update(localTask.id, this.mapRemoteTask(remoteTask));
        }
      }
    } catch (error) {
      console.error('Sync failed:', error);
    }
  }

  async fetchRemoteTasks() {
    const response = await fetch(`${this.baseUrl}/tasks`, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      }
    });

    return response.json();
  }

  mapRemoteTask(remoteTask) {
    return {
      title: remoteTask.title,
      description: remoteTask.description,
      dueDate: new Date(remoteTask.due_date),
      priority: this.mapPriority(remoteTask.priority),
      remoteId: remoteTask.id,
      lastSync: new Date()
    };
  }
}

Webhook Automations

External Trigger Handling

class WebhookHandler {
  constructor() {
    this.setupRoutes();
  }

  setupRoutes() {
    app.api.post('/webhooks/github', this.handleGitHubWebhook.bind(this));
    app.api.post('/webhooks/calendar', this.handleCalendarWebhook.bind(this));
    app.api.post('/webhooks/email', this.handleEmailWebhook.bind(this));
  }

  async handleGitHubWebhook(req, res) {
    const { action, pull_request, repository } = req.body;

    if (action === 'opened') {
      await app.tasks.create({
        title: `Review PR: ${pull_request.title}`,
        description: `${pull_request.html_url}`,
        context: '@Computer',
        project: repository.name,
        priority: 'medium',
        tags: ['code-review', 'github']
      });
    }

    res.status(200).send('OK');
  }

  async handleCalendarWebhook(req, res) {
    const { event_type, event } = req.body;

    if (event_type === 'meeting.started') {
      // Create follow-up task for after the meeting
      const followUpTime = new Date(event.end_time);
      followUpTime.setMinutes(followUpTime.getMinutes() + 15);

      await app.tasks.create({
        title: `Follow up on: ${event.summary}`,
        scheduledFor: followUpTime,
        context: '@Computer',
        project: 'Meeting Follow-ups'
      });
    }
  }
}

Custom Themes and UI

Creating Custom Themes

Theme Structure

/* Custom dark theme with productivity focus */
.theme-productivity-dark {
  /* Color palette */
  --primary-color: #4A90E2;
  --secondary-color: #7ED321;
  --accent-color: #F5A623;
  --background-color: #1E1E1E;
  --surface-color: #2D2D2D;
  --text-color: #FFFFFF;
  --text-muted: #CCCCCC;

  /* Task priority colors */
  --priority-high: #D0021B;
  --priority-medium: #F5A623;
  --priority-low: #7ED321;

  /* Context colors */
  --context-computer: #4A90E2;
  --context-phone: #9013FE;
  --context-errands: #F5A623;
  --context-home: #7ED321;
}

/* Task list styling */
.theme-productivity-dark .task-item {
  background: var(--surface-color);
  border-left: 3px solid var(--primary-color);
  border-radius: 4px;
  margin-bottom: 8px;
  padding: 12px 16px;
  transition: all 0.2s ease;
}

.theme-productivity-dark .task-item:hover {
  background: lighten(var(--surface-color), 5%);
  transform: translateX(4px);
}

/* Priority indicators */
.theme-productivity-dark .task-item[data-priority="high"] {
  border-left-color: var(--priority-high);
}

.theme-productivity-dark .task-item[data-priority="medium"] {
  border-left-color: var(--priority-medium);
}

.theme-productivity-dark .task-item[data-priority="low"] {
  border-left-color: var(--priority-low);
}

Dynamic Theme Switching

class ThemeManager {
  constructor() {
    this.themes = new Map();
    this.currentTheme = 'default';
    this.loadThemes();
  }

  registerTheme(name, theme) {
    this.themes.set(name, theme);
  }

  applyTheme(name) {
    const theme = this.themes.get(name);
    if (!theme) return;

    // Remove current theme classes
    document.body.className = document.body.className
      .replace(/theme-\w+/g, '');

    // Apply new theme
    document.body.classList.add(`theme-${name}`);

    // Update CSS custom properties
    Object.entries(theme.variables).forEach(([key, value]) => {
      document.documentElement.style.setProperty(`--${key}`, value);
    });

    this.currentTheme = name;
    this.savePreference(name);
  }

  autoTheme() {
    const hour = new Date().getHours();
    const isDarkTime = hour < 7 || hour > 19;

    this.applyTheme(isDarkTime ? 'dark' : 'light');
  }
}

Advanced Automation

Workflow Automation Engine

Custom Workflow Builder

class WorkflowEngine {
  constructor() {
    this.workflows = new Map();
    this.triggers = new Map();
  }

  createWorkflow(name, config) {
    const workflow = new Workflow(name, config);
    this.workflows.set(name, workflow);

    // Register triggers
    config.triggers.forEach(trigger => {
      this.registerTrigger(trigger, workflow);
    });
  }

  registerTrigger(trigger, workflow) {
    if (!this.triggers.has(trigger.type)) {
      this.triggers.set(trigger.type, []);
    }
    this.triggers.get(trigger.type).push({ trigger, workflow });
  }

  async executeTrigger(type, data) {
    const handlers = this.triggers.get(type) || [];

    for (const { trigger, workflow } of handlers) {
      if (await this.evaluateCondition(trigger.condition, data)) {
        await workflow.execute(data);
      }
    }
  }

  async evaluateCondition(condition, data) {
    // Simple condition evaluation
    return new Function('data', `return ${condition}`)(data);
  }
}

// Example workflow definition
const projectCompletionWorkflow = {
  name: 'Project Completion',
  triggers: [
    {
      type: 'task-completed',
      condition: 'data.task.isLastInProject === true'
    }
  ],
  actions: [
    {
      type: 'create-task',
      config: {
        title: 'Conduct project retrospective for {{project.name}}',
        context: '@Computer',
        scheduledFor: '+1 day'
      }
    },
    {
      type: 'send-notification',
      config: {
        message: 'Project {{project.name}} completed! 🎉',
        type: 'success'
      }
    },
    {
      type: 'archive-project',
      config: {
        projectId: '{{project.id}}'
      }
    }
  ]
};

Performance and Best Practices

Optimization Guidelines

Efficient Code Patterns

// Use debouncing for frequent operations
const debounce = (func, wait) => {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
};

// Debounced task search
const debouncedSearch = debounce((query) => {
  app.search.perform(query);
}, 300);

// Use caching for expensive operations
class DataCache {
  constructor(ttl = 300000) { // 5 minutes default
    this.cache = new Map();
    this.ttl = ttl;
  }

  set(key, value) {
    this.cache.set(key, {
      value,
      timestamp: Date.now()
    });
  }

  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;

    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key);
      return null;
    }

    return item.value;
  }
}

Memory Management

// Clean up event listeners
class PluginBase {
  constructor() {
    this.eventListeners = [];
  }

  addEventListener(element, event, handler) {
    element.addEventListener(event, handler);
    this.eventListeners.push({ element, event, handler });
  }

  destroy() {
    this.eventListeners.forEach(({ element, event, handler }) => {
      element.removeEventListener(event, handler);
    });
    this.eventListeners = [];
  }
}

Debugging and Testing

Development Tools

Debug Logging

class Logger {
  constructor(namespace) {
    this.namespace = namespace;
    this.enabled = app.debug.isEnabled(namespace);
  }

  log(message, ...args) {
    if (this.enabled) {
      console.log(`[${this.namespace}] ${message}`, ...args);
    }
  }

  error(message, error) {
    console.error(`[${this.namespace}] ${message}`, error);
    app.debug.reportError(this.namespace, message, error);
  }
}

// Usage
const logger = new Logger('custom-plugin');
logger.log('Plugin initialized');

Testing Framework

// Simple testing utilities
class PluginTester {
  static async test(name, testFn) {
    try {
      await testFn();
      console.log(`${name}`);
    } catch (error) {
      console.error(`${name}:`, error);
    }
  }

  static assertEqual(actual, expected, message) {
    if (actual !== expected) {
      throw new Error(`${message}: expected ${expected}, got ${actual}`);
    }
  }
}

// Test your customizations
async function runTests() {
  await PluginTester.test('Task categorization', async () => {
    const task = { title: 'Send email to client' };
    const categorized = categorizeTask(task);
    PluginTester.assertEqual(categorized.context, '@Email', 'Should categorize email tasks');
  });
}

Sharing and Distribution

Plugin Packaging

// plugin.json - Plugin manifest
{
  "name": "productivity-dashboard",
  "version": "1.0.0",
  "description": "Advanced productivity analytics dashboard",
  "author": "Your Name",
  "license": "MIT",
  "main": "index.js",
  "dependencies": {
    "chart.js": "^3.0.0"
  },
  "permissions": [
    "tasks.read",
    "tasks.write",
    "analytics.read"
  ],
  "settings": [
    {
      "key": "refreshInterval",
      "type": "number",
      "default": 300000,
      "description": "Dashboard refresh interval in milliseconds"
    }
  ]
}

Community Sharing

  • Plugin repository: Submit to the official plugin library
  • Code snippets: Share useful scripts in the community forum
  • Documentation: Write guides for your customizations
  • Open source: Publish on GitHub for collaboration

The code editor opens up unlimited possibilities for customization. Start with simple automations, gradually build more complex features, and share your innovations with the community. Remember to test thoroughly and follow best practices for maintainable code.


Ready to share your custom extensions? Submit them to our community plugin library!

Ready to Own Your Life?

Join thousands of users who've transformed their productivity

Start Your 14-Day Free Trial

No credit card required • Cancel anytime • Setup in under 5 minutes