├── .gitignore ├── README.md ├── demo.gif ├── manifest.json ├── package.json ├── rollup.config.js ├── src └── main.ts ├── styles.css ├── tsconfig.json └── versions.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | *.iml 3 | .idea 4 | 5 | # npm 6 | node_modules 7 | package-lock.json 8 | 9 | # build 10 | main.js 11 | *.js.map 12 | 13 | # obsidian 14 | data.json 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Obsidian Sidebar Expand on Hover Plugin 2 | 3 | ### Features 4 | 5 | - Expand sidebars on mouse hovering on the corresponding ribbons. 6 | - Pin/unpin sidebar state(expand/collapse) by double clicking on the ribbons. 7 | - Set sidebar width either by dragging or setting the value in plugin settings. 8 | - Keep previous state of sidebars on relaunch. 9 | 10 | ### Demo: 11 | 12 | ![Plugin in Action: Auto expand of sidebar when hovering on ribbon](./demo.gif) 13 | 14 | ### TODO: 15 | 16 | - [ ] Option to toggle sidebar animations. 17 | - [ ] Change animation style. 18 | 19 | ### How to Install (Manual): 20 | 21 | - If Obsidian is running then close it. 22 | - Locate your Obsidian vault. (It's where you keep your notes) 23 | - Run the following commands in terminal: 24 | ```bash 25 | $ cd /.obsidian/plugins/ 26 | $ git clone https://github.com/toiq/obsidian-sidebar-expand-on-hover 27 | ``` 28 | - Now launch Obsidian and go to `settings > Community Plugins > Installed Plugins` and enable this plugin. (Make sure you disabled 'Safe Mode') 29 | 30 | ### How to Uninstall: 31 | 32 | - go to `settings > Community Plugins > Installed Plugins` and click the X besides this plugin's name. 33 | - Restart Obsidian. 34 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/toiq/obsidian-sidebar-expand-on-hover/2375470e59e9dc5fa93525ebe56ba7123a2b225e/demo.gif -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-sidebar-expand-on-hover", 3 | "name": "Sidebar Expand on Hover", 4 | "version": "1.0.1", 5 | "minAppVersion": "0.12.3", 6 | "description": "This Obsidian plugin expands or collapses the sidebars based on mouse hovering on the ribbons.", 7 | "authorUrl": "https://github.com/toiq", 8 | "isDesktopOnly": true 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sidebar-expand-on-hover-plugin", 3 | "version": "1.0.0", 4 | "description": "This Obsidian plugin expands or collapses the sidebars based on mouse hovering on the ribbons.", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "rollup --config rollup.config.js -w", 8 | "build": "rollup --config rollup.config.js --environment BUILD:production" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@rollup/plugin-commonjs": "^18.0.0", 15 | "@rollup/plugin-node-resolve": "^11.2.1", 16 | "@rollup/plugin-typescript": "^8.2.1", 17 | "@types/node": "^14.14.37", 18 | "obsidian": "^0.12.0", 19 | "rollup": "^2.32.1", 20 | "tslib": "^2.2.0", 21 | "typescript": "^4.2.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from '@rollup/plugin-typescript'; 2 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | 5 | const isProd = process.env.BUILD === 'production'; 6 | 7 | const banner = `/* 8 | THIS IS A GENERATED/BUNDLED FILE BY ROLLUP 9 | if you want to view the source visit the plugins github repository 10 | */ 11 | `; 12 | 13 | export default { 14 | input: './src/main.ts', 15 | output: { 16 | dir: '.', 17 | sourcemap: 'inline', 18 | sourcemapExcludeSources: isProd, 19 | format: 'cjs', 20 | exports: 'default', 21 | banner, 22 | }, 23 | external: ['obsidian'], 24 | plugins: [typescript(), nodeResolve({ browser: true }), commonjs()], 25 | }; 26 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { App, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; 2 | interface SidebarExpandOnHoverSettings { 3 | leftSidebarWidth: number; 4 | rightSidebarWidth: number; 5 | leftPin: boolean; 6 | rightPin: boolean; 7 | leftSideEnabled: boolean; 8 | rightSideEnabled: boolean; 9 | } 10 | 11 | const DEFAULT_SETTINGS: SidebarExpandOnHoverSettings = { 12 | leftSidebarWidth: 252, 13 | rightSidebarWidth: 252, 14 | leftPin: false, 15 | rightPin: false, 16 | leftSideEnabled: true, 17 | rightSideEnabled: true, 18 | }; 19 | 20 | export default class SidebarExpandOnHoverPlugin extends Plugin { 21 | settings: SidebarExpandOnHoverSettings; 22 | leftRibbon: HTMLElement; 23 | rightRibbon: HTMLElement; 24 | leftSidebar: HTMLElement; 25 | rightSidebar: HTMLElement; 26 | 27 | async onload() { 28 | // Initialize and set events when layout is fully ready 29 | this.app.workspace.onLayoutReady(() => { 30 | this.loadSettings().then(() => { 31 | this.initialize(); 32 | this.setEvents(); 33 | this.addSettingTab(new SidebarExpandOnHoverSettingTab(this.app, this)); 34 | // This timeout is needed to override Obsidian sidebar state at launch 35 | setTimeout(() => { 36 | if (this.settings.leftPin) { 37 | this.expandSidebar(this.leftSidebar); 38 | } else { 39 | this.collapseSidebar(this.leftSidebar); 40 | } 41 | if (this.settings.rightPin) { 42 | this.expandSidebar(this.rightSidebar); 43 | } else { 44 | this.collapseSidebar(this.rightSidebar); 45 | } 46 | }, 200); 47 | }); 48 | }); 49 | 50 | this.addCommand({ 51 | id: 'Toggle-Left-Sidebar-Expand-On-Hover', 52 | name: 'Toggle Left Sidebar Behavior', 53 | callback: () => { 54 | this.settings.leftSideEnabled = !this.settings.leftSideEnabled; 55 | if (this.settings.leftSideEnabled == false) 56 | this.settings.leftPin = false; 57 | this.saveSettings(); 58 | if (this.settings.leftSideEnabled) { 59 | new Notice('Left Sidebar Expand on Hover Enabled'); 60 | } else { 61 | new Notice('Left Sidebar Expand on Hover disabled'); 62 | } 63 | }, 64 | }); 65 | 66 | this.addCommand({ 67 | id: 'Toggle-Right-Sidebar-Expand-On-Hover', 68 | name: 'Toggle Right Sidebar Behavior', 69 | callback: () => { 70 | this.settings.rightSideEnabled = !this.settings.rightSideEnabled; 71 | if (this.settings.rightSideEnabled == false) 72 | this.settings.rightPin = false; 73 | this.saveSettings(); 74 | if (this.settings.rightSideEnabled) { 75 | new Notice('Right Sidebar Expand on Hover Enabled'); 76 | } else { 77 | new Notice('Right Sidebar Expand on Hover disabled'); 78 | } 79 | }, 80 | }); 81 | } 82 | 83 | // Initializes the variables to store DOM HTML elements 84 | initialize: Function = () => { 85 | this.leftRibbon = (this.app.workspace.leftRibbon as any).containerEl; 86 | this.rightRibbon = (this.app.workspace.rightRibbon as any).containerEl; 87 | this.leftSidebar = ((this.app.workspace 88 | .leftSplit as unknown) as any).containerEl; 89 | this.rightSidebar = ((this.app.workspace 90 | .rightSplit as unknown) as any).containerEl; 91 | }; 92 | 93 | // Adds event listeners to the HTML elements 94 | setEvents: Function = () => { 95 | this.registerDomEvent(document, 'mouseleave', () => { 96 | this.collapseSidebar(this.leftSidebar); 97 | this.collapseSidebar(this.rightSidebar); 98 | }); 99 | 100 | this.registerDomEvent( 101 | (this.app.workspace.rootSplit as any).containerEl, 102 | 'mouseenter', 103 | () => { 104 | this.collapseSidebar(this.leftSidebar); 105 | this.collapseSidebar(this.rightSidebar); 106 | } 107 | ); 108 | 109 | this.registerDomEvent(this.leftRibbon, 'mouseenter', () => { 110 | if (!this.settings.leftPin) { 111 | this.expandSidebar(this.leftSidebar); 112 | } 113 | }); 114 | 115 | this.registerDomEvent(this.rightRibbon, 'mouseenter', () => { 116 | if (!this.settings.rightPin) { 117 | this.expandSidebar(this.rightSidebar); 118 | } 119 | }); 120 | 121 | // To avoid 'glitch' 122 | this.registerDomEvent( 123 | (this.app.workspace.leftSplit as any).resizeHandleEl, 124 | 'mouseenter', 125 | () => { 126 | if (!this.settings.leftPin) { 127 | this.expandSidebar(this.leftSidebar); 128 | } 129 | this.settings.leftSidebarWidth = Number( 130 | (this.app.workspace.leftSplit as any).size 131 | ); 132 | this.saveSettings(); 133 | } 134 | ); 135 | this.registerDomEvent( 136 | (this.app.workspace.rightSplit as any).resizeHandleEl, 137 | 'mouseenter', 138 | () => { 139 | if (!this.settings.rightPin) { 140 | this.expandSidebar(this.rightSidebar); 141 | } 142 | this.settings.rightSidebarWidth = Number( 143 | (this.app.workspace.rightSplit as any).size 144 | ); 145 | this.saveSettings(); 146 | } 147 | ); 148 | 149 | // Double click on left ribbon to toggle pin/unpin of left sidebar 150 | this.registerDomEvent(this.leftRibbon, 'dblclick', () => { 151 | if (this.settings.leftSideEnabled) { 152 | this.settings.leftPin = !this.settings.leftPin; 153 | this.saveSettings(); 154 | } 155 | }); 156 | 157 | // Double click on right ribbon to toggle pin/unpin of right sidebar 158 | this.registerDomEvent(this.rightRibbon, 'dblclick', () => { 159 | if (this.settings.rightSideEnabled) { 160 | this.settings.rightPin = !this.settings.rightPin; 161 | this.saveSettings(); 162 | } 163 | }); 164 | }; 165 | 166 | // Changes sidebar style width and display to expand it 167 | expandSidebar = (sidebar: HTMLElement) => { 168 | if (sidebar == this.leftSidebar && this.settings.leftSideEnabled) { 169 | (this.app.workspace.leftSplit as any).setSize( 170 | this.settings.leftSidebarWidth 171 | ); 172 | (this.app.workspace.leftSplit as any).expand(); 173 | } 174 | if (sidebar == this.rightSidebar && this.settings.rightSideEnabled) { 175 | (this.app.workspace.rightSplit as any).setSize( 176 | this.settings.rightSidebarWidth 177 | ); 178 | (this.app.workspace.rightSplit as any).expand(); 179 | } 180 | }; 181 | 182 | // Changes sidebar style width to collapse it 183 | collapseSidebar = (sidebar: HTMLElement) => { 184 | if ( 185 | sidebar == this.leftSidebar && 186 | !this.settings.leftPin && 187 | this.settings.leftSideEnabled 188 | ) { 189 | (this.app.workspace.leftSplit as any).collapse(); 190 | } 191 | if ( 192 | sidebar == this.rightSidebar && 193 | !this.settings.rightPin && 194 | this.settings.rightSideEnabled 195 | ) { 196 | (this.app.workspace.rightSplit as any).collapse(); 197 | } 198 | }; 199 | 200 | onunload() { 201 | this.saveSettings(); 202 | } 203 | 204 | async loadSettings() { 205 | this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData()); 206 | } 207 | 208 | async saveSettings() { 209 | await this.saveData(this.settings); 210 | } 211 | } 212 | 213 | // Plugin settings 214 | class SidebarExpandOnHoverSettingTab extends PluginSettingTab { 215 | plugin: SidebarExpandOnHoverPlugin; 216 | 217 | constructor(app: App, plugin: SidebarExpandOnHoverPlugin) { 218 | super(app, plugin); 219 | this.plugin = plugin; 220 | } 221 | 222 | display(): void { 223 | const { containerEl } = this; 224 | 225 | containerEl.empty(); 226 | 227 | this.plugin.loadData(); 228 | containerEl.createEl('h2', { text: 'Sidebar Expand On Hover' }); 229 | containerEl.createEl('p', { 230 | text: `Note: You can also double click on each of the ribbons to 'pin' the corresponding 231 | sidebar so that it remains expanded. 232 | You can undo this 'pinned state' behavior by double clicking on the ribbons again. 233 | This only works when you have that sidebar 'enabled' in this settings. Enjoy! :D`, 234 | }); 235 | 236 | containerEl.createEl('h4', { text: 'Enable Individual Sidebar' }); 237 | const leftSideEnabled = new Setting(containerEl); 238 | leftSideEnabled.setName('Left Sidebar'); 239 | leftSideEnabled.setDesc( 240 | 'Toggle to enable/disable left sidebar expand on hover' 241 | ); 242 | leftSideEnabled.addToggle((t) => { 243 | t.setValue(this.plugin.settings.leftSideEnabled); 244 | t.onChange(async (v) => { 245 | this.plugin.settings.leftSideEnabled = v; 246 | if (v == false) this.plugin.settings.leftPin = false; 247 | this.plugin.saveSettings(); 248 | }); 249 | }); 250 | 251 | const rightSideEnabled = new Setting(containerEl); 252 | rightSideEnabled.setName('Right Sidebar'); 253 | rightSideEnabled.setDesc( 254 | 'Toggle to enable/disable right sidebar expand on hover' 255 | ); 256 | rightSideEnabled.addToggle((t) => { 257 | t.setValue(this.plugin.settings.rightSideEnabled); 258 | t.onChange(async (v) => { 259 | this.plugin.settings.rightSideEnabled = v; 260 | if (v == false) this.plugin.settings.rightPin = false; 261 | this.plugin.saveSettings(); 262 | }); 263 | }); 264 | 265 | containerEl.createEl('h4', { text: 'Sidebar Expand Width' }); 266 | const leftSidebarWidth = new Setting(containerEl); 267 | leftSidebarWidth.setName('Left Sidebar'); 268 | leftSidebarWidth.setDesc('Set the width of left sidebar in pixel unit'); 269 | leftSidebarWidth.addText((t) => { 270 | t.setValue(String(this.plugin.settings.leftSidebarWidth)); 271 | t.setPlaceholder('Default: 252').onChange(async (value) => { 272 | this.plugin.settings.leftSidebarWidth = Number(value); 273 | (this.app.workspace.leftSplit as any).setSize( 274 | this.plugin.settings.leftSidebarWidth 275 | ); 276 | this.plugin.saveSettings(); 277 | }); 278 | }); 279 | 280 | const rightSidebarWidth = new Setting(containerEl); 281 | rightSidebarWidth.setName('Right Sidebar'); 282 | rightSidebarWidth.setDesc('Set the width of right sidebar in pixel unit'); 283 | rightSidebarWidth.addText((t) => { 284 | t.setValue(String(this.plugin.settings.rightSidebarWidth)); 285 | t.setPlaceholder('Default: 252').onChange(async (value) => { 286 | this.plugin.settings.rightSidebarWidth = Number(value); 287 | (this.app.workspace.rightSplit as any).setSize( 288 | this.plugin.settings.rightSidebarWidth 289 | ); 290 | this.plugin.saveSettings(); 291 | }); 292 | }); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .cm-hmd-list-indent .cm-tab, 2 | ul ul { 3 | position: relative; 4 | } 5 | .cm-hmd-list-indent .cm-tab::before, 6 | ul ul::before { 7 | content: ''; 8 | border-left: 1px solid rgba(0, 122, 255, 0.25); 9 | position: absolute; 10 | } 11 | .cm-hmd-list-indent .cm-tab::before { 12 | left: 0; 13 | top: -5px; 14 | bottom: -4px; 15 | } 16 | ul ul::before { 17 | left: -11px; 18 | top: 0; 19 | bottom: 0; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "es6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "lib": ["dom", "es5", "scripthost", "es2015", "DOM.Iterable"] 13 | }, 14 | "include": ["**/*.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.0.1": "0.12.3" 3 | } 4 | --------------------------------------------------------------------------------