module Program open Obsidian open Fable.Core open Fable.Core.JsInterop [] let obsidian : Obsidian.IExports = jsNative type SamplePluginSettings = { mutable mySetting : string } let defaultSettings : SamplePluginSettings = { mySetting = "default" } [] type DefaultCommand() = do () let defaultCommand () = let mutable mid = "" let mutable mname = "" let mutable _cb = None let mutable _ccb = None let mutable _hotkeys = None let mutable _editorCallback = None let mutable _editorCheckCallback = None { new Command with member this.callback with get () = _cb and set v = _cb <- v member this.checkCallback with get () = _ccb and set v = _ccb <- v member this.editorCallback with get () = _editorCallback and set v = _editorCallback <- v member this.editorCheckCallback with get () = _editorCheckCallback and set v = _editorCheckCallback <- v member this.hotkeys with get () = _hotkeys and set v = _hotkeys <- v member this.icon: string option = None member this.icon with set (v: string option): unit = () member this.id: string = mid member this.id with set (v: string): unit = mid <- v member this.mobileOnly: bool option = None member this.mobileOnly with set (v: bool option): unit = () member this.name: string = mname member this.name with set (v: string): unit = mname <- v } [] type DefaultPluginSettingTab(app, plugin) = do () type SamplePluginSettingTab(app, plugin) as instance = inherit DefaultPluginSettingTab(app, plugin) let settings = instance :> obj :?> PluginSettingTab let init () = settings?plugin <- plugin instance?display <- fun () -> let containerEl : HTMLElement = settings?containerEl containerEl.empty () let mutable setting : Setting = obsidian.Setting.Create(containerEl) setting <- setting .setName(U2.Case1 "Setting #1") .setDesc(U2.Case1 "It's a secret") .addText(fun text -> text .setPlaceholder("Enter your secret") .setValue(plugin?settings.mySetting) .onChange(fun value -> settings?plugin?settings.mySetting <- value settings?plugin?saveSettings () Some () ) |> ignore Some () ) do init () [] type DefaultModal(app) = do () type SampleModal(app) as instance = inherit DefaultModal(app) let modal = instance :> obj :?> Modal let init () = modal?onOpen <- fun () -> let contentEl : HTMLElement = modal?contentEl U2.Case1 "Woah!" |> contentEl.setText modal?onClose <- fun () -> let contentEl : HTMLElement = modal?contentEl contentEl.empty () do init () // This workaround allows the members to be defined dynamically, without implementing // the whole interface. [] type DefaultPlugin(app, manifest) = do () // Note: This cannot be named "Plugin", or the import from the Obsidian API will be renamed type SamplePlugin(app, manifest) as instance = inherit DefaultPlugin(app, manifest) // This is what is actually holding the plugin data // It would be better to have the main plugin implement this interface, but I can't quite // get it to work right without an absurd amount of boilerplate let plugin = instance :> obj :?> Plugin let init () : unit = plugin?loadSettings <- (fun _ -> promise { let! data = plugin.loadData () match data with | None -> plugin?settings <- defaultSettings | Some v -> try plugin?settings <- v :?> SamplePluginSettings with | _ -> plugin?settings <- defaultSettings } ) plugin?saveSettings <- (fun () -> promise { do! plugin.saveData (Some(!!plugin?settings)) } ) // This creates an icon in the left ribbon. plugin?ribbonIconEl <- // Called when the user clicks the icon. let callback (evt : Browser.Types.MouseEvent) : Obsidian.Notice = obsidian.Notice.Create(U2.Case1 "This is a notice!") in plugin?addRibbonIcon("dice", "Sample Plugin", callback) // Perform additional things with the ribbon (plugin?ribbonIconEl : Element).addClass ( let array = new ResizeArray(1) array.Add("my-plugin-ribbon-class") array ) // This adds a status bar item to the bottom of the app. Does not work on mobile apps. let statusBarItemEl : Element = plugin?addStatusBarItem () "Status Bar Text" |> U2.Case1 |> statusBarItemEl.setText // This adds a simple command that can be triggered anywhere plugin?addCommand ( let mutable cmd = defaultCommand () cmd.id <- "open-sample-modal-simple" cmd.name <- "Open sample modal (simple)" cmd.callback <- Some (fun _ -> Some((new SampleModal(plugin?app) :> obj :?> Modal).``open`` ()) ) cmd ) // This adds an editor command that can perform some operation on the current editor instance plugin?addCommand ( let mutable cmd = defaultCommand () cmd.id <- "sample-editor-command" cmd.name <- "Sample editor command" cmd.editorCallback <- Some (fun (editor : Editor) (view : MarkdownView) -> printf "%A" (editor.getSelection ()) let replacement = editor.replaceSelection ("Sample Editor Command") Some replacement ) cmd ) // This adds a complex command that can check whether the current state of the app allows execution of the command plugin?addCommand ( let mutable cmd = defaultCommand () cmd.id <- "open-sample-modal-complex" cmd.name <- "Open sample modal (complex)" cmd.checkCallback <- Some (fun checking -> // Conditions to check let app : App = plugin?app let markdownView = obsidian.MarkdownView :?> Constructor |> app.workspace.getActiveViewOfType match markdownView with | Some markdownView -> // If checking is true, we're simply "checking" if the command can be run. // If checking is false, then we want to actually perform the operation. if checking |> not then Some((new SampleModal(plugin?app) :> obj :?> Modal).``open`` ()) |> ignore // This command will only show up in Command Palette when the check function returns true true |> U2.Case1 | None -> false |> U2.Case1 ) cmd ) // This adds a settings tab so the user can configure various aspects of the plugin plugin?addSettingTab (new SamplePluginSettingTab (plugin?app, plugin)) // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) // Using this function will automatically remove the event listener when this plugin is disabled. plugin?registerDomEvent (Browser.Dom.document, "click", fun (evt : Browser.Types.MouseEvent) -> ( printfn "click: %A" evt )) // When registering intervals, this function will automatically clear the interval when the plugin is disabled. plugin?registerInterval Browser.Dom.window.setInterval (fun () -> printfn "setInterval") (5 * 60 * 1000) () let onload: unit -> unit = fun _ -> plugin?settings <- plugin?loadSettings () do init () do plugin?onload <- onload // DO NOT REMOVE // Change the name here to match the type name used above emitJsStatement "" "module.exports = SamplePlugin";