mirror of
https://github.com/idanlib/ObsidianFastForwardLinkPlugin.git
synced 2026-07-22 11:30:28 +00:00
commit
d65390304b
5 changed files with 142 additions and 119 deletions
10
README.md
10
README.md
|
|
@ -23,6 +23,16 @@ Here are some examples of how you might set up FastForwardLink:
|
|||
|
||||

|
||||
|
||||
## Version Updates
|
||||
|
||||
### Version 1.1
|
||||
|
||||
Some improvements include:
|
||||
|
||||
- You can now temporarily by pass forwarding with a designated command!
|
||||
- Source code refactored and better organized for future conributors.
|
||||
- Better robust event and error handling and messaging.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiple Links, One Target**: Set multiple links to redirect to a single target note for quick navigation between related topics or abbreviations. Organize synonyms or alternate spellings for easier access.
|
||||
|
|
|
|||
242
main.ts
242
main.ts
|
|
@ -1,6 +1,7 @@
|
|||
import { getLinkpath, Plugin, Notice, TFolder, TFile } from "obsidian";
|
||||
import RedirectSettingsTab from "components/Settings";
|
||||
|
||||
// --- Setup ---
|
||||
interface RedirectSettings {
|
||||
openInNewTab: boolean;
|
||||
switchToNewTab: boolean;
|
||||
|
|
@ -11,70 +12,163 @@ const DEFAULT_SETTINGS: RedirectSettings = {
|
|||
switchToNewTab: false,
|
||||
};
|
||||
|
||||
// --- Main Plugin Class ---
|
||||
export default class RedirectPlugin extends Plugin {
|
||||
// --- Properties ---
|
||||
settings: RedirectSettings;
|
||||
redirectsFolder: TFolder | null = null;
|
||||
isMovingNote = false;
|
||||
isBypassRedirect = false;
|
||||
|
||||
// --- Lifecycle methods and commands---
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
this.addSettingTab(new RedirectSettingsTab(this.app, this));
|
||||
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
await this.createRedirectsFolder();
|
||||
await this.redirect();
|
||||
});
|
||||
|
||||
this.app.workspace.on("file-open", this.handleFileOpen);
|
||||
|
||||
this.addCommand({
|
||||
id: "paste-redirect-syntax",
|
||||
name: "Paste redirect syntax onto selection",
|
||||
editorCallback: (editor, view) => {
|
||||
const selection = editor.getSelection();
|
||||
editor.replaceSelection(`::>[[${selection}]]`);
|
||||
const { ch, line } = editor.getCursor();
|
||||
if (!selection) {
|
||||
editor.setCursor({
|
||||
ch: ch - 2,
|
||||
line: line,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "bypass-forwarding",
|
||||
name: "Bypass forwarding to target note",
|
||||
callback: () => {
|
||||
this.isBypassRedirect = true;
|
||||
new Notice("Bypassing redirects.");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.app.workspace.off("file-open", this.handleFileOpen);
|
||||
}
|
||||
|
||||
// --- Event Handlers ---
|
||||
handleFileOpen = async (file: TFile) => {
|
||||
if (this.isMovingNote) return;
|
||||
|
||||
if (this.isBypassRedirect) {
|
||||
this.isBypassRedirect = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await this.redirect();
|
||||
};
|
||||
|
||||
async changeSettings(newTab: boolean, switchToTab: boolean): Promise<void> {
|
||||
this.settings.openInNewTab = newTab;
|
||||
this.settings.switchToNewTab = switchToTab;
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
// --- Core Logic ---
|
||||
private async redirect(): Promise<void> {
|
||||
const fileAndContent = await this.getCurrentFile();
|
||||
if (!fileAndContent) return;
|
||||
|
||||
const { currentFile, content } = fileAndContent;
|
||||
const targetNoteFile = this.getTargetFile(content);
|
||||
if (!targetNoteFile) return;
|
||||
|
||||
await this.moveRedirectNote(currentFile);
|
||||
await this.openTargetNote(targetNoteFile);
|
||||
}
|
||||
|
||||
// --- Helper functions ---
|
||||
|
||||
private async moveRedirectNote(
|
||||
redirectingNote: TFile | null
|
||||
): Promise<TFile | void> {
|
||||
if (
|
||||
!redirectingNote ||
|
||||
redirectingNote.path === `_forwards/${redirectingNote.name}`
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Notice(
|
||||
`Moving ${redirectingNote.name} to the _forwards folder.`,
|
||||
2000
|
||||
);
|
||||
|
||||
if (!this.redirectsFolder) {
|
||||
await this.createRedirectsFolder();
|
||||
}
|
||||
|
||||
try {
|
||||
this.isMovingNote = true;
|
||||
const redirectingNoteInFolder = await this.app.vault.copy(
|
||||
redirectingNote,
|
||||
`/_forwards/${redirectingNote.name}`
|
||||
);
|
||||
|
||||
if (this.settings.openInNewTab) {
|
||||
await this.openTargetNote(redirectingNoteInFolder);
|
||||
}
|
||||
|
||||
// Delay deletion to avoid race condition where the original note is deleted before the new note is fully opened in the UI.
|
||||
setTimeout(async () => {
|
||||
await this.deleteNote(redirectingNote);
|
||||
this.isMovingNote = false;
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
this.isMovingNote = false;
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async openTargetNote(targetNoteFile: TFile) {
|
||||
await this.app.workspace.openLinkText(
|
||||
targetNoteFile.name,
|
||||
targetNoteFile.path,
|
||||
this.settings.openInNewTab,
|
||||
{ active: this.settings.switchToNewTab }
|
||||
);
|
||||
}
|
||||
|
||||
private async createRedirectsFolder() {
|
||||
try {
|
||||
this.redirectsFolder = await this.app.vault.createFolder(
|
||||
"/_forwards"
|
||||
);
|
||||
} catch (error) {
|
||||
new Notice("Failed to create the `_forwards` folder.");
|
||||
console.error(error);
|
||||
console.warn(
|
||||
"`_forwards` folder not created or already exists.",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async redirect() {
|
||||
private async getCurrentFile(): Promise<{
|
||||
currentFile: TFile;
|
||||
content: string;
|
||||
} | null> {
|
||||
const currentFile = this.app.workspace.getActiveFile();
|
||||
|
||||
if (!currentFile) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const currentFileContent = (await this.getCurrentFileContent(
|
||||
currentFile
|
||||
)) as string;
|
||||
|
||||
const targetNoteFile = this.getTargetFile(currentFileContent);
|
||||
|
||||
if (!targetNoteFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFile.path !== `_forwards/${currentFile.name}`) {
|
||||
new Notice(
|
||||
`Moving ${currentFile.name} to the _forwards folder.`,
|
||||
2000
|
||||
);
|
||||
await this.moveRedirectNote(currentFile);
|
||||
}
|
||||
|
||||
await this.app.workspace.openLinkText(
|
||||
targetNoteFile.name,
|
||||
targetNoteFile.path,
|
||||
this.settings.openInNewTab,
|
||||
{ active: this.settings.switchToNewTab }
|
||||
);
|
||||
return {
|
||||
currentFile: currentFile,
|
||||
content: (await this.getCurrentFileContent(currentFile)) || "",
|
||||
};
|
||||
}
|
||||
|
||||
private async getCurrentFileContent(
|
||||
|
|
@ -85,7 +179,6 @@ export default class RedirectPlugin extends Plugin {
|
|||
} catch (error) {
|
||||
new Notice(`Cannot read ${currentFile}.`);
|
||||
console.error(`Failed to read ${currentFile} content: `, error);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -116,46 +209,6 @@ export default class RedirectPlugin extends Plugin {
|
|||
return targetNoteFile;
|
||||
}
|
||||
|
||||
private async moveRedirectNote(
|
||||
redirectingNote: TFile | null
|
||||
): Promise<TFile | void> {
|
||||
if (!redirectingNote) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.redirectsFolder) {
|
||||
await this.createRedirectsFolder();
|
||||
}
|
||||
|
||||
try {
|
||||
this.isMovingNote = true;
|
||||
const redirectingNoteInFolder = await this.app.vault.copy(
|
||||
redirectingNote,
|
||||
`/_forwards/${redirectingNote.name}`
|
||||
);
|
||||
|
||||
if (this.settings.openInNewTab) {
|
||||
await this.app.workspace.openLinkText(
|
||||
redirectingNoteInFolder.name,
|
||||
redirectingNoteInFolder.path,
|
||||
this.settings.openInNewTab,
|
||||
{
|
||||
active: this.settings.switchToNewTab,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Delay deletion to avoid race condition where the original note is deleted before the new note is fully opened in the UI.
|
||||
setTimeout(async () => {
|
||||
await this.deleteNote(redirectingNote);
|
||||
this.isMovingNote = false;
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
this.isMovingNote = false;
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteNote(orgRedirectingNote: TFile): Promise<void> {
|
||||
try {
|
||||
await this.app.fileManager.trashFile(orgRedirectingNote);
|
||||
|
|
@ -168,48 +221,7 @@ export default class RedirectPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
this.addSettingTab(new RedirectSettingsTab(this.app, this));
|
||||
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
await this.createRedirectsFolder();
|
||||
await this.redirect();
|
||||
});
|
||||
|
||||
this.app.workspace.on("file-open", this.handleFileOpen);
|
||||
|
||||
this.addCommand({
|
||||
id: "paste-redirect-syntax",
|
||||
name: "Paste redirect syntax onto selection",
|
||||
editorCallback: (editor, view) => {
|
||||
const selection = editor.getSelection();
|
||||
editor.replaceSelection(`::>[[${selection}]]`);
|
||||
const { ch, line } = editor.getCursor();
|
||||
if (!selection) {
|
||||
editor.setCursor({
|
||||
ch: ch - 2,
|
||||
line: line,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "bypass-redirect",
|
||||
name: "Bypass redirect to target note",
|
||||
callback: () => {
|
||||
this.isBypassRedirect = true;
|
||||
new Notice("Bypassing redirects.");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.app.workspace.off("file-open", this.handleFileOpen);
|
||||
}
|
||||
|
||||
// --- Settings Persistence ---
|
||||
async loadSettings() {
|
||||
try {
|
||||
this.settings = Object.assign(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "fast-forward-link",
|
||||
"name": "FastForwardLink",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Fast-forward multiple links to a single target note. Create custom link shorthands (like `ps` > `photoshop`) to create synonyms, streamline navigation, and keep your vault organized.",
|
||||
"author": "Idan Liberman",
|
||||
"isDesktopOnly": false,
|
||||
"fundingUrl": "https://buymeacoffee.com/idanlib"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fast-forward-link",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "Forward multiple links to a single note with ease.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"1.0.0": "0.15.0",
|
||||
"1.1.0": "0.15.0"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue