Skip to main content Inicio Creadores dicklesworthstone pi_agent_rust obsidian-core-workflow-b
obsidian-core-workflow-b Execute Obsidian secondary workflow: UI components and user interaction.
Use when building modals, views, suggestions, or custom UI elements.
Trigger with phrases like "obsidian modal", "obsidian UI",
"obsidian view", "obsidian custom interface".
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/Dicklesworthstone/pi_agent_rust --skill obsidian-core-workflow-bEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Ocupaciones relacionadas SOC
Basado en la clasificación ocupacional SOC
name obsidian-core-workflow-b description Execute Obsidian secondary workflow: UI components and user interaction.
Use when building modals, views, suggestions, or custom UI elements.
Trigger with phrases like "obsidian modal", "obsidian UI",
"obsidian view", "obsidian custom interface".
allowed-tools Read, Write, Edit, Bash(npm:*), Grep version 1.0.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io>
Obsidian Core Workflow B: UI Components
Overview
Secondary workflow for Obsidian: building modals, views, suggestion popups, and custom UI elements.
Prerequisites
Completed obsidian-install-auth setup
Familiarity with obsidian-core-workflow-a
Understanding of DOM manipulation
Instructions
Step 1: Modal Dialogs
import { , , } ;
{
: = ;
: ;
: ;
( ) {
(app);
. = message;
. = onSubmit;
}
( ) {
{ contentEl } = ;
contentEl. ( , { : });
contentEl. ( , { : . });
(contentEl)
. ( btn
. ( )
. ( {
. = ;
. ();
}))
. ( btn
. ( )
. ()
. ( {
. = ;
. ();
}));
}
( ) {
. ( . );
}
}
{
: = ;
: ;
: ;
: ;
( ) {
(app);
. = title;
. = placeholder;
. = onSubmit;
}
( ) {
{ contentEl } = ;
contentEl. ( , { : . });
(contentEl)
. ( )
. ( text
. ( . )
. ( . = value));
(contentEl)
. ( btn
. ( )
. ( {
. ();
. ( );
}))
. ( btn
. ( )
. ()
. ( {
. ();
. ( . );
}));
}
( ) {
{ contentEl } = ;
contentEl. ();
}
}
App
Modal
Setting
from
'obsidian'
export
class
ConfirmModal
extends
Modal
private
result
boolean
false
private
onSubmit
(result : boolean ) =>
void
private
message
string
constructor
app : App , message : string , onSubmit : (result: boolean ) => void
super
this
message
this
onSubmit
onOpen
const
this
createEl
'h2'
text
'Confirm Action'
createEl
'p'
text
this
message
new
Setting
addButton
btn =>
setButtonText
'Cancel'
onClick
() =>
this
result
false
this
close
addButton
btn =>
setButtonText
'Confirm'
setCta
onClick
() =>
this
result
true
this
close
onClose
this
onSubmit
this
result
export
class
InputModal
extends
Modal
private
value
string
''
private
onSubmit
(value : string | null ) =>
void
private
placeholder
string
private
title
string
constructor
app : App ,
title : string ,
placeholder : string ,
onSubmit : (value: string | null ) => void
super
this
title
this
placeholder
this
onSubmit
onOpen
const
this
createEl
'h2'
text
this
title
new
Setting
setName
'Input'
addText
text =>
setPlaceholder
this
placeholder
onChange
value =>
this
value
new
Setting
addButton
btn =>
setButtonText
'Cancel'
onClick
() =>
this
close
this
onSubmit
null
addButton
btn =>
setButtonText
'Submit'
setCta
onClick
() =>
this
close
this
onSubmit
this
value
onClose
const
this
empty
Step 2: Suggestion Popups (FuzzySuggestModal) import { App , FuzzySuggestModal , TFile } from 'obsidian' ;
export class FileSuggestModal extends FuzzySuggestModal <TFile > {
private onSelect : (file : TFile ) => void ;
constructor (app : App , onSelect : (file: TFile) => void ) {
super (app);
this .onSelect = onSelect;
}
getItems (): TFile [] {
return this .app .vault .getMarkdownFiles ();
}
getItemText (file : TFile ): string {
return file.path ;
}
onChooseItem (file : TFile , evt : MouseEvent | KeyboardEvent ): void {
this .onSelect (file);
}
}
export class SuggestModal <T> extends FuzzySuggestModal <T> {
private items : T[];
private getText : (item : T ) => string ;
private onSelect : (item : T ) => void ;
constructor (
app : App ,
items : T[],
getText : (item: T) => string ,
onSelect : (item: T) => void
) {
super (app);
this .items = items;
this .getText = getText;
this .onSelect = onSelect;
}
getItems (): T[] {
return this .items ;
}
getItemText (item : T): string {
return this .getText (item);
}
onChooseItem (item : T, evt : MouseEvent | KeyboardEvent ): void {
this .onSelect (item);
}
}
new SuggestModal (
this .app ,
['Option 1' , 'Option 2' , 'Option 3' ],
(item ) => item,
(selected ) => console .log ('Selected:' , selected)
).open ();
Step 3: Custom Views (Leaf Views) import { ItemView , WorkspaceLeaf } from 'obsidian' ;
export const VIEW_TYPE_CUSTOM = 'custom-view' ;
export class CustomView extends ItemView {
constructor (leaf : WorkspaceLeaf ) {
super (leaf);
}
getViewType (): string {
return VIEW_TYPE_CUSTOM ;
}
getDisplayText (): string {
return 'Custom View' ;
}
getIcon (): string {
return 'dice' ;
}
async onOpen ( ) {
const container = this .containerEl .children [1 ];
container.empty ();
container.createEl ('h4' , { text : 'Custom View Title' });
const content = container.createDiv ({ cls : 'custom-view-content' });
content.createEl ('p' , { text : 'This is a custom view!' });
const button = content.createEl ('button' , { text : 'Click me' });
button.addEventListener ('click' , () => {
console .log ('Button clicked!' );
});
}
async onClose ( ) {
}
}
export default class MyPlugin extends Plugin {
async onload ( ) {
this .registerView (
VIEW_TYPE_CUSTOM ,
(leaf ) => new CustomView (leaf)
);
this .addRibbonIcon ('dice' , 'Open Custom View' , () => {
this .activateView ();
});
this .addCommand ({
id : 'open-custom-view' ,
name : 'Open Custom View' ,
callback : () => this .activateView (),
});
}
async activateView ( ) {
const { workspace } = this .app ;
let leaf = workspace.getLeavesOfType (VIEW_TYPE_CUSTOM )[0 ];
if (!leaf) {
leaf = workspace.getRightLeaf (false );
await leaf.setViewState ({
type : VIEW_TYPE_CUSTOM ,
active : true ,
});
}
workspace.revealLeaf (leaf);
}
onunload ( ) {
this .app .workspace .detachLeavesOfType (VIEW_TYPE_CUSTOM );
}
}
Step 4: Editor Extensions (CodeMirror 6) import { EditorView , ViewPlugin , Decoration , DecorationSet } from '@codemirror/view' ;
import { RangeSetBuilder } from '@codemirror/state' ;
import { syntaxTree } from '@codemirror/language' ;
const highlightPlugin = ViewPlugin .fromClass (class {
decorations : DecorationSet ;
constructor (view : EditorView ) {
this .decorations = this .buildDecorations (view);
}
update (update : any ) {
if (update.docChanged || update.viewportChanged ) {
this .decorations = this .buildDecorations (update.view );
}
}
buildDecorations (view : EditorView ): DecorationSet {
const builder = new RangeSetBuilder <Decoration >();
for (const { from , to } of view.visibleRanges ) {
syntaxTree (view.state ).iterate ({
from ,
to,
enter : (node ) => {
if (node.name === 'HyperLink' ) {
builder.add (
node.from ,
node.to ,
Decoration .mark ({ class : 'custom-highlight' })
);
}
},
});
}
return builder.finish ();
}
}, {
decorations : (v ) => v.decorations ,
});
this .registerEditorExtension (highlightPlugin);
Step 5: Context Menus import { Menu , TFile , TFolder } from 'obsidian' ;
this .registerEvent (
this .app .workspace .on ('file-menu' , (menu : Menu , file : TAbstractFile ) => {
if (file instanceof TFile && file.extension === 'md' ) {
menu.addItem ((item ) => {
item
.setTitle ('My Custom Action' )
.setIcon ('star' )
.onClick (async () => {
console .log ('File:' , file.path );
});
});
}
})
);
this .registerEvent (
this .app .workspace .on ('editor-menu' , (menu : Menu , editor : Editor , view : MarkdownView ) => {
menu.addItem ((item ) => {
item
.setTitle ('Insert Timestamp' )
.setIcon ('clock' )
.onClick (() => {
editor.replaceSelection (new Date ().toISOString ());
});
});
})
);
Output
Modal dialogs for user input
Suggestion popups with fuzzy search
Custom sidebar views
Editor decorations
Context menus
Error Handling Error Cause Solution View not showing Not registered Call registerView in onload Modal closes immediately Event propagation Stop event propagation Decorations not updating Missing update handler Implement update method Menu item missing Wrong event Verify event type
Examples
Progress Modal export class ProgressModal extends Modal {
private progressEl : HTMLElement ;
private textEl : HTMLElement ;
onOpen ( ) {
const { contentEl } = this ;
contentEl.createEl ('h2' , { text : 'Processing...' });
this .textEl = contentEl.createEl ('p' , { text : 'Starting...' });
const progressContainer = contentEl.createDiv ({ cls : 'progress-container' });
this .progressEl = progressContainer.createDiv ({ cls : 'progress-bar' });
this .progressEl .style .width = '0%' ;
}
setProgress (percent : number , text ?: string ) {
this .progressEl .style .width = `${percent} %` ;
if (text) this .textEl .setText (text);
}
onClose ( ) {
this .contentEl .empty ();
}
}
const modal = new ProgressModal (this .app );
modal.open ();
for (let i = 0 ; i <= 100 ; i += 10 ) {
modal.setProgress (i, `Processing ${i} %` );
await sleep (100 );
}
modal.close ();
Sidebar Styling
.custom-view-content {
padding : 16px ;
}
.custom-view-content h4 {
margin-bottom : 12px ;
}
.custom-view-content button {
margin-top : 8px ;
}
Resources
Next Steps For common errors, see obsidian-common-errors.