From ed3c918bd4aea91f2ae184c4508356d72334b336 Mon Sep 17 00:00:00 2001 From: hacker-c <1507559148@qq.com> Date: Sun, 30 Mar 2025 12:08:10 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20resolve=20cross-platform?= =?UTF-8?q?=20mobile=20issues=20and=20remove=20redundant=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.ts | 271 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 152 insertions(+), 119 deletions(-) diff --git a/main.ts b/main.ts index f7d1799..b1f4847 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,6 @@ -import { Notice, Plugin, addIcon } from 'obsidian' -import * as path from 'path' -import * as fs from 'fs' +import { Notice, Plugin, addIcon, TFile, Vault } from 'obsidian' +// open github icon const customIcon = ` @@ -18,179 +17,213 @@ const CONSTS = { TITLE: 'Open in GitHub' } -interface OpenInGitHubPluginSettings { - mySetting: string -} - -const DEFAULT_SETTINGS: OpenInGitHubPluginSettings = { - mySetting: 'default' -} +// Define constants for git paths relative to vault root +const GIT_DIR = '.git' +const GIT_CONFIG_PATH = `${GIT_DIR}/config` +const GIT_HEAD_PATH = `${GIT_DIR}/HEAD` export default class OpenInGitHubPlugin extends Plugin { - settings: OpenInGitHubPluginSettings + vaultAdapter: Vault['adapter'] async onload() { - await this.loadSettings() + // Use vault adapter + this.vaultAdapter = this.app.vault.adapter + + // Check if adapter exists (basic check for environment support) + if (!this.vaultAdapter) { + console.error("OpenInGitHubPlugin: Vault adapter is not available. Plugin may not work correctly.") + new Notice("OpenInGitHubPlugin: Vault adapter not found. Some features might be limited.") + } // add icon addIcon(CONSTS.TITLE_ID, customIcon) // left open-in-github button - this.addRibbonIcon(CONSTS.TITLE_ID, CONSTS.TITLE, this.openGitHub) + this.addRibbonIcon(CONSTS.TITLE_ID, CONSTS.TITLE, (evt) => this.openGitHub(evt)) // Pass event directly // add file menu this.registerEvent( this.app.workspace.on('file-menu', (menu, file) => { - menu.addItem((item) => { - item - .setTitle(CONSTS.TITLE) - .setIcon(CONSTS.TITLE_ID) - .onClick((e) => this.openGitHub(e as MouseEvent, true)); - }); + // Only add if the file is potentially in a git repo + if (file instanceof TFile) { + menu.addItem((item) => { + item + .setTitle(CONSTS.TITLE) + .setIcon(CONSTS.TITLE_ID) + .onClick((e) => this.openGitHub(e as MouseEvent, file)) // Pass file context + }) + } }) - ); + ) - // add editormenu + // add editor menu this.registerEvent( this.app.workspace.on("editor-menu", (menu, editor, view) => { - menu.addItem((item) => { - item - .setTitle(CONSTS.TITLE) - .setIcon(CONSTS.TITLE_ID) - .onClick((e) => this.openGitHub(e as MouseEvent, true)); - }); + // Only add if the file is potentially in a git repo + if (view?.file) { + menu.addItem((item) => { + item + .setTitle(CONSTS.TITLE) + .setIcon(CONSTS.TITLE_ID) + .onClick((e) => this.openGitHub(e as MouseEvent, view.file as TFile)) // Pass file context + }) + } }) - ); - } - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) - } - - async saveSettings() { - await this.saveData(this.settings) + ) } /** * open github in browser - * @param evt - * @param openFile + * @param evt The mouse event that triggered the action + * @param file The file context (optional), if triggered from file/editor menu */ - openGitHub = async (evt: MouseEvent, openFile?: boolean) => { + openGitHub = async (evt: MouseEvent, file?: TFile) => { try { + // Determine the file path to use + const targetFile = file // this.app.workspace.getActiveFile() + const openFileInRepo = !!targetFile // True if we have a file context + const repoUrl = await this.getGitHubRepoUrl() - if (repoUrl) { - const fileRelativePath = this.app.workspace.getActiveFile() - if (openFile) { - if (fileRelativePath?.path) { - let branch = await this.getCurrentBranch(fileRelativePath?.path) - if (!branch) { - branch = 'main' - } - const fileUrl = `${repoUrl}/blob/${branch}/${fileRelativePath?.path}` - window.open(fileUrl, '_blank') - } else { - new Notice('Not found file relative path!') - } - } else { - window.open(repoUrl, '_blank') - } - } else { - new Notice('Could not determine GitHub repository URL.') + if (!repoUrl) { + // Notice is shown within getGitHubRepoUrl if needed + return } + + if (openFileInRepo && targetFile) { + // Use targetFile.path which is relative to the vault root + const filePathRelative = targetFile.path + let branch = await this.getCurrentBranch() + if (!branch) { + // Default to 'main' if branch detection fails + branch = 'main' + new Notice("Could not detect current branch, defaulting to 'main'.") + } + const fileUrl = `${repoUrl}/blob/${branch}/${filePathRelative}` + window.open(fileUrl, '_blank') + + } else if (!openFileInRepo) { + // If no file context (e.g., ribbon click), open the repo root + window.open(repoUrl, '_blank') + } else { + // Case where we intended to open a file, but targetFile is null/undefined + new Notice('No active file selected to open in GitHub.') + } + } catch (err) { new Notice('Failed to open GitHub repository.') - console.error(err) + console.error("OpenInGitHubPlugin Error:", err) } } /** - * Get the GitHub repository URL for the current vault. + * Get the GitHub repository URL for the current vault using Vault adapter. */ async getGitHubRepoUrl(): Promise { - const vaultPath = this.getVaultBasePath() - if (!vaultPath) { - new Notice('This feature is only supported in the desktop app.') + if (!this.vaultAdapter) { + new Notice('Vault adapter not available. Cannot access file system.') return null } - const gitDir = path.join(vaultPath, '.git') - if (!fs.existsSync(gitDir)) { - new Notice('No .git directory found in the vault.') + try { + // Check if .git directory exists at the vault root + const gitDirExists = await this.vaultAdapter.exists(GIT_DIR) + if (!gitDirExists) { + new Notice('No .git directory found in the vault root.') + return null + } + + // Check if .git/config file exists + const configPathExists = await this.vaultAdapter.exists(GIT_CONFIG_PATH) + if (!configPathExists) { + new Notice('No .git/config file found.') + return null + } + + // Read the config file content + const configContent = await this.vaultAdapter.read(GIT_CONFIG_PATH) + + // Find the remote "origin" URL + const remoteMatch = configContent.match(/\[remote\s+"origin"\][^[]*?\n\s*url\s*=\s*(.*)/) + if (!remoteMatch || !remoteMatch[1]) { + new Notice('No remote "origin" URL found in .git/config.') + return null + } + + const remoteUrl = remoteMatch[1].trim() + const githubUrl = this.convertToGitHubUrl(remoteUrl) + + if (!githubUrl) { + new Notice('Could not parse GitHub URL from remote "origin".') + return null + } + + return githubUrl + + } catch (error) { + console.error("OpenInGitHubPlugin: Error accessing git config:", error) + new Notice("Error accessing git configuration.") return null } - - const configPath = path.join(gitDir, 'config') - if (!fs.existsSync(configPath)) { - new Notice('No .git/config file found.') - return null - } - - const configContent = fs.readFileSync(configPath, 'utf-8') - const remoteMatch = configContent.match(/\[remote "origin"\][\s\S]*?url\s*=\s*(.*)/) - if (!remoteMatch) { - new Notice('No remote "origin" found in .git/config.') - return null - } - - const remoteUrl = remoteMatch[1].trim() - return this.convertToGitHubUrl(remoteUrl) } /** - * Convert a Git remote URL to a GitHub URL. + * Convert a Git remote URL to a GitHub web URL. */ convertToGitHubUrl(remoteUrl: string): string | null { // Handle HTTPS URLs (e.g., https://github.com/user/repo.git) - if (remoteUrl.startsWith('https://')) { - return remoteUrl.replace(/\.git$/, '') + let match = remoteUrl.match(/^https?:\/\/github\.com\/([^/]+\/[^/]+?)(?:\.git)?$/) + if (match) { + return `https://github.com/${match[1]}` } // Handle SSH URLs (e.g., git@github.com:user/repo.git) - if (remoteUrl.startsWith('git@')) { - return remoteUrl.replace(':', '/').replace('git@', 'https://').replace(/\.git$/, '') + match = remoteUrl.match(/^git@github\.com:([^/]+\/[^/]+?)(?:\.git)?$/) + if (match) { + return `https://github.com/${match[1]}` } - return null + return null // Return null if format is not recognized } /** - * Get the base path of the vault (only works in desktop app). + * Get the current branch of the Git repository using Vault adapter. */ - getVaultBasePath(): string | null { - // @ts-ignore - if (this.app.vault.adapter?.getBasePath) { - // @ts-ignore - return this.app.vault.adapter.getBasePath() - } - return null - } - - /** - * Get the current branch of the Git repository. - */ - async getCurrentBranch(filePath: string): Promise { - const vaultPath = this.getVaultBasePath() - if (!vaultPath) { - new Notice('This feature is only supported in the desktop app.') + async getCurrentBranch(): Promise { + if (!this.vaultAdapter) { + new Notice('Vault adapter not available. Cannot access file system.') return null } - const gitDir = path.join(vaultPath, '.git') - if (!fs.existsSync(gitDir)) { - new Notice('No .git directory found in the vault.') + try { + // Check if .git directory exists + const gitDirExists = await this.vaultAdapter.exists(GIT_DIR) + if (!gitDirExists) { + // No notice here, as getGitHubRepoUrl would likely fail first + return null + } + + // Check if .git/HEAD file exists + const headPathExists = await this.vaultAdapter.exists(GIT_HEAD_PATH) + if (!headPathExists) { + new Notice('No .git/HEAD file found. Cannot determine branch.') + return null + } + + // Read the HEAD file content + const headContent = (await this.vaultAdapter.read(GIT_HEAD_PATH)).trim() + + // Example HEAD content: "ref: refs/heads/main" or "ref: refs/heads/feature/branch" + const branchMatch = headContent.match(/^ref:\s+refs\/heads\/(.*)/) + if (branchMatch && branchMatch[1]) { + return branchMatch[1] // Return the branch name + } + + new Notice('Could not parse branch name from .git/HEAD.') + return null + } catch (error) { + console.error("OpenInGitHubPlugin: Error reading git HEAD:", error) + new Notice("Error reading git branch information.") return null } - - const headPath = path.join(gitDir, 'HEAD'); - const headContent = fs.readFileSync(headPath, 'utf-8').trim(); - - // Example: ref: refs/heads/main - const branchMatch = headContent.match(/ref: refs\/heads\/(.*)/); - if (branchMatch) { - return branchMatch[1]; - } - - return null; } -} +} \ No newline at end of file