Build tool
This commit is contained in:
Vendored
+1
@@ -0,0 +1 @@
|
||||
{}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"theme": "obsidian"
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"frontmatter-folder-organizer"
|
||||
]
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"footnotes": false,
|
||||
"properties": true,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": true,
|
||||
"bases": true,
|
||||
"webviewer": false
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "frontmatter-folder-organizer",
|
||||
"name": "Frontmatter Folder Organizer",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "1.0.0",
|
||||
"description": "Organize markdown files into folders based on frontmatter hierarchy.",
|
||||
"author": "Obsidian",
|
||||
"authorUrl": "https://obsidian.md",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
|
||||
This CSS file will be included with your plugin, and
|
||||
available in the app when your plugin is enabled.
|
||||
|
||||
If your plugin does not need CSS, delete this file.
|
||||
|
||||
*/
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"types": {
|
||||
"aliases": "aliases",
|
||||
"cssclasses": "multitext",
|
||||
"tags": "tags",
|
||||
"shouldInstallPlugins": "checkbox"
|
||||
}
|
||||
}
|
||||
Vendored
+194
@@ -0,0 +1,194 @@
|
||||
{
|
||||
"main": {
|
||||
"id": "eeb36d4e48f0ca25",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "4fe61a13d63c2732",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "362018eb264ab766",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "docs/op.docs.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "op.docs"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "b9bf87c6cad06498",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "6685cdba50e4a0a7",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "6ef95b00a28c418d",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical",
|
||||
"autoReveal": false
|
||||
},
|
||||
"icon": "lucide-folder-closed",
|
||||
"title": "Files"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c5043b22e38e990c",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
},
|
||||
"icon": "lucide-search",
|
||||
"title": "Search"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b159963fcca61ebc",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {},
|
||||
"icon": "lucide-bookmark",
|
||||
"title": "Bookmarks"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "c5e47ea76d041492",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "897865c856161773",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "379d0b312b9d30c9",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "doc.zs.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-coming-in",
|
||||
"title": "Backlinks for doc.zs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "a8f559c72a82e929",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "doc.zs.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-going-out",
|
||||
"title": "Outgoing links from doc.zs"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e1552c44ca209983",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-tags",
|
||||
"title": "Tags"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2d2defa7638fb5a3",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "all-properties",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-archive",
|
||||
"title": "All properties"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "82b06aefc5b6df13",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "doc.zs.md",
|
||||
"followCursor": false,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-list",
|
||||
"title": "Outline of doc.zs"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false,
|
||||
"bases:Create new base": false
|
||||
}
|
||||
},
|
||||
"active": "362018eb264ab766",
|
||||
"lastOpenFiles": [
|
||||
"docs/doc.zs.md",
|
||||
"docs/op.docs.md",
|
||||
"docs",
|
||||
"os.docs.md",
|
||||
"PLUGIN_SETTINGS_UI_GUIDE.md"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
# Plugin Settings UI Guide
|
||||
|
||||
This guide explains how to structure and format plugin settings UI in Obsidian, with examples for building multiple plugins with distinct features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Core Concepts](#core-concepts)
|
||||
2. [Setting Types](#setting-types)
|
||||
3. [UI Organization Patterns](#ui-organization-patterns)
|
||||
4. [Multi-Plugin Architecture](#multi-plugin-architecture)
|
||||
5. [Plugin Examples](#plugin-examples)
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Settings Interface & Settings Tab
|
||||
|
||||
Every plugin has:
|
||||
|
||||
1. **Settings Interface** - TypeScript interface defining your data structure
|
||||
2. **Settings Tab Class** - Extends `PluginSettingTab` and defines the UI
|
||||
|
||||
```typescript
|
||||
// Define the data structure
|
||||
export interface MyPluginSettings {
|
||||
settingName: string;
|
||||
settingValue: boolean;
|
||||
}
|
||||
|
||||
// Default values
|
||||
export const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
settingName: 'default',
|
||||
settingValue: false,
|
||||
};
|
||||
|
||||
// UI definition
|
||||
export class MySettingTab extends PluginSettingTab {
|
||||
plugin: MyPlugin;
|
||||
|
||||
constructor(app: App, plugin: MyPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty(); // Clear previous UI
|
||||
|
||||
// Add UI elements here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Container Structure
|
||||
|
||||
The `containerEl` is where you build your UI. Always start with `containerEl.empty()` to clear old content when the settings tab reopens.
|
||||
|
||||
```typescript
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
// Add heading
|
||||
containerEl.createEl('h2', { text: 'Plugin Name' });
|
||||
|
||||
// Add settings
|
||||
new Setting(containerEl)
|
||||
.setName('Setting Name')
|
||||
.setDesc('Description of what this does')
|
||||
.addText((text) => { /* ... */ });
|
||||
}
|
||||
```
|
||||
|
||||
## Setting Types
|
||||
|
||||
### Text Input
|
||||
|
||||
Single-line text input:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Plugin Name')
|
||||
.setDesc('The name of your plugin')
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder('Enter name')
|
||||
.setValue(this.plugin.settings.pluginName)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.pluginName = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Text Area
|
||||
|
||||
Multi-line text input:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Configuration')
|
||||
.setDesc('Multi-line configuration')
|
||||
.addTextArea((text) =>
|
||||
text
|
||||
.setPlaceholder('Enter configuration')
|
||||
.setValue(this.plugin.settings.config)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.config = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Toggle
|
||||
|
||||
Boolean switch:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Feature')
|
||||
.setDesc('Turn this 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
|
||||
|
||||
Select from predefined options:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Theme')
|
||||
.setDesc('Choose a color theme')
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('light', 'Light')
|
||||
.addOption('dark', 'Dark')
|
||||
.addOption('auto', 'Auto')
|
||||
.setValue(this.plugin.settings.theme)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.theme = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Slider
|
||||
|
||||
Numeric input with range:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Search Depth')
|
||||
.setDesc('How deep to search in folders (1-10)')
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setMin(1)
|
||||
.setMax(10)
|
||||
.setStep(1)
|
||||
.setValue(this.plugin.settings.searchDepth)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.searchDepth = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Button
|
||||
|
||||
Trigger an action:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Process Files')
|
||||
.setDesc('Start processing all files')
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText('Process Now')
|
||||
.onClick(async () => {
|
||||
await this.plugin.processAllFiles();
|
||||
new Notice('Processing complete!');
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
### Color Picker
|
||||
|
||||
Select a color:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Tag Color')
|
||||
.setDesc('Color for tags')
|
||||
.addColorPicker((color) =>
|
||||
color
|
||||
.setValue(this.plugin.settings.tagColor)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.tagColor = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
## UI Organization Patterns
|
||||
|
||||
### Section Headers
|
||||
|
||||
Organize settings into logical sections:
|
||||
|
||||
```typescript
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
// Main heading
|
||||
containerEl.createEl('h2', { text: 'Plugin Settings' });
|
||||
|
||||
// Section 1
|
||||
containerEl.createEl('h3', { text: 'Basic Configuration' });
|
||||
new Setting(containerEl).setName('Setting 1')...
|
||||
new Setting(containerEl).setName('Setting 2')...
|
||||
|
||||
// Section 2
|
||||
containerEl.createEl('h3', { text: 'Advanced Options' });
|
||||
new Setting(containerEl).setName('Setting 3')...
|
||||
new Setting(containerEl).setName('Setting 4')...
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Display
|
||||
|
||||
Show/hide settings based on other settings:
|
||||
|
||||
```typescript
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Advanced')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.advancedMode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.advancedMode = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.display(); // Redraw to show/hide advanced settings
|
||||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.advancedMode) {
|
||||
new Setting(containerEl)
|
||||
.setName('Advanced Option')
|
||||
.setDesc('This only shows when advanced mode is on')
|
||||
.addText((text) => { /* ... */ });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Grouped Controls
|
||||
|
||||
Multiple controls in one setting:
|
||||
|
||||
```typescript
|
||||
new Setting(containerEl)
|
||||
.setName('Color')
|
||||
.setDesc('Choose color and opacity')
|
||||
.addColorPicker((color) =>
|
||||
color
|
||||
.setValue(this.plugin.settings.color)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.color = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
)
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setMin(0)
|
||||
.setMax(100)
|
||||
.setValue(this.plugin.settings.opacity)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.opacity = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
## Multi-Plugin Architecture
|
||||
|
||||
When building multiple plugins within one codebase, consider this structure:
|
||||
|
||||
### Folder Structure
|
||||
|
||||
```
|
||||
src/
|
||||
main.ts (Plugin entry point)
|
||||
settings.ts (Shared settings)
|
||||
plugins/
|
||||
folder/
|
||||
folderPlugin.ts
|
||||
folderSettings.ts
|
||||
markdown/
|
||||
markdownPlugin.ts
|
||||
markdownSettings.ts
|
||||
kanban/
|
||||
kanbanPlugin.ts
|
||||
kanbanSettings.ts
|
||||
```
|
||||
|
||||
### Unified Settings Interface
|
||||
|
||||
```typescript
|
||||
// settings.ts
|
||||
export interface PluginConfig {
|
||||
// Folder Plugin settings
|
||||
folderPlugin: {
|
||||
enabled: boolean;
|
||||
organizationHierarchy: string;
|
||||
};
|
||||
|
||||
// Markdown Plugin settings
|
||||
markdownPlugin: {
|
||||
enabled: boolean;
|
||||
autoCreatePath: string;
|
||||
template: string;
|
||||
};
|
||||
|
||||
// Kanban Plugin settings
|
||||
kanbanPlugin: {
|
||||
enabled: boolean;
|
||||
boardTemplate: string;
|
||||
defaultLayout: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: PluginConfig = {
|
||||
folderPlugin: {
|
||||
enabled: true,
|
||||
organizationHierarchy: 'Category, Status',
|
||||
},
|
||||
markdownPlugin: {
|
||||
enabled: true,
|
||||
autoCreatePath: 'Notes',
|
||||
template: 'default',
|
||||
},
|
||||
kanbanPlugin: {
|
||||
enabled: false,
|
||||
boardTemplate: 'default',
|
||||
defaultLayout: 'columns',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Unified Settings Tab
|
||||
|
||||
```typescript
|
||||
// settings.ts
|
||||
export class UnifiedSettingTab extends PluginSettingTab {
|
||||
plugin: MultiPluginBase;
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', { text: 'Plugin Suite Settings' });
|
||||
|
||||
// Folder Plugin Section
|
||||
this.displayFolderPluginSettings(containerEl);
|
||||
|
||||
// Markdown Plugin Section
|
||||
this.displayMarkdownPluginSettings(containerEl);
|
||||
|
||||
// Kanban Plugin Section
|
||||
this.displayKanbanPluginSettings(containerEl);
|
||||
}
|
||||
|
||||
displayFolderPluginSettings(containerEl: HTMLElement): void {
|
||||
containerEl.createEl('h3', { text: 'Folder Organization Plugin' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Folder Plugin')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.folderPlugin.enabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.folderPlugin.enabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.folderPlugin.enabled) {
|
||||
new Setting(containerEl)
|
||||
.setName('Organization Hierarchy')
|
||||
.setDesc('Comma-separated frontmatter fields')
|
||||
.addTextArea((text) =>
|
||||
text
|
||||
.setValue(this.plugin.settings.folderPlugin.organizationHierarchy)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.folderPlugin.organizationHierarchy = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
displayMarkdownPluginSettings(containerEl: HTMLElement): void {
|
||||
containerEl.createEl('h3', { text: 'Markdown Auto-Create Plugin' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Markdown Plugin')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.markdownPlugin.enabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.markdownPlugin.enabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.markdownPlugin.enabled) {
|
||||
new Setting(containerEl)
|
||||
.setName('Auto-Create Path')
|
||||
.setDesc('Default folder for auto-created files')
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder('Notes')
|
||||
.setValue(this.plugin.settings.markdownPlugin.autoCreatePath)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.markdownPlugin.autoCreatePath = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Template')
|
||||
.setDesc('Template to use for new files')
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('default', 'Default')
|
||||
.addOption('minimal', 'Minimal')
|
||||
.addOption('detailed', 'Detailed')
|
||||
.setValue(this.plugin.settings.markdownPlugin.template)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.markdownPlugin.template = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
displayKanbanPluginSettings(containerEl: HTMLElement): void {
|
||||
containerEl.createEl('h3', { text: 'Kanban Board Plugin' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Enable Kanban Plugin')
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.kanbanPlugin.enabled)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.kanbanPlugin.enabled = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
if (this.plugin.settings.kanbanPlugin.enabled) {
|
||||
new Setting(containerEl)
|
||||
.setName('Board Template')
|
||||
.setDesc('Default board template')
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('default', 'Default')
|
||||
.addOption('agile', 'Agile')
|
||||
.addOption('gtd', 'Getting Things Done')
|
||||
.setValue(this.plugin.settings.kanbanPlugin.boardTemplate)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.kanbanPlugin.boardTemplate = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Default Layout')
|
||||
.setDesc('How boards are displayed by default')
|
||||
.addDropdown((dropdown) =>
|
||||
dropdown
|
||||
.addOption('columns', 'Columns')
|
||||
.addOption('rows', 'Rows')
|
||||
.addOption('grid', 'Grid')
|
||||
.setValue(this.plugin.settings.kanbanPlugin.defaultLayout)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.kanbanPlugin.defaultLayout = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Examples
|
||||
|
||||
### Example 1: Folder Organization Plugin
|
||||
|
||||
**Settings Interface:**
|
||||
```typescript
|
||||
export interface FolderOrganizerSettings {
|
||||
enabled: boolean;
|
||||
organizationHierarchy: string;
|
||||
createMissingFolders: boolean;
|
||||
overwriteExisting: boolean;
|
||||
excludeFolders: string[];
|
||||
}
|
||||
```
|
||||
|
||||
**Key UI Elements:**
|
||||
- Toggle to enable/disable
|
||||
- Text area for hierarchy configuration
|
||||
- Toggle for auto-create folders
|
||||
- Toggle for overwrite confirmation
|
||||
- Multi-select or list for excluded folders
|
||||
- Action button to organize files
|
||||
|
||||
### Example 2: Markdown Auto-Create Plugin
|
||||
|
||||
**Settings Interface:**
|
||||
```typescript
|
||||
export interface MarkdownAutoCreateSettings {
|
||||
enabled: boolean;
|
||||
defaultPath: string;
|
||||
template: 'default' | 'minimal' | 'detailed';
|
||||
autoOpenNewFile: boolean;
|
||||
defaultFrontmatter: string;
|
||||
hotkey: string;
|
||||
}
|
||||
```
|
||||
|
||||
**Key UI Elements:**
|
||||
- Toggle to enable/disable
|
||||
- Text input for default path
|
||||
- Dropdown for template selection
|
||||
- Toggle for auto-open
|
||||
- Text area for frontmatter template
|
||||
- Display current hotkey (or hotkey picker if available)
|
||||
|
||||
### Example 3: Kanban Board Plugin
|
||||
|
||||
**Settings Interface:**
|
||||
```typescript
|
||||
export interface KanbanBoardSettings {
|
||||
enabled: boolean;
|
||||
boardTemplate: 'default' | 'agile' | 'gtd';
|
||||
defaultLayout: 'columns' | 'rows' | 'grid';
|
||||
cardWidth: number;
|
||||
showDates: boolean;
|
||||
showTags: boolean;
|
||||
defaultColumns: string[];
|
||||
darkMode: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
**Key UI Elements:**
|
||||
- Toggle to enable/disable
|
||||
- Dropdown for board templates
|
||||
- Dropdown for default layout
|
||||
- Slider for card width
|
||||
- Toggles for feature flags
|
||||
- Text area for default columns (comma-separated)
|
||||
- Color picker for theme customization
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always save settings** - Call `await this.plugin.saveSettings()` after any change
|
||||
2. **Use descriptive names** - Users should understand what each setting does
|
||||
3. **Group related settings** - Use section headers (h3) to organize
|
||||
4. **Provide defaults** - Always have sensible defaults in `DEFAULT_SETTINGS`
|
||||
5. **Validate input** - Check user input before saving (trim, validate paths, etc.)
|
||||
6. **Use conditionals wisely** - Show/hide advanced options based on basic settings
|
||||
7. **Provide feedback** - Use `new Notice()` to confirm actions
|
||||
8. **Redraw on enable/disable** - Call `this.display()` to update UI when features toggle
|
||||
9. **Document complex settings** - Use `.setDesc()` liberally
|
||||
10. **Test multi-step workflows** - Ensure settings changes work with all plugins enabled
|
||||
|
||||
## Testing Your Settings UI
|
||||
|
||||
```typescript
|
||||
// In your test file
|
||||
const plugin = new MyPlugin(app, manifest);
|
||||
plugin.loadSettings();
|
||||
plugin.registerSettingTab(new UnifiedSettingTab(app, plugin));
|
||||
|
||||
// Settings should load without errors
|
||||
// Check that all toggles/inputs work
|
||||
// Verify settings persist after reload
|
||||
// Test conditional displays
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
This guide provides a foundation for building clean, organized, and user-friendly plugin settings. Adapt these patterns to your specific needs!
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: docs
|
||||
path: ZeroSpace\zs.docs
|
||||
shouldInstallPlugins: true
|
||||
---
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: docs
|
||||
path: Obsidian-Plugins\op.docs
|
||||
---
|
||||
Reference in New Issue
Block a user