2022-03-22 13:17:10 +01:00
import { App , ButtonComponent , DropdownComponent , ItemView , Plugin , PluginSettingTab , Setting , WorkspaceLeaf } from "obsidian" ;
import { remote } from "electron" ;
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
const defaultSettings : CustomFramesSettings = {
2022-03-22 13:17:10 +01:00
frames : [ ] ,
padding : 5
} ;
const presets : Record < string , CustomFrame > = {
"keep" : {
url : "https://keep.google.com" ,
displayName : "Google Keep" ,
minimumWidth : 370 ,
customCss : ` /* hide the menu bar and the "Keep" text */
2022-03-22 01:06:16 +01:00
. PvRhvb - qAWA2 , . gb_2d . gb_Zc {
2022-03-20 13:55:19 +01:00
display : none ! important ;
} `
2022-03-22 13:17:10 +01:00
}
2022-03-22 00:51:28 +01:00
} ;
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
interface CustomFramesSettings {
2022-03-22 13:17:10 +01:00
frames : CustomFrame [ ] ;
2022-03-20 13:55:19 +01:00
padding : number ;
2022-03-22 13:17:10 +01:00
}
interface CustomFrame {
url : string ;
displayName : string ;
minimumWidth : number ;
customCss : string ;
2022-03-20 13:55:19 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
export default class CustomFramesPlugin extends Plugin {
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
settings : CustomFramesSettings ;
2022-03-19 20:21:16 +01:00
2022-03-20 00:20:09 +01:00
async onload ( ) : Promise < void > {
2022-03-20 13:55:19 +01:00
await this . loadSettings ( ) ;
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
for ( let frame of this . settings . frames ) {
if ( ! frame . url || ! frame . displayName )
continue ;
let name = ` custom-frames- ${ frame . displayName . toLowerCase ( ) . replace ( /\s/g , "-" ) } ` ;
try {
console . log ( ` Registering frame ${ name } for URL ${ frame . url } ` ) ;
this . registerView ( name , l = > new CustomFrameView ( l , this . settings , frame , name ) ) ;
this . addCommand ( {
id : ` open- ${ name } ` ,
name : ` Open ${ frame . displayName } ` ,
2022-03-22 13:22:58 +01:00
callback : ( ) = > this . openLeaf ( name ) ,
2022-03-22 13:17:10 +01:00
} ) ;
} catch {
console . error ( ` Couldn't register frame ${ name } , is there already one with the same name? ` ) ;
}
}
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
this . addSettingTab ( new CustomFramesSettingTab ( this . app , this ) ) ;
2022-03-20 00:20:09 +01:00
}
2022-03-20 13:55:19 +01:00
async loadSettings() {
this . settings = Object . assign ( { } , defaultSettings , await this . loadData ( ) ) ;
}
async saveSettings() {
await this . saveData ( this . settings ) ;
}
2022-03-22 13:22:58 +01:00
private async openLeaf ( name : string ) : Promise < void > {
if ( ! this . app . workspace . getLeavesOfType ( name ) . length )
await this . app . workspace . getRightLeaf ( false ) . setViewState ( { type : name } ) ;
this . app . workspace . revealLeaf ( this . app . workspace . getLeavesOfType ( name ) [ 0 ] ) ;
}
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
class CustomFrameView extends ItemView {
2022-03-19 20:21:16 +01:00
2022-03-22 12:03:48 +01:00
private settings : CustomFramesSettings ;
2022-03-22 13:17:10 +01:00
private frame : CustomFrame ;
private name : string ;
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
constructor ( leaf : WorkspaceLeaf , settings : CustomFramesSettings , frame : CustomFrame , name : string ) {
2022-03-20 13:55:19 +01:00
super ( leaf ) ;
this . settings = settings ;
2022-03-22 13:17:10 +01:00
this . frame = frame ;
this . name = name ;
2022-03-20 13:55:19 +01:00
}
2022-03-22 00:51:28 +01:00
onload ( ) : void {
this . contentEl . empty ( ) ;
2022-03-22 13:17:10 +01:00
this . contentEl . addClass ( "custom-frames-view" ) ;
2022-03-22 00:51:28 +01:00
let frame = this . contentEl . createEl ( "iframe" ) ;
2022-03-22 13:17:10 +01:00
frame . addClass ( "custom-frames-frame" ) ;
2022-03-22 00:51:28 +01:00
frame . setAttribute ( "style" , ` padding: ${ this . settings . padding } px ` ) ;
frame . onload = ( ) = > {
for ( let other of remote . getCurrentWebContents ( ) . mainFrame . frames ) {
if ( frame . src . contains ( new URL ( other . url ) . host ) ) {
other . executeJavaScript ( `
let style = document . createElement ( "style" ) ;
2022-03-22 13:17:10 +01:00
style . textContent = \ ` ${ this . frame . customCss } \` ;
2022-03-22 00:51:28 +01:00
document . head . appendChild ( style ) ;
` );
}
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 13:17:10 +01:00
if ( this . frame . minimumWidth ) {
2022-03-20 19:32:21 +01:00
let parent = this . contentEl . closest < HTMLElement > ( ".workspace-split.mod-horizontal" ) ;
if ( parent ) {
2022-03-22 13:17:10 +01:00
let minWidth = ` ${ this . frame . minimumWidth + 2 * this . settings . padding } px ` ;
2022-03-20 19:32:21 +01:00
if ( parent . style . width < minWidth )
parent . style . width = minWidth ;
}
}
2022-03-22 00:51:28 +01:00
} ;
2022-03-22 13:17:10 +01:00
frame . src = this . frame . url ;
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 00:51:28 +01:00
getViewType ( ) : string {
2022-03-22 13:17:10 +01:00
return this . name ;
2022-03-22 00:51:28 +01:00
}
getDisplayText ( ) : string {
2022-03-22 13:17:10 +01:00
return this . frame . displayName ;
2022-03-20 00:20:09 +01:00
}
2022-03-19 20:21:16 +01:00
2022-03-22 00:51:28 +01:00
getIcon ( ) : string {
return "documents" ;
2022-03-19 20:21:16 +01:00
}
2022-03-20 13:55:19 +01:00
}
2022-03-22 12:03:48 +01:00
class CustomFramesSettingTab extends PluginSettingTab {
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
plugin : CustomFramesPlugin ;
2022-03-20 13:55:19 +01:00
2022-03-22 12:03:48 +01:00
constructor ( app : App , plugin : CustomFramesPlugin ) {
2022-03-20 13:55:19 +01:00
super ( app , plugin ) ;
2022-03-20 13:57:02 +01:00
this . plugin = plugin ;
2022-03-20 13:55:19 +01:00
}
display ( ) : void {
this . containerEl . empty ( ) ;
2022-03-22 13:17:10 +01:00
this . containerEl . createEl ( "h2" , { text : "Custom Frames Settings" } ) ;
this . containerEl . createEl ( "p" , { text : "Note that Obsidian has to be restarted or reloaded for most of these settings to take effect." } ) ;
2022-03-20 13:55:19 +01:00
new Setting ( this . containerEl )
2022-03-22 13:17:10 +01:00
. setName ( "Frame Padding" )
. setDesc ( "The padding that should be left around the inside of custom frame tabs, in pixels." )
2022-03-20 19:32:21 +01:00
. addText ( t = > {
t . inputEl . type = "number" ;
t . setValue ( String ( this . plugin . settings . padding ) ) ;
t . onChange ( async v = > {
2022-03-21 19:39:03 +01:00
this . plugin . settings . padding = v . length ? Number ( v ) : defaultSettings . padding ;
2022-03-20 19:32:21 +01:00
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
2022-03-22 13:17:10 +01:00
for ( let frame of this . plugin . settings . frames ) {
let heading = this . containerEl . createEl ( "h3" , { text : frame.displayName || "Unnamed Frame" } ) ;
new Setting ( this . containerEl )
. setName ( "Display Name" )
. setDesc ( "The display name that this frame should have." )
. addText ( t = > {
t . setValue ( frame . displayName ) ;
t . onChange ( async v = > {
frame . displayName = v ;
heading . setText ( frame . displayName || "Unnamed Frame" ) ;
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
new Setting ( this . containerEl )
. setName ( "URL" )
. setDesc ( "The URL that should be opened in this frame." )
. addText ( t = > {
t . setValue ( frame . url ) ;
t . onChange ( async v = > {
frame . url = v ;
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
new Setting ( this . containerEl )
. setName ( "Minimum Width" )
. setDesc ( "The width that this frame's tab should be adjusted to automatically if it is lower. Set to 0 to disable." )
. addText ( t = > {
t . inputEl . type = "number" ;
t . setValue ( String ( frame . minimumWidth ) ) ;
t . onChange ( async v = > {
frame . minimumWidth = v . length ? Number ( v ) : 0 ;
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
new Setting ( this . containerEl )
. setName ( "Additional CSS" )
. setDesc ( "A snippet of additional CSS that should be applied to this frame." )
. addTextArea ( t = > {
2022-03-22 13:18:53 +01:00
t . inputEl . rows = 5 ;
2022-03-22 13:17:10 +01:00
t . inputEl . cols = 50 ;
t . setValue ( frame . customCss ) ;
t . onChange ( async v = > {
frame . customCss = v ;
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
new ButtonComponent ( this . containerEl )
. setButtonText ( "Remove Frame" )
. onClick ( async ( ) = > {
this . plugin . settings . frames . remove ( frame ) ;
2022-03-20 13:57:02 +01:00
await this . plugin . saveSettings ( ) ;
2022-03-22 13:17:10 +01:00
this . display ( ) ;
2022-03-20 13:57:02 +01:00
} ) ;
2022-03-22 13:17:10 +01:00
}
this . containerEl . createEl ( "hr" ) ;
2022-03-22 13:33:35 +01:00
this . containerEl . createEl ( "p" , { text : "Create a new frame, either from a preset shipped with the plugin, or a custom one that you can edit yourself. After restarting or reloading Obsidian, each frame's tab can be opened using the 'Custom Frames: Open' command." } ) ;
2022-03-22 13:17:10 +01:00
let addDiv = this . containerEl . createDiv ( ) ;
addDiv . addClass ( "custom-frames-add" ) ;
let dropdown = new DropdownComponent ( addDiv ) ;
dropdown . addOption ( "new" , "Custom" ) ;
for ( let key of Object . keys ( presets ) )
dropdown . addOption ( key , ` ${ presets [ key ] . displayName } Preset ` ) ;
new ButtonComponent ( addDiv )
. setButtonText ( "Add Frame" )
. onClick ( async ( ) = > {
let option = dropdown . getValue ( ) ;
if ( option == "new" ) {
this . plugin . settings . frames . push ( {
url : "" ,
displayName : "" ,
minimumWidth : 0 ,
customCss : ""
} ) ;
}
else {
this . plugin . settings . frames . push ( presets [ option ] ) ;
}
await this . plugin . saveSettings ( ) ;
this . display ( ) ;
2022-03-20 13:55:19 +01:00
} ) ;
}
2022-03-20 00:20:09 +01:00
}