Skip to main content

Create a Custom Module

This guide teaches you how to create a new data module for OpenTree.

1. Define the Data Model

// packages/main/src/database/models/Mood.ts

import { DataTypes, Model } from 'sequelize'

export class Mood extends Model {
id!: string
mood!: string // 😊 😢 😡
note?: string
date!: string
}

Mood.init({
id: { type: DataTypes.UUID, primaryKey: true, defaultValue: DataTypes.UUIDV4 },
mood: { type: DataTypes.STRING, allowNull: false },
note: { type: DataTypes.TEXT },
date: { type: DataTypes.DATEONLY, allowNull: false },
}, { tableName: 'moods', sequelize })

2. Register IPC Handlers

// packages/main/src/ipc/mood.ts

import { ipcMain } from 'electron'
import { Mood } from '../database/models/Mood'

export function registerMoodIpc() {
ipcMain.handle('mood-get-all', async () => {
return await Mood.findAll({ order: [['date', 'DESC']] })
})

ipcMain.handle('mood-create', async (_, data) => {
return await Mood.create(data)
})

ipcMain.handle('mood-update', async (_, id, data) => {
await Mood.update(data, { where: { id } })
return await Mood.findByPk(id)
})

ipcMain.handle('mood-delete', async (_, id) => {
return await Mood.destroy({ where: { id } })
})
}

3. Expose to Renderer Process

// packages/main/src/preload.ts

contextBridge.exposeInMainWorld('electronAPI', {
// ... existing modules

mood: {
getAll: () => ipcRenderer.invoke('mood-get-all'),
create: (data) => ipcRenderer.invoke('mood-create', data),
update: (id, data) => ipcRenderer.invoke('mood-update', id, data),
remove: (id) => ipcRenderer.invoke('mood-delete', id),
},
})

4. Integrate in Metaverse

// static/metaverse-v2/app.js

// Add to getWtElectronAPI
mood: {
getAll: () => ipc.invoke('mood-get-all'),
create: (data) => ipc.invoke('mood-create', data),
update: (id, data) => ipc.invoke('mood-update', id, data),
remove: (id) => ipc.invoke('mood-delete', id),
},

// Add render function
async function renderMoodModule(body, title) {
title.textContent = '😊 Mood'
const api = getWtElectronAPI()
const items = await api.mood.getAll()

body.innerHTML = ''
// Render list...
}

5. Add Floating Button

<!-- index.html -->
<button class="float-btn mod-btn" data-mod="mood" title="Mood (J)">
<span class="btn-icon">😊</span>
<span class="btn-label">Mood</span>
</button>

6. Bind Keyboard Shortcut

// app.js
else if (key === 'j') openMod('mood')

7. Connect to renderModule

} else if (id === 'mood') {
renderMoodModule(body, title)
}

Done!

Your custom module is now available in both the World Tree and Metaverse!