* feat: support slack integrations * chore: upgrade nextjs * feat: add user temp internal token --------- Co-authored-by: Boris <boris2code@outlook.com>
Plugin Development Guide
Table of Contents
- Overview
- Plugin Structure
- Creating a Plugin
- Plugin Configuration
- Plugin Bridge
- Internationalization
- Publishing
Overview
Plugins are extensions that can be integrated into different positions within the application. All official plugins are managed within a single Next.js project (plugins), allowing for code sharing and simplified maintenance.
Supported Positions
Plugins can be integrated into two main positions:
export enum PluginPosition {
Dashboard = 'dashboard',
View = 'view',
}
Plugin Structure
Project Structure
The plugins project uses Next.js App Router structure:
plugins/
├── src/
│ ├── app/
│ │ ├── chart/ # Chart plugin
│ │ │ ├── components/
│ │ │ ├── page.tsx
│ │ │ └── favicon.ico
│ │ ├── sheet-form/ # Sheet Form plugin
│ │ │ ├── components/
│ │ │ ├── page.tsx
│ │ │ └── favicon.ico
│ ├── components/ # Shared components
│ ├── locales/ # i18n translations
│ │ ├── chart/
│ │ │ ├── en.json
│ │ │ └── zh.json
│ │ └── sheet-form/
│ │ ├── en.json
│ │ └── zh.json
│ └── types.ts
├── package.json
└── tsconfig.json
Plugin Page Structure
Each plugin should have its own directory under src/app/ with the following structure:
import type { Metadata } from 'next';
import { EnvProvider } from '../../components/EnvProvider';
import { I18nProvider } from '../../components/I18nProvider';
import QueryClientProvider from '../../components/QueryClientProvider';
import { PageType } from '../../components/types';
import enCommonJson from '../../locales/chart/en.json';
import zhCommonJson from '../../locales/chart/zh.json';
import { Pages } from './components/Pages';
export async function generateMetadata({ searchParams }: Props): Promise<Metadata> {
const lang = searchParams.lang;
return {
title: lang === 'zh' ? '图表' : 'Chart',
icons: icon.src,
};
}
export default async function Home(props: { searchParams: IPageParams }) {
return (
<main className="flex h-screen flex-col items-center justify-center">
<EnvProvider>
<I18nProvider
lang={props.searchParams.lang}
resources={resources}
defaultNS="common"
pageType={PageType.Chart}
>
<QueryClientProvider>
<Pages {...props.searchParams} />
</QueryClientProvider>
</I18nProvider>
</EnvProvider>
</main>
);
}
Creating a Plugin
1. Create Plugin Directory
Add a new directory under src/app/ for your plugin:
src/app/my-plugin/
├── components/
├── page.tsx
└── favicon.ico
2. Configure Plugin
Create a plugin configuration file:
import { PluginPosition } from '@teable/openapi';
import type { IOfficialPluginConfig } from './types';
export const myPluginConfig: IOfficialPluginConfig = {
id: 'plg-my-plugin',
name: 'My Plugin',
description: 'Plugin description',
detailDesc: `Detailed description with markdown support`,
helpUrl: 'https://help.teable.ai',
positions: [PluginPosition.Dashboard],
i18n: {
zh: {
name: '我的插件',
helpUrl: 'https://help.teable.cn',
description: '插件描述',
detailDesc: '详细描述',
},
},
logoPath: 'static/plugin/my-plugin.png',
pluginUserId: 'plgmypluginuser',
avatarPath: 'static/plugin/my-plugin.png',
};
Plugin Bridge
The Plugin Bridge enables communication between your plugin and the main application.
Bridge Methods
const methods: IParentBridgeMethods = {
expandRecord: (recordIds) => {
console.log('expandRecord', recordIds);
},
updateStorage: (storage) => {
return updateDashboardPluginStorage(baseId, positionId, pluginInstallId, storage).then(
(res) => res.data.storage ?? {}
);
},
getAuthCode: () => {
return pluginGetAuthCode(pluginId, baseId).then((res) => res.data);
},
expandPlugin: () => {
onExpand?.();
},
};
Initializing the Bridge
export const initializeBridge = async () => {
if (typeof window === 'undefined') {
return;
}
const pluginBridge = new PluginBridge();
const bridge = await pluginBridge.init();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any)._teable_plugin_bridge = bridge;
return bridge;
};
Internationalization
Add translations for your plugin under src/locales/[plugin-name]/:
{
"title": "我的插件",
"description": "插件描述",
"actions": {
"save": "保存",
"cancel": "取消"
}
}
Publishing
- Development Status: Plugins start in
developingstatus - Review Process: Submit for review using the plugin management interface
- Publication: Once approved, the plugin will be published and available in the plugin center
Plugin Status Flow
export enum PluginStatus {
Developing = 'developing',
Reviewing = 'reviewing',
Published = 'published',
}
Best Practices
- Code Sharing: Utilize shared components and utilities from the plugins project
- Consistent UI: Follow the design patterns used by other plugins
- Error Handling: Implement proper error handling and display user-friendly messages
- Responsive Design: Ensure your plugin works well in different container sizes
- Performance: Optimize loading time and resource usage
- Security: Never expose sensitive information in the client-side code
- Documentation: Document your plugin's features and configuration options
For more detailed information and API references, please refer to our complete API documentation.
Best Practices
- Error Handling: Implement proper error handling and display user-friendly messages
- Responsive Design: Ensure your plugin works well in different container sizes
- Performance: Optimize loading time and resource usage
- Security: Never expose sensitive information in the client-side code
- Documentation: Provide clear documentation for your plugin's features and configuration options
For more detailed information and API references, please refer to our complete API documentation.