Overview
Provide user-configurable settings for your plugin using theSetting component and PluginSettingTab class.
import { PluginSettingTab, Setting } from '@inkdown/api';
class MySettingTab extends PluginSettingTab {
display() {
const { containerEl } = this;
new Setting(containerEl)
.setName('My Setting')
.setDesc('Description here')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enabled)
.onChange(async (value) => {
this.plugin.settings.enabled = value;
await this.plugin.saveSettings();
})
);
}
}
PluginSettingTab
Class Definition
abstract class PluginSettingTab {
app: App;
plugin: Plugin;
containerEl: HTMLElement;
constructor(app: App, plugin: Plugin);
abstract display(): void;
hide(): void;
}
Properties
Reference to the application instance.
Reference to your plugin instance.
Container where settings UI is rendered.
Methods
display()
Build your settings UI here. Called when the settings tab is opened.
display() {
const { containerEl } = this;
// Clear previous content
containerEl.empty();
// Add settings
new Setting(containerEl)
.setName('Setting Name')
.addToggle(/* ... */);
}
hide()
Called when the settings tab is closed. Clean up if needed.
hide() {
// Optional cleanup
}
Basic Example
import { Plugin, PluginSettingTab, Setting } from '@inkdown/api';
interface MyPluginSettings {
enabled: boolean;
apiKey: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
enabled: true,
apiKey: '',
};
export default class MyPlugin extends Plugin {
settings: MyPluginSettings = DEFAULT_SETTINGS;
async onload() {
await this.loadSettings();
this.addSettingTab(new MySettingTab(this.app, this));
}
async loadSettings() {
const data = await this.loadData<MyPluginSettings>();
this.settings = { ...DEFAULT_SETTINGS, ...data };
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class MySettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName('Enable Plugin')
.setDesc('Turn the plugin on or off')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enabled)
.onChange(async (value) => {
this.plugin.settings.enabled = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName('API Key')
.setDesc('Your API key for the service')
.addText(text => text
.setPlaceholder('Enter your API key')
.setValue(this.plugin.settings.apiKey)
.onChange(async (value) => {
this.plugin.settings.apiKey = value;
await this.plugin.saveSettings();
})
);
}
}
Setting Component
Class Definition
class Setting {
settingEl: HTMLElement;
infoEl: HTMLElement;
nameEl: HTMLElement;
descEl: HTMLElement;
controlEl: HTMLElement;
constructor(containerEl: HTMLElement);
setName(name: string): this;
setDesc(desc: string): this;
setClass(cls: string): this;
setHeading(): this;
setDisabled(disabled: boolean): this;
addText(cb: (component: SettingTextComponent) => void): this;
addTextArea(cb: (component: SettingTextAreaComponent) => void): this;
addToggle(cb: (component: SettingToggleComponent) => void): this;
addDropdown(cb: (component: SettingDropdownComponent) => void): this;
addSlider(cb: (component: SettingSliderComponent) => void): this;
addButton(cb: (component: SettingButtonComponent) => void): this;
addExtraButton(cb: (component: SettingExtraButtonComponent) => void): this;
addColorPicker(cb: (component: SettingColorPickerComponent) => void): this;
}
Basic Methods
setName()
Set the setting name/label.
new Setting(containerEl)
.setName('Enable Feature');
setDesc()
Set the setting description.
new Setting(containerEl)
.setName('Enable Feature')
.setDesc('Turn this feature on or off');
setHeading()
Make this setting a heading/section title.
new Setting(containerEl)
.setName('General Settings')
.setHeading();
setClass()
Add a CSS class to the setting element.
new Setting(containerEl)
.setClass('my-custom-setting')
.setName('Custom Setting');
setDisabled()
Enable or disable the setting.
new Setting(containerEl)
.setName('Advanced Option')
.setDisabled(!this.plugin.settings.advancedMode)
.addToggle(/* ... */);
Control Types
Text Input
interface SettingTextComponent {
setValue(value: string): this;
setPlaceholder(placeholder: string): this;
onChange(callback: (value: string) => void): this;
getValue(): string;
}
new Setting(containerEl)
.setName('Username')
.setDesc('Your username')
.addText(text => text
.setPlaceholder('Enter username')
.setValue(this.plugin.settings.username)
.onChange(async (value) => {
this.plugin.settings.username = value;
await this.plugin.saveSettings();
})
);
Text Area
interface SettingTextAreaComponent {
setValue(value: string): this;
setPlaceholder(placeholder: string): this;
onChange(callback: (value: string) => void): this;
getValue(): string;
}
new Setting(containerEl)
.setName('Custom Template')
.setDesc('Enter your template')
.addTextArea(text => text
.setPlaceholder('Template content...')
.setValue(this.plugin.settings.template)
.onChange(async (value) => {
this.plugin.settings.template = value;
await this.plugin.saveSettings();
})
);
Toggle
interface SettingToggleComponent {
setValue(on: boolean): this;
onChange(callback: (value: boolean) => void): this;
getValue(): boolean;
}
new Setting(containerEl)
.setName('Enable Feature')
.setDesc('Turn feature on or off')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.enabled)
.onChange(async (value) => {
this.plugin.settings.enabled = value;
await this.plugin.saveSettings();
})
);
Dropdown
interface SettingDropdownComponent {
addOption(value: string, display: string): this;
addOptions(options: Record<string, string>): this;
setValue(value: string): this;
onChange(callback: (value: string) => void): this;
getValue(): string;
}
// Using addOptions
new Setting(containerEl)
.setName('Theme')
.setDesc('Select theme')
.addDropdown(dropdown => dropdown
.addOptions({
'light': 'Light',
'dark': 'Dark',
'system': 'System',
})
.setValue(this.plugin.settings.theme)
.onChange(async (value) => {
this.plugin.settings.theme = value;
await this.plugin.saveSettings();
})
);
// Using addOption
new Setting(containerEl)
.setName('Language')
.addDropdown(dropdown => {
dropdown.addOption('en', 'English');
dropdown.addOption('es', 'Spanish');
dropdown.addOption('fr', 'French');
dropdown.setValue(this.plugin.settings.language);
dropdown.onChange(async (value) => {
this.plugin.settings.language = value;
await this.plugin.saveSettings();
});
});
Slider
interface SettingSliderComponent {
setLimits(min: number, max: number, step: number | 'any'): this;
setValue(value: number): this;
setDynamicTooltip(): this;
onChange(callback: (value: number) => void): this;
getValue(): number;
}
new Setting(containerEl)
.setName('Font Size')
.setDesc('Adjust the font size')
.addSlider(slider => slider
.setLimits(10, 30, 1)
.setValue(this.plugin.settings.fontSize)
.setDynamicTooltip()
.onChange(async (value) => {
this.plugin.settings.fontSize = value;
await this.plugin.saveSettings();
})
);
Button
interface SettingButtonComponent {
setButtonText(name: string): this;
setIcon(icon: string): this;
setCta(): this;
setWarning(): this;
onClick(callback: () => void): this;
}
new Setting(containerEl)
.setName('Clear Cache')
.setDesc('Remove all cached data')
.addButton(button => button
.setButtonText('Clear')
.setWarning()
.onClick(async () => {
await this.plugin.clearCache();
new Notice('Cache cleared');
})
);
// Call-to-action button
new Setting(containerEl)
.setName('Export Data')
.addButton(button => button
.setButtonText('Export')
.setCta()
.setIcon('download')
.onClick(() => {
this.plugin.exportData();
})
);
Extra Button
Small icon buttons for actions.interface SettingExtraButtonComponent {
setIcon(icon: string): this;
setTooltip(tooltip: string): this;
onClick(callback: () => void): this;
}
new Setting(containerEl)
.setName('API Key')
.addText(text => text.setValue(this.plugin.settings.apiKey))
.addExtraButton(button => button
.setIcon('refresh')
.setTooltip('Generate new API key')
.onClick(async () => {
this.plugin.settings.apiKey = await this.plugin.generateApiKey();
await this.plugin.saveSettings();
this.display(); // Refresh UI
})
)
.addExtraButton(button => button
.setIcon('copy')
.setTooltip('Copy to clipboard')
.onClick(() => {
navigator.clipboard.writeText(this.plugin.settings.apiKey);
new Notice('Copied to clipboard');
})
);
Color Picker
interface SettingColorPickerComponent {
setValue(value: string): this;
onChange(callback: (value: string) => void): this;
getValue(): string;
}
new Setting(containerEl)
.setName('Highlight Color')
.setDesc('Choose highlight color')
.addColorPicker(color => color
.setValue(this.plugin.settings.highlightColor)
.onChange(async (value) => {
this.plugin.settings.highlightColor = value;
await this.plugin.saveSettings();
})
);
Word Count Plugin Example
From the built-in Word Count plugin:import { App, PluginSettingTab, Setting } from '@inkdown/api';
import type WordCountPlugin from './WordCountPlugin';
export class WordCountSettingTab extends PluginSettingTab {
plugin: WordCountPlugin;
constructor(app: App, plugin: WordCountPlugin) {
super(app, plugin);
this.plugin = plugin;
this.icon = 'whole-word'; // Lucide icon name
}
display(): void {
const { containerEl } = this;
// Heading
new Setting(containerEl).setName('Word Count Settings').setHeading();
// Show word count toggle
new Setting(containerEl)
.setName('Show word count')
.setDesc('Display the number of words in the current note')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.showWordCount)
.onChange(async (value) => {
this.plugin.settings.showWordCount = value;
await this.plugin.saveSettings();
})
);
// Show character count toggle
new Setting(containerEl)
.setName('Show character count')
.setDesc('Display the number of characters in the current note')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.showCharCount)
.onChange(async (value) => {
this.plugin.settings.showCharCount = value;
await this.plugin.saveSettings();
})
);
// Count spaces toggle
new Setting(containerEl)
.setName('Count spaces in character count')
.setDesc('Include spaces when counting characters')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.countSpaces)
.onChange(async (value) => {
this.plugin.settings.countSpaces = value;
await this.plugin.saveSettings();
})
);
}
}
Advanced Patterns
Conditional Settings
display() {
const { containerEl } = this;
new Setting(containerEl)
.setName('Enable Advanced Mode')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.advancedMode)
.onChange(async (value) => {
this.plugin.settings.advancedMode = value;
await this.plugin.saveSettings();
this.display(); // Refresh to show/hide advanced settings
})
);
// Only show if advanced mode is enabled
if (this.plugin.settings.advancedMode) {
new Setting(containerEl)
.setName('Advanced Option')
.addText(text => text.setValue(this.plugin.settings.advancedOption));
}
}
Section Headings
display() {
const { containerEl } = this;
new Setting(containerEl)
.setName('General Settings')
.setHeading();
new Setting(containerEl)
.setName('Option 1')
.addToggle(/* ... */);
new Setting(containerEl)
.setName('Option 2')
.addToggle(/* ... */);
new Setting(containerEl)
.setName('Advanced Settings')
.setHeading();
new Setting(containerEl)
.setName('Advanced Option')
.addText(/* ... */);
}
Multiple Controls
new Setting(containerEl)
.setName('Date Format')
.addDropdown(dropdown => dropdown
.addOptions({
'YYYY-MM-DD': 'ISO (2024-01-15)',
'MM/DD/YYYY': 'US (01/15/2024)',
'DD/MM/YYYY': 'EU (15/01/2024)',
})
.setValue(this.plugin.settings.dateFormat)
.onChange(async (value) => {
this.plugin.settings.dateFormat = value;
await this.plugin.saveSettings();
})
)
.addExtraButton(button => button
.setIcon('refresh')
.setTooltip('Reset to default')
.onClick(async () => {
this.plugin.settings.dateFormat = 'YYYY-MM-DD';
await this.plugin.saveSettings();
this.display();
})
);
Custom HTML
display() {
const { containerEl } = this;
// Add description
containerEl.createEl('p', {
text: 'Configure your plugin settings below.',
cls: 'setting-description',
});
// Add link
const linkEl = containerEl.createEl('a', {
text: 'View documentation',
href: 'https://example.com/docs',
});
linkEl.setAttr('target', '_blank');
// Settings
new Setting(containerEl)
.setName('My Setting')
.addToggle(/* ... */);
}
Best Practices
Clear Empty Container
// ✅ Good - clear before rebuilding
display() {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl).setName('Setting');
}
// ❌ Bad - duplicates on refresh
display() {
const { containerEl } = this;
new Setting(containerEl).setName('Setting');
}
Always Save Changes
// ✅ Good
new Setting(containerEl)
.addToggle(toggle => toggle
.onChange(async (value) => {
this.plugin.settings.enabled = value;
await this.plugin.saveSettings(); // Save!
})
);
// ❌ Bad - changes not persisted
new Setting(containerEl)
.addToggle(toggle => toggle
.onChange((value) => {
this.plugin.settings.enabled = value;
// Forgot to save!
})
);
Use Async/Await
// ✅ Good
.onChange(async (value) => {
this.plugin.settings.value = value;
await this.plugin.saveSettings();
await this.plugin.applySettings();
})
// ❌ Bad - not waiting for save
.onChange((value) => {
this.plugin.settings.value = value;
this.plugin.saveSettings(); // Not awaited!
})
Provide Descriptions
// ✅ Good
new Setting(containerEl)
.setName('Enable Feature')
.setDesc('Enables the awesome feature that does X, Y, and Z')
.addToggle(/* ... */);
// ❌ Bad - no description
new Setting(containerEl)
.setName('Enable Feature')
.addToggle(/* ... */);
Related
- Plugin Class - Data persistence
- Commands - Settings-based commands
- Best Practices - Development guidelines
