mirror of
https://github.com/youfoundjk/TeXcore.git
synced 2026-07-22 07:33:31 +00:00
feat: implement custom note management with hotkey support
This commit is contained in:
parent
ed45018fdc
commit
451ff80edf
5 changed files with 275 additions and 0 deletions
71
src/features/custom-notes/manager.ts
Normal file
71
src/features/custom-notes/manager.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { Notice, TFile } from "obsidian";
|
||||
import type LatexReferencer from "../../main";
|
||||
|
||||
export class CustomNoteManager {
|
||||
private registeredCommandIds: string[] = [];
|
||||
|
||||
constructor(private plugin: LatexReferencer) {}
|
||||
|
||||
onLoad() {
|
||||
this.registerCommands();
|
||||
}
|
||||
|
||||
registerCommands() {
|
||||
// 1. Unregister all previously registered commands
|
||||
const appCommands = (this.plugin.app as any).commands;
|
||||
if (appCommands && typeof appCommands.removeCommand === "function") {
|
||||
for (const cmdId of this.registeredCommandIds) {
|
||||
try {
|
||||
appCommands.removeCommand(cmdId);
|
||||
} catch (e) {
|
||||
console.error("Failed to remove custom note command", cmdId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.registeredCommandIds = [];
|
||||
|
||||
// 2. Register new commands
|
||||
const customNotes = this.plugin.settings.customNoteHotkeys || [];
|
||||
for (const item of customNotes) {
|
||||
if (!item.notePath) continue;
|
||||
|
||||
const displayName = item.name || item.notePath.split("/").pop()?.replace(/\.md$/, "") || item.notePath;
|
||||
const commandId = `open-custom-note-${item.id}`;
|
||||
const fullCommandId = `${this.plugin.manifest.id}:${commandId}`;
|
||||
|
||||
const commandConfig: any = {
|
||||
id: commandId,
|
||||
name: `Open custom note: ${displayName}`,
|
||||
callback: () => {
|
||||
this.openCustomNote(item.notePath);
|
||||
}
|
||||
};
|
||||
|
||||
if (item.hotkeyKey && item.hotkeyKey.trim()) {
|
||||
commandConfig.hotkeys = [
|
||||
{
|
||||
modifiers: item.hotkeyModifiers || [],
|
||||
key: item.hotkeyKey.trim()
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
this.plugin.addCommand(commandConfig);
|
||||
this.registeredCommandIds.push(fullCommandId);
|
||||
}
|
||||
}
|
||||
|
||||
async openCustomNote(notePath: string) {
|
||||
let file = this.plugin.app.vault.getAbstractFileByPath(notePath);
|
||||
if (!file && !notePath.endsWith(".md")) {
|
||||
file = this.plugin.app.vault.getAbstractFileByPath(notePath + ".md");
|
||||
}
|
||||
|
||||
if (file instanceof TFile) {
|
||||
const leaf = this.plugin.app.workspace.getLeaf("tab");
|
||||
await leaf.openFile(file);
|
||||
} else {
|
||||
new Notice(`Custom note not found at path: ${notePath}. Please verify your configuration in settings.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/features/custom-notes/modal.ts
Normal file
25
src/features/custom-notes/modal.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { App, FuzzyMatch, FuzzySuggestModal, TFile } from "obsidian";
|
||||
|
||||
export class NoteSuggestModal extends FuzzySuggestModal<TFile> {
|
||||
constructor(app: App, private onSelect: (file: TFile) => void) {
|
||||
super(app);
|
||||
this.setPlaceholder("Search note path...");
|
||||
}
|
||||
|
||||
getItems(): TFile[] {
|
||||
return this.app.vault.getMarkdownFiles();
|
||||
}
|
||||
|
||||
getItemText(item: TFile): string {
|
||||
return item.path;
|
||||
}
|
||||
|
||||
renderSuggestion(item: FuzzyMatch<TFile>, el: HTMLElement): void {
|
||||
el.createEl("div", { text: item.item.basename });
|
||||
el.createEl("small", { text: item.item.path, attr: { style: "color: var(--text-muted);" } });
|
||||
}
|
||||
|
||||
onChooseItem(item: TFile, evt: MouseEvent | KeyboardEvent): void {
|
||||
this.onSelect(item);
|
||||
}
|
||||
}
|
||||
|
|
@ -63,6 +63,17 @@ export interface PluginSettings {
|
|||
|
||||
// Zotero Cleanup
|
||||
zoteroCleanDirectories: string;
|
||||
|
||||
// Custom Note Hotkeys
|
||||
customNoteHotkeys: CustomNoteHotkey[];
|
||||
}
|
||||
|
||||
export interface CustomNoteHotkey {
|
||||
id: string;
|
||||
notePath: string;
|
||||
name: string;
|
||||
hotkeyModifiers: Modifier[];
|
||||
hotkeyKey: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Required<PluginSettings> = {
|
||||
|
|
@ -122,4 +133,7 @@ export const DEFAULT_SETTINGS: Required<PluginSettings> = {
|
|||
|
||||
// Zotero Cleanup
|
||||
zoteroCleanDirectories: "",
|
||||
|
||||
// Custom Note Hotkeys
|
||||
customNoteHotkeys: [],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { App, PluginSettingTab, Setting, TextAreaComponent } from "obsidian";
|
||||
import LatexReferencer from "../../main";
|
||||
import { NUMBER_STYLES } from "./settings";
|
||||
import { NoteSuggestModal } from "../custom-notes/modal";
|
||||
|
||||
export class MathSettingTab extends PluginSettingTab {
|
||||
constructor(app: App, public plugin: LatexReferencer) {
|
||||
|
|
@ -288,6 +289,164 @@ export class MathSettingTab extends PluginSettingTab {
|
|||
textArea.inputEl.setAttr("rows", 3);
|
||||
});
|
||||
|
||||
containerEl.createEl("h2", { text: "Custom Note Hotkeys" });
|
||||
containerEl.createEl("p", {
|
||||
text: "Configure hotkeys to quickly open specific notes in your vault. You can define optional default hotkeys here, and further customize or rebind them within Obsidian's global 'Hotkeys' settings.",
|
||||
cls: "setting-item-description"
|
||||
});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Add new hotkey mapping")
|
||||
.setDesc("Create a new shortcut command to open a specific note.")
|
||||
.addButton(btn => btn
|
||||
.setButtonText("+ Add hotkey mapping")
|
||||
.setCta()
|
||||
.onClick(async () => {
|
||||
if (!this.plugin.settings.customNoteHotkeys) {
|
||||
this.plugin.settings.customNoteHotkeys = [];
|
||||
}
|
||||
this.plugin.settings.customNoteHotkeys.push({
|
||||
id: Date.now().toString(),
|
||||
notePath: "",
|
||||
name: "",
|
||||
hotkeyModifiers: ["Mod"],
|
||||
hotkeyKey: ""
|
||||
});
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
this.display();
|
||||
})
|
||||
);
|
||||
|
||||
const hotkeys = this.plugin.settings.customNoteHotkeys || [];
|
||||
hotkeys.forEach((item, index) => {
|
||||
const hotkeyContainer = containerEl.createEl("div", {
|
||||
cls: "custom-note-hotkey-item",
|
||||
attr: {
|
||||
style: "border: 1px solid var(--background-modifier-border); padding: 15px; margin-bottom: 15px; border-radius: 8px; background-color: var(--background-primary-alt);"
|
||||
}
|
||||
});
|
||||
|
||||
const titleRow = hotkeyContainer.createEl("div", {
|
||||
attr: { style: "display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;" }
|
||||
});
|
||||
titleRow.createEl("strong", { text: `Hotkey Mapping #${index + 1}` });
|
||||
|
||||
const deleteBtn = titleRow.createEl("button", {
|
||||
text: "Delete",
|
||||
cls: "mod-warning",
|
||||
attr: { style: "background-color: var(--background-modifier-error); color: var(--text-on-accent);" }
|
||||
});
|
||||
deleteBtn.addEventListener("click", async () => {
|
||||
hotkeys.splice(index, 1);
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
this.display();
|
||||
});
|
||||
|
||||
new Setting(hotkeyContainer)
|
||||
.setName("Friendly name")
|
||||
.setDesc("A clear label for the Obsidian command (e.g. 'Daily Planner').")
|
||||
.addText(text => text
|
||||
.setPlaceholder("e.g. My Note")
|
||||
.setValue(item.name)
|
||||
.onChange(async value => {
|
||||
item.name = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
})
|
||||
);
|
||||
|
||||
const pathSetting = new Setting(hotkeyContainer)
|
||||
.setName("Note path")
|
||||
.setDesc("The relative vault path to the target markdown note.");
|
||||
|
||||
pathSetting.addText(text => {
|
||||
text.setPlaceholder("e.g. Folder/My Note.md")
|
||||
.setValue(item.notePath)
|
||||
.onChange(async value => {
|
||||
item.notePath = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
});
|
||||
|
||||
pathSetting.addButton(btn => btn
|
||||
.setIcon("search")
|
||||
.setTooltip("Browse/Search vault notes")
|
||||
.onClick(() => {
|
||||
new NoteSuggestModal(this.app, async (file) => {
|
||||
text.setValue(file.path);
|
||||
item.notePath = file.path;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
}).open();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const hotkeySetting = new Setting(hotkeyContainer)
|
||||
.setName("Default hotkey")
|
||||
.setDesc("Select modifiers and input a key (e.g., '1', 'a') to assign a default shortcut.");
|
||||
|
||||
const isMod = item.hotkeyModifiers.includes("Mod");
|
||||
const isAlt = item.hotkeyModifiers.includes("Alt");
|
||||
const isShift = item.hotkeyModifiers.includes("Shift");
|
||||
|
||||
hotkeySetting.addToggle(toggle => toggle
|
||||
.setTooltip("Ctrl / Cmd (Mod)")
|
||||
.setValue(isMod)
|
||||
.onChange(async value => {
|
||||
if (value) {
|
||||
if (!item.hotkeyModifiers.includes("Mod")) item.hotkeyModifiers.push("Mod");
|
||||
} else {
|
||||
item.hotkeyModifiers = item.hotkeyModifiers.filter(m => m !== "Mod");
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
})
|
||||
);
|
||||
hotkeySetting.controlEl.createSpan({ text: "Ctrl/Cmd ", attr: { style: "margin-right: 15px; font-size: 0.9em;" } });
|
||||
|
||||
hotkeySetting.addToggle(toggle => toggle
|
||||
.setTooltip("Alt")
|
||||
.setValue(isAlt)
|
||||
.onChange(async value => {
|
||||
if (value) {
|
||||
if (!item.hotkeyModifiers.includes("Alt")) item.hotkeyModifiers.push("Alt");
|
||||
} else {
|
||||
item.hotkeyModifiers = item.hotkeyModifiers.filter(m => m !== "Alt");
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
})
|
||||
);
|
||||
hotkeySetting.controlEl.createSpan({ text: "Alt ", attr: { style: "margin-right: 15px; font-size: 0.9em;" } });
|
||||
|
||||
hotkeySetting.addToggle(toggle => toggle
|
||||
.setTooltip("Shift")
|
||||
.setValue(isShift)
|
||||
.onChange(async value => {
|
||||
if (value) {
|
||||
if (!item.hotkeyModifiers.includes("Shift")) item.hotkeyModifiers.push("Shift");
|
||||
} else {
|
||||
item.hotkeyModifiers = item.hotkeyModifiers.filter(m => m !== "Shift");
|
||||
}
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
})
|
||||
);
|
||||
hotkeySetting.controlEl.createSpan({ text: "Shift ", attr: { style: "margin-right: 15px; font-size: 0.9em;" } });
|
||||
|
||||
hotkeySetting.addText(text => text
|
||||
.setPlaceholder("Key (e.g. 1, a)")
|
||||
.setValue(item.hotkeyKey || "")
|
||||
.onChange(async value => {
|
||||
item.hotkeyKey = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.plugin.customNoteManager.registerCommands();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
new Setting(containerEl).setName("Debug").setHeading();
|
||||
new Setting(containerEl)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { checkAndFixCalloutMath } from 'utils/fixer';
|
|||
import { traverseFolder } from "./features/export-pdf/utils";
|
||||
import { SnippetManager } from 'features/snippets/manager';
|
||||
import { processZoteroCleanup } from 'features/zotero-cleanup';
|
||||
import { CustomNoteManager } from 'features/custom-notes/manager';
|
||||
|
||||
|
||||
const isDev = process.env.NODE_ENV === "development";
|
||||
|
|
@ -37,6 +38,7 @@ export default class LatexReferencer extends Plugin {
|
|||
editorExtensions: Extension[];
|
||||
internalProviders: Provider[] = [];
|
||||
snippetManager: SnippetManager;
|
||||
customNoteManager: CustomNoteManager;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
|
@ -47,6 +49,10 @@ export default class LatexReferencer extends Plugin {
|
|||
this.snippetManager = new SnippetManager(this);
|
||||
this.snippetManager.onLoad();
|
||||
|
||||
// Custom Notes
|
||||
this.customNoteManager = new CustomNoteManager(this);
|
||||
this.customNoteManager.onLoad();
|
||||
|
||||
this.addSettingTab(new MathSettingTab(this.app, this));
|
||||
|
||||
// Commands
|
||||
|
|
|
|||
Loading…
Reference in a new issue