├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.yml └── workflows │ └── release.yml ├── .gitignore ├── .npmrc ├── README.md ├── esbuild.config.mjs ├── main.ts ├── manifest.json ├── media ├── switcher-settings.png ├── usage.png ├── vault-off.png └── vault-on.png ├── package.json ├── styles.css ├── tsconfig.json ├── version-bump.mjs └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = tab 9 | indent_size = 4 10 | tab_width = 4 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | npm node_modules 2 | build -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "env": { "node": true }, 5 | "plugins": [ 6 | "@typescript-eslint" 7 | ], 8 | "extends": [ 9 | "eslint:recommended", 10 | "plugin:@typescript-eslint/eslint-recommended", 11 | "plugin:@typescript-eslint/recommended" 12 | ], 13 | "parserOptions": { 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off", 18 | "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], 19 | "@typescript-eslint/ban-ts-comment": "off", 20 | "no-prototype-builtins": "off", 21 | "@typescript-eslint/no-empty-function": "off" 22 | } 23 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [ "bug" ] 5 | body: 6 | - type: textarea 7 | id: bug-description 8 | attributes: 9 | label: Bug Description 10 | description: A clear and concise description of the bug. 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: screenshot 15 | attributes: 16 | label: Relevant Screenshot 17 | description: If applicable, add screenshots or a screen recording to help explain your problem. 18 | - type: textarea 19 | id: reproduction-steps 20 | attributes: 21 | label: To Reproduce 22 | description: Steps to reproduce the problem 23 | placeholder: | 24 | For example: 25 | 1. Go to '...' 26 | 2. Click on '...' 27 | 3. Scroll down to '...' 28 | - type: input 29 | id: obsi-version 30 | attributes: 31 | label: Obsidian Version 32 | description: You can find the version in the *About* Tab of the settings. 33 | placeholder: 0.13.19 34 | validations: 35 | required: true 36 | - type: checkboxes 37 | id: checklist 38 | attributes: 39 | label: Checklist 40 | options: 41 | - label: I updated to the latest version of the plugin. 42 | required: true 43 | 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea 3 | title: "Feature Request: " 4 | labels: [ "feature request" ] 5 | body: 6 | - type: textarea 7 | id: feature-requested 8 | attributes: 9 | label: Feature Requested 10 | description: A clear and concise description of the feature. 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: screenshot 15 | attributes: 16 | label: Relevant Screenshot 17 | description: If applicable, add screenshots or a screen recording to help explain the request. 18 | - type: checkboxes 19 | id: checklist 20 | attributes: 21 | label: Checklist 22 | options: 23 | - label: The feature would be useful to more users than just me. 24 | required: true 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Obsidian plugin 2 | 3 | on: 4 | release: 5 | types: [ created ] 6 | 7 | env: 8 | PLUGIN_NAME: obsidian-legacy-vault-switcher 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Use Node.js 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: 16 19 | - name: Build 20 | id: build 21 | run: | 22 | npm install 23 | npm run build 24 | mkdir ${{ env.PLUGIN_NAME }} 25 | cp main.js manifest.json styles.css ${{ env.PLUGIN_NAME }} 26 | zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }} 27 | ls 28 | echo "::set-output name=tag_name::$(git tag --sort version:refname | tail -n 1)" 29 | - name: Upload zip file 30 | id: upload-zip 31 | uses: actions/upload-release-asset@v1 32 | env: 33 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 34 | with: 35 | upload_url: ${{ github.event.release.upload_url }} 36 | asset_path: ./${{ env.PLUGIN_NAME }}.zip 37 | asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip 38 | asset_content_type: application/zip 39 | 40 | - name: Upload main.js 41 | id: upload-main 42 | uses: actions/upload-release-asset@v1 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | with: 46 | upload_url: ${{ github.event.release.upload_url }} 47 | asset_path: ./main.js 48 | asset_name: main.js 49 | asset_content_type: text/javascript 50 | 51 | - name: Upload manifest.json 52 | id: upload-manifest 53 | uses: actions/upload-release-asset@v1 54 | env: 55 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 56 | with: 57 | upload_url: ${{ github.event.release.upload_url }} 58 | asset_path: ./manifest.json 59 | asset_name: manifest.json 60 | asset_content_type: application/json 61 | 62 | - name: Upload styles.css 63 | id: upload-css 64 | uses: actions/upload-release-asset@v1 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | with: 68 | upload_url: ${{ github.event.release.upload_url }} 69 | asset_path: ./styles.css 70 | asset_name: styles.css 71 | asset_content_type: text/css 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Don't include the compiled main.js file in the repo. 12 | # They should be uploaded to GitHub releases instead. 13 | main.js 14 | 15 | # Exclude sourcemaps 16 | *.map 17 | 18 | # obsidian 19 | data.json 20 | 21 | # Exclude macOS Finder (System Explorer) View States 22 | .DS_Store 23 | 24 | 25 | pnpm-lock.yaml 26 | package-lock.json 27 | 28 | 29 | *.zip 30 | *.tar.gz 31 | *.tgz 32 | *.rar 33 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Legacy vault switcher 2 | 3 | An [Obsidian](https://obsidian.md/) plugin that brings back the pre-1.6.0 vault switcher, help, and settings buttons. 4 | 5 | ![img.png](./media/usage.png) 6 | 7 | ![legacy-vault-on.png](./media/vault-on.png) 8 | ![legacy-vault-off.png](./media/vault-off.png) 9 | 10 | ![legacy-vault-settings.png](./media/switcher-settings.png) 11 | 12 | # Installation 13 | 14 | ### BRAT 15 | 16 | [BRAT](https://github.com/TfTHacker/obsidian42-brat) (Beta Reviewer's Auto-update Tool) is a plugin that allows users to 17 | install Obsidian plugins directly from GitHub with automatic updates. 18 | 19 | via Commands: 20 | 21 | 1. Ensure BRAT is installed 22 | 2. Enter the command `BRAT: Plugins: Add a beta plugin for testing` 23 | 3. Enter `Quorafind/Obsidian-Legacy-Vault-Switcher` 24 | 4. Click on Add Plugin 25 | 26 | via Settings: 27 | 28 | 1. Ensure BRAT is installed 29 | 2. Go to *Settings > BRAT > Beta Plugin List* 30 | 3. Click on Add Beta plugin 31 | 4. Enter `Quorafind/Obsidian-Legacy-Vault-Switcher` 32 | 5. Click on Add Plugin 33 | 34 | ### Manual 35 | 36 | Option 1: 37 | 38 | 1. Go to [Releases](https://github.com/Quorafind/Obsidian-Legacy-Vault-Switcher/releases) 39 | 2. Download the latest `Obsidian-Legacy-Vault-Switcher-${version}.rar` 40 | 3. Extract its contents 41 | 4. Move the contents into /your-vault/.obsidian/plugins/obsidian-legacy-vault-switcher/ 42 | 5. Go to *Settings > Community plugins* 43 | 6. Enable Legacy vault switcher 44 | 45 | Option 2: 46 | 47 | 1. Go to [Releases](https://github.com/Quorafind/Obsidian-Legacy-Vault-Switcher/releases) 48 | 2. Download the latest `main.js` and `manifest.json` 49 | 3. Move the files into /your-vault/.obsidian/plugins/obsidian-legacy-vault-switcher/ 50 | 5. Go to *Settings > Community plugins* 51 | 6. Enable Legacy vault switcher 52 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from 'builtin-modules' 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === 'production'); 13 | 14 | esbuild.build({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ['main.ts'], 19 | bundle: true, 20 | external: [ 21 | 'obsidian', 22 | 'electron', 23 | '@codemirror/autocomplete', 24 | '@codemirror/collab', 25 | '@codemirror/commands', 26 | '@codemirror/language', 27 | '@codemirror/lint', 28 | '@codemirror/search', 29 | '@codemirror/state', 30 | '@codemirror/view', 31 | '@lezer/common', 32 | '@lezer/highlight', 33 | '@lezer/lr', 34 | ...builtins], 35 | format: 'cjs', 36 | watch: !prod, 37 | target: 'es2018', 38 | logLevel: "info", 39 | sourcemap: prod ? false : 'inline', 40 | treeShaking: true, 41 | outfile: 'main.js', 42 | }).catch(() => process.exit(1)); 43 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Plugin, setIcon, PluginSettingTab, Setting, App } from 'obsidian'; 2 | 3 | declare module 'Obsidian' { 4 | interface WorkspaceRibbon { 5 | ribbonSettingEl: HTMLElement; 6 | 7 | makeRibbonItemButton(icon: string, tooltip: string, onClick: (e: MouseEvent) => void): HTMLElement; 8 | } 9 | 10 | interface App { 11 | setting: { 12 | open: () => void; 13 | }; 14 | 15 | openVaultChooser(): void; 16 | 17 | openHelp(): void; 18 | } 19 | } 20 | 21 | interface MyPluginSettings { 22 | restoreVaultSwitcher: boolean; 23 | restoreVaultActionsHelp: boolean; 24 | restoreVaultActionsSettings: boolean; 25 | } 26 | 27 | const DEFAULT_SETTINGS: MyPluginSettings = { 28 | restoreVaultSwitcher: true, 29 | restoreVaultActionsHelp: true, 30 | restoreVaultActionsSettings: true, 31 | }; 32 | 33 | export default class MyPlugin extends Plugin { 34 | ribbonMap: Map = new Map(); 35 | settings: MyPluginSettings; 36 | styleElements: Record = {}; 37 | 38 | async onload() { 39 | await this.loadSettings(); 40 | this.addSettingTab(new MyPluginSettingTab(this.app, this)); 41 | this.initVaultSwitcher(); 42 | this.updateRibbonButtons(); 43 | } 44 | 45 | onunload() { 46 | this.ribbonMap.forEach((value, key) => { 47 | value.detach(); 48 | }); 49 | this.ribbonMap.clear(); 50 | Object.values(this.styleElements).forEach(el => el.remove()); 51 | } 52 | 53 | async loadSettings() { 54 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 55 | } 56 | 57 | async saveSettings() { 58 | await this.saveData(this.settings); 59 | } 60 | 61 | initVaultSwitcher() { 62 | this.createRibbonButton('vault', 'Switch vault', 'vault', () => this.app.openVaultChooser()); 63 | this.createRibbonButton('help', 'Help', 'help', () => this.app.openHelp()); 64 | this.createRibbonButton('settings', 'Settings', 'lucide-settings', () => this.app.setting.open()); 65 | } 66 | 67 | createRibbonButton(id: string, tooltip: string, icon: string, onClick: () => void) { 68 | const leftRibbon = this.app.workspace.leftRibbon; 69 | const button = leftRibbon.makeRibbonItemButton(icon, tooltip, (e) => { 70 | e.stopPropagation(); 71 | onClick(); 72 | }); 73 | 74 | this.ribbonMap.set(id, button); 75 | leftRibbon.ribbonSettingEl.appendChild(button); 76 | } 77 | 78 | toggleRibbonItem(id: string, show: boolean) { 79 | const item = this.ribbonMap.get(id); 80 | if (item) { 81 | if (show) { 82 | this.app.workspace.leftRibbon.ribbonSettingEl.appendChild(item); 83 | } else { 84 | item.detach(); 85 | } 86 | } 87 | } 88 | 89 | updateRibbonButtons() { 90 | this.toggleRibbonItem('vault', this.settings.restoreVaultSwitcher); 91 | this.toggleRibbonItem('help', this.settings.restoreVaultActionsHelp); 92 | this.toggleRibbonItem('settings', this.settings.restoreVaultActionsSettings); 93 | this.applyStyleSettings(); 94 | } 95 | 96 | applyStyleSettings() { 97 | this.updateStyle('vault-profile', ` 98 | body:not(.is-mobile) .workspace-split.mod-left-split .workspace-sidedock-vault-profile { 99 | display: ${this.settings.restoreVaultSwitcher && this.settings.restoreVaultActionsHelp && this.settings.restoreVaultActionsSettings ? 'none' : 'flex'}; 100 | } 101 | `); 102 | this.updateStyle('vault-switcher', ` 103 | body:not(.is-mobile) .workspace-split.mod-left-split .workspace-sidedock-vault-profile .workspace-drawer-vault-switcher { 104 | display: ${this.settings.restoreVaultSwitcher ? 'none' : 'flex'}; 105 | } 106 | `); 107 | this.updateStyle('vault-actions-help', ` 108 | body:not(.is-mobile) .workspace-split.mod-left-split .workspace-sidedock-vault-profile .workspace-drawer-vault-actions .clickable-icon:has(svg.svg-icon.help) { 109 | display: ${this.settings.restoreVaultActionsHelp ? 'none' : 'flex'}; 110 | } 111 | `); 112 | this.updateStyle('vault-actions-settings', ` 113 | body:not(.is-mobile) .workspace-split.mod-left-split .workspace-sidedock-vault-profile .workspace-drawer-vault-actions .clickable-icon:has(svg.svg-icon.lucide-settings) { 114 | display: ${this.settings.restoreVaultActionsSettings ? 'none' : 'flex'}; 115 | } 116 | `); 117 | } 118 | 119 | updateStyle(id: string, css: string) { 120 | let styleEl = this.styleElements[id]; 121 | if (!styleEl) { 122 | styleEl = document.createElement('style'); 123 | styleEl.id = id; 124 | document.head.appendChild(styleEl); 125 | this.styleElements[id] = styleEl; 126 | } 127 | styleEl.textContent = css; 128 | } 129 | } 130 | 131 | class MyPluginSettingTab extends PluginSettingTab { 132 | plugin: MyPlugin; 133 | 134 | constructor(app: App, plugin: MyPlugin) { 135 | super(app, plugin); 136 | this.plugin = plugin; 137 | } 138 | 139 | display() { 140 | const {containerEl} = this; 141 | containerEl.empty(); 142 | containerEl.createEl('h3', {text: 'Ribbon Button Settings'}); 143 | 144 | this.addToggle('Vault Switcher', 'restoreVaultSwitcher', 'Show or hide the vault switcher button'); 145 | this.addToggle('Help', 'restoreVaultActionsHelp', 'Show or hide the help button'); 146 | this.addToggle('Settings', 'restoreVaultActionsSettings', 'Show or hide the settings button'); 147 | } 148 | 149 | addToggle(name: string, settingKey: keyof MyPluginSettings, description: string) { 150 | new Setting(this.containerEl) 151 | .setName(name) 152 | .setDesc(description) 153 | .addToggle(toggle => 154 | toggle 155 | .setValue(this.plugin.settings[settingKey]) 156 | .onChange(async (value) => { 157 | this.plugin.settings[settingKey] = value; 158 | await this.plugin.saveSettings(); 159 | this.plugin.updateRibbonButtons(); 160 | }) 161 | ); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "legacy-vault-switcher", 3 | "name": "Legacy vault switcher", 4 | "version": "1.0.0", 5 | "minAppVersion": "1.6.0", 6 | "description": "For switching back to the legacy vault switcher.", 7 | "author": "Boninall", 8 | "authorUrl": "https://github.com/Quorafind", 9 | "fundingUrl": { 10 | "Buy Me a Coffee": "https://www.buymeacoffee.com/boninall", 11 | "爱发电": "https://afdian.net/a/boninall", 12 | "支付宝": "https://cdn.jsdelivr.net/gh/Quorafind/.github@main/IMAGE/%E6%94%AF%E4%BB%98%E5%AE%9D%E4%BB%98%E6%AC%BE%E7%A0%81.jpg" 13 | }, 14 | "isDesktopOnly": false 15 | } -------------------------------------------------------------------------------- /media/switcher-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quorafind/Obsidian-Legacy-Vault-Switcher/ca5aaffd3aa13f834499a7c9b0a815e27a29031a/media/switcher-settings.png -------------------------------------------------------------------------------- /media/usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quorafind/Obsidian-Legacy-Vault-Switcher/ca5aaffd3aa13f834499a7c9b0a815e27a29031a/media/usage.png -------------------------------------------------------------------------------- /media/vault-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quorafind/Obsidian-Legacy-Vault-Switcher/ca5aaffd3aa13f834499a7c9b0a815e27a29031a/media/vault-off.png -------------------------------------------------------------------------------- /media/vault-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quorafind/Obsidian-Legacy-Vault-Switcher/ca5aaffd3aa13f834499a7c9b0a815e27a29031a/media/vault-on.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "legacy-vault-switcher", 3 | "version": "1.0.0", 4 | "description": "Brings back the pre-1.6.0 vault switcher, help, and settings buttons.", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/node": "^16.11.6", 16 | "@typescript-eslint/eslint-plugin": "5.29.0", 17 | "@typescript-eslint/parser": "5.29.0", 18 | "builtin-modules": "3.3.0", 19 | "esbuild": "0.14.47", 20 | "obsidian": "latest", 21 | "tslib": "2.4.0", 22 | "typescript": "4.7.4" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | /* body:not(.is-mobile) .workspace-split.mod-left-split .workspace-sidedock-vault-profile { 2 | display: none; 3 | } */ -------------------------------------------------------------------------------- /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 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7" 19 | ] 20 | }, 21 | "include": [ 22 | "**/*.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /version-bump.mjs: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from "fs"; 2 | 3 | const targetVersion = process.env.npm_package_version; 4 | 5 | // read minAppVersion from manifest.json and bump version to target version 6 | let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); 7 | const { minAppVersion } = manifest; 8 | manifest.version = targetVersion; 9 | writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); 10 | 11 | // update versions.json with target version and minAppVersion from manifest.json 12 | let versions = JSON.parse(readFileSync("versions.json", "utf8")); 13 | versions[targetVersion] = minAppVersion; 14 | writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); 15 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.0.1": "1.6.0", 3 | "0.0.2": "1.6.0", 4 | "1.0.0": "1.6.0" 5 | } --------------------------------------------------------------------------------