267 lines
8.7 KiB
Forth
267 lines
8.7 KiB
Forth
module Program
|
|
open Obsidian
|
|
open Fable.Core
|
|
open Fable.Core.JsInterop
|
|
|
|
[<Fable.Core.ImportAll(from = "obsidian")>]
|
|
let obsidian : Obsidian.IExports = jsNative
|
|
|
|
type SamplePluginSettings = {
|
|
mutable mySetting : string
|
|
}
|
|
|
|
let defaultSettings : SamplePluginSettings = {
|
|
mySetting = "default"
|
|
}
|
|
|
|
[<Fable.Core.Import("Command", from = "obsidian")>]
|
|
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
|
|
}
|
|
|
|
[<Fable.Core.Import("PluginSettingTab", from = "obsidian")>]
|
|
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 ()
|
|
|
|
|
|
[<Fable.Core.Import("Modal", from = "obsidian")>]
|
|
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.
|
|
[<Fable.Core.Import("Plugin", from = "obsidian")>]
|
|
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<string>(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<MarkdownView>
|
|
|> 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";
|