mirror of
https://github.com/felvesthe/Note-Locker.git
synced 2026-07-22 12:30:24 +00:00
Compare commits
4 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a2fbbbb9d | ||
|
|
1bbbb63f90 | ||
|
|
7d377dc99d | ||
|
|
cd4ecd7821 |
5 changed files with 63 additions and 42 deletions
58
src/main.ts
58
src/main.ts
|
|
@ -8,6 +8,7 @@ import {
|
||||||
WorkspaceLeaf,
|
WorkspaceLeaf,
|
||||||
TAbstractFile,
|
TAbstractFile,
|
||||||
TFolder,
|
TFolder,
|
||||||
|
setIcon,
|
||||||
} from "obsidian";
|
} from "obsidian";
|
||||||
|
|
||||||
import { NoteLockerSettings, DEFAULT_SETTINGS } from "./models/types";
|
import { NoteLockerSettings, DEFAULT_SETTINGS } from "./models/types";
|
||||||
|
|
@ -44,7 +45,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
// hotkey
|
// hotkey
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'toggle-note-lock',
|
id: 'toggle-note-lock',
|
||||||
name: 'Toggle Lock for current note',
|
name: 'Toggle lock for current note',
|
||||||
checkCallback: (checking: boolean) => {
|
checkCallback: (checking: boolean) => {
|
||||||
const file = this.app.workspace.getActiveFile();
|
const file = this.app.workspace.getActiveFile();
|
||||||
if (file && file.extension === 'md') {
|
if (file && file.extension === 'md') {
|
||||||
|
|
@ -65,7 +66,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'switch-to-edit-mode-intercept',
|
id: 'switch-to-edit-mode-intercept',
|
||||||
name: 'Switch to Edit Mode (Strict Lock Check)',
|
name: 'Switch to edit mode (strict lock check)',
|
||||||
hotkeys: [{ modifiers: ["Mod"], key: "e" }],
|
hotkeys: [{ modifiers: ["Mod"], key: "e" }],
|
||||||
checkCallback: (checking: boolean) => {
|
checkCallback: (checking: boolean) => {
|
||||||
const file = this.app.workspace.getActiveFile();
|
const file = this.app.workspace.getActiveFile();
|
||||||
|
|
@ -113,7 +114,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
|
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.workspace.on("active-leaf-change", (leaf) => {
|
this.app.workspace.on("active-leaf-change", (leaf) => {
|
||||||
this.updateLeafMode(leaf);
|
this.updateLeafMode(leaf, true);
|
||||||
this.statusBarUI.updateStatusBarButton();
|
this.statusBarUI.updateStatusBarButton();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -147,6 +148,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
this.app.workspace.on("file-open", () => {
|
this.app.workspace.on("file-open", () => {
|
||||||
this.statusBarUI.updateStatusBarButton();
|
this.statusBarUI.updateStatusBarButton();
|
||||||
this.fileExplorerUI.updateFileExplorerIcons();
|
this.fileExplorerUI.updateFileExplorerIcons();
|
||||||
|
this.updateLeafMode(this.app.workspace.getLeaf(false), true);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -161,7 +163,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
private initializeExistingLeaves() {
|
private initializeExistingLeaves() {
|
||||||
this.app.workspace
|
this.app.workspace
|
||||||
.getLeavesOfType("markdown")
|
.getLeavesOfType("markdown")
|
||||||
.forEach((leaf) => this.updateLeafMode(leaf));
|
.forEach((leaf) => this.updateLeafMode(leaf, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private addBulkLockMenuItem(menu: Menu, files: TAbstractFile[]) {
|
private addBulkLockMenuItem(menu: Menu, files: TAbstractFile[]) {
|
||||||
|
|
@ -201,7 +203,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
new StrictUnlockModal(
|
new StrictUnlockModal(
|
||||||
this.app,
|
this.app,
|
||||||
() => this.toggleBulkLock(validItems, false),
|
() => this.toggleBulkLock(validItems, false),
|
||||||
"Strictly Locked Items",
|
"Strictly locked items",
|
||||||
"At least one of the selected items is strictly locked."
|
"At least one of the selected items is strictly locked."
|
||||||
).open();
|
).open();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -223,7 +225,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
if (allUnlocked || allLocked) {
|
if (allUnlocked || allLocked) {
|
||||||
menu.addItem((item) =>
|
menu.addItem((item) =>
|
||||||
item
|
item
|
||||||
.setTitle(`Strict Lock ${validItems.length} items`)
|
.setTitle(`Strict lock ${validItems.length} items`)
|
||||||
.setIcon("lock")
|
.setIcon("lock")
|
||||||
.onClick(() => this.toggleBulkStrictLock(validItems, true))
|
.onClick(() => this.toggleBulkStrictLock(validItems, true))
|
||||||
);
|
);
|
||||||
|
|
@ -310,14 +312,14 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
if (!isStrictLocked) {
|
if (!isStrictLocked) {
|
||||||
menu.addItem((item) =>
|
menu.addItem((item) =>
|
||||||
item
|
item
|
||||||
.setTitle("Strict Lock")
|
.setTitle("Strict lock")
|
||||||
.setIcon("lock")
|
.setIcon("lock")
|
||||||
.onClick(() => this.toggleStrictLock(path))
|
.onClick(() => this.toggleStrictLock(path))
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
menu.addItem((item) =>
|
menu.addItem((item) =>
|
||||||
item
|
item
|
||||||
.setTitle("Strict Unlock")
|
.setTitle("Strict unlock")
|
||||||
.setIcon("unlock")
|
.setIcon("unlock")
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
new StrictUnlockModal(this.app, () => {
|
new StrictUnlockModal(this.app, () => {
|
||||||
|
|
@ -409,15 +411,26 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
async toggleStrictLock(notePath: string) {
|
async toggleStrictLock(notePath: string) {
|
||||||
const isStrictLocked = this.settings.strictLockedNotes.has(notePath);
|
const isStrictLocked = this.settings.strictLockedNotes.has(notePath);
|
||||||
|
|
||||||
if (isStrictLocked) {
|
isStrictLocked
|
||||||
this.settings.strictLockedNotes.delete(notePath);
|
? this.settings.strictLockedNotes.delete(notePath)
|
||||||
} else {
|
: this.settings.strictLockedNotes.add(notePath);
|
||||||
this.settings.strictLockedNotes.add(notePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.saveSettings();
|
await this.saveSettings();
|
||||||
|
|
||||||
new Notice(`${isStrictLocked ? '🔓 Strictly Unlocked' : '🔒 Strictly Locked'}: ${notePath}`);
|
const file = this.app.vault.getAbstractFileByPath(notePath);
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
new Notice("Error: Note not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.settings.showNotifications) {
|
||||||
|
const fileName = file instanceof TFile ? file.basename :
|
||||||
|
notePath.split('/').pop()?.replace(/\..+$/, '') || notePath;
|
||||||
|
const displayName = this.truncateFileName(fileName);
|
||||||
|
|
||||||
|
new Notice(`${isStrictLocked ? '🔓 Strictly unlocked' : '🔒 Strictly locked'}: ${displayName}`);
|
||||||
|
}
|
||||||
|
|
||||||
this.updateAllNoteInstances(notePath);
|
this.updateAllNoteInstances(notePath);
|
||||||
this.statusBarUI.updateStatusBarButton();
|
this.statusBarUI.updateStatusBarButton();
|
||||||
|
|
@ -459,7 +472,7 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateLeafMode(leaf: WorkspaceLeaf | null) {
|
public updateLeafMode(leaf: WorkspaceLeaf | null, fromNavigation: boolean = false) {
|
||||||
if (!leaf || !(leaf.view instanceof MarkdownView)) return;
|
if (!leaf || !(leaf.view instanceof MarkdownView)) return;
|
||||||
|
|
||||||
const { view } = leaf;
|
const { view } = leaf;
|
||||||
|
|
@ -476,17 +489,11 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
...state,
|
...state,
|
||||||
state: { ...state.state, mode: 'preview' },
|
state: { ...state.state, mode: 'preview' },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.app.workspace.activeLeaf === leaf) {
|
|
||||||
new StrictUnlockModal(this.app, () => {
|
|
||||||
this.toggleStrictLock(path);
|
|
||||||
}).open();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (isLocked) {
|
} else if (isLocked) {
|
||||||
const state = leaf.getViewState();
|
const state = leaf.getViewState();
|
||||||
if (this.settings.preventEditInLockedNotes && state.state?.mode === 'source') {
|
if ((fromNavigation || this.settings.preventEditInLockedNotes) && state.state?.mode === 'source') {
|
||||||
leaf.setViewState({
|
leaf.setViewState({
|
||||||
...state,
|
...state,
|
||||||
state: { ...state.state, mode: 'preview' },
|
state: { ...state.state, mode: 'preview' },
|
||||||
|
|
@ -538,12 +545,12 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
|
|
||||||
private addStrictLockButton(view: MarkdownView, path: string) {
|
private addStrictLockButton(view: MarkdownView, path: string) {
|
||||||
const container = view.containerEl;
|
const container = view.containerEl;
|
||||||
let lockBtn = container.querySelector('.note-locker-strict-btn');
|
let lockBtn = container.querySelector('.note-locker-strict-btn') as HTMLElement;
|
||||||
if (!lockBtn) {
|
if (!lockBtn) {
|
||||||
lockBtn = document.createElement('div');
|
lockBtn = document.createElement('div');
|
||||||
lockBtn.addClass('view-action', 'clickable-icon', 'note-locker-strict-btn');
|
lockBtn.addClass('view-action', 'clickable-icon', 'note-locker-strict-btn');
|
||||||
lockBtn.setAttribute('aria-label', 'Strictly Locked');
|
lockBtn.setAttribute('aria-label', 'Strictly locked');
|
||||||
lockBtn.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-lock"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>';
|
setIcon(lockBtn, 'lock-keyhole');
|
||||||
|
|
||||||
lockBtn.addEventListener('click', () => {
|
lockBtn.addEventListener('click', () => {
|
||||||
new StrictUnlockModal(this.app, () => {
|
new StrictUnlockModal(this.app, () => {
|
||||||
|
|
@ -564,7 +571,6 @@ export default class NoteLockerPlugin extends Plugin {
|
||||||
if (lockBtn) lockBtn.remove();
|
if (lockBtn) lockBtn.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async loadSettings() {
|
async loadSettings() {
|
||||||
const loaded = await this.loadData();
|
const loaded = await this.loadData();
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ export class NoteLockerSettingTab extends PluginSettingTab {
|
||||||
});
|
});
|
||||||
hotkeyInfo.style.fontStyle = 'italic';
|
hotkeyInfo.style.fontStyle = 'italic';
|
||||||
|
|
||||||
containerEl.createEl('h3', { text: 'Locked Notes & Folders Statistics' });
|
containerEl.createEl('h3', { text: 'Locked notes & folders statistics' });
|
||||||
const lockedNotesCount = this.plugin.settings.lockedNotes.size;
|
const lockedNotesCount = this.plugin.settings.lockedNotes.size;
|
||||||
const lockedFoldersCount = this.plugin.settings.lockedFolders.size;
|
const lockedFoldersCount = this.plugin.settings.lockedFolders.size;
|
||||||
const strictLockedNotesCount = this.plugin.settings.strictLockedNotes.size;
|
const strictLockedNotesCount = this.plugin.settings.strictLockedNotes.size;
|
||||||
|
|
|
||||||
|
|
@ -141,14 +141,20 @@ export class FileExplorerUI {
|
||||||
const path = titleEl.getAttribute('data-path');
|
const path = titleEl.getAttribute('data-path');
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
|
|
||||||
let shouldHaveIcon = false;
|
let iconName: string | null = null;
|
||||||
if (item.classList.contains('nav-file')) {
|
if (item.classList.contains('nav-file')) {
|
||||||
shouldHaveIcon = this.plugin.settings.lockedNotes.has(path) || this.plugin.settings.strictLockedNotes.has(path);
|
if (this.plugin.settings.strictLockedNotes.has(path)) {
|
||||||
|
iconName = 'lock-keyhole';
|
||||||
|
} else if (this.plugin.settings.lockedNotes.has(path)) {
|
||||||
|
iconName = 'lock';
|
||||||
|
}
|
||||||
} else if (item.classList.contains('nav-folder')) {
|
} else if (item.classList.contains('nav-folder')) {
|
||||||
shouldHaveIcon = this.plugin.settings.lockedFolders.has(path);
|
if (this.plugin.settings.lockedFolders.has(path)) {
|
||||||
|
iconName = 'lock';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateIconState(titleEl, shouldHaveIcon);
|
this.updateIconState(titleEl, iconName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateFileExplorerIcons(): void {
|
public updateFileExplorerIcons(): void {
|
||||||
|
|
@ -166,8 +172,13 @@ export class FileExplorerUI {
|
||||||
const filePath = titleEl.getAttribute('data-path');
|
const filePath = titleEl.getAttribute('data-path');
|
||||||
if (!filePath) return;
|
if (!filePath) return;
|
||||||
|
|
||||||
const shouldHaveIcon = this.plugin.settings.lockedNotes.has(filePath) || this.plugin.settings.strictLockedNotes.has(filePath);
|
let iconName: string | null = null;
|
||||||
this.updateIconState(titleEl, shouldHaveIcon);
|
if (this.plugin.settings.strictLockedNotes.has(filePath)) {
|
||||||
|
iconName = 'lock-keyhole';
|
||||||
|
} else if (this.plugin.settings.lockedNotes.has(filePath)) {
|
||||||
|
iconName = 'lock';
|
||||||
|
}
|
||||||
|
this.updateIconState(titleEl, iconName);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle folders
|
// Handle folders
|
||||||
|
|
@ -179,20 +190,25 @@ export class FileExplorerUI {
|
||||||
const folderPath = titleEl.getAttribute('data-path');
|
const folderPath = titleEl.getAttribute('data-path');
|
||||||
if (!folderPath) return;
|
if (!folderPath) return;
|
||||||
|
|
||||||
const shouldHaveIcon = this.plugin.settings.lockedFolders.has(folderPath);
|
let iconName: string | null = null;
|
||||||
this.updateIconState(titleEl, shouldHaveIcon);
|
if (this.plugin.settings.lockedFolders.has(folderPath)) {
|
||||||
|
iconName = 'lock';
|
||||||
|
}
|
||||||
|
this.updateIconState(titleEl, iconName);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateIconState(targetEl: Element, shouldHaveIcon: boolean): void {
|
private updateIconState(targetEl: Element, iconName: string | null): void {
|
||||||
const existingIcon = targetEl.querySelector('.note-locker-icon');
|
const existingIcon = targetEl.querySelector('.note-locker-icon');
|
||||||
|
|
||||||
if (shouldHaveIcon) {
|
if (iconName) {
|
||||||
if (!existingIcon) {
|
if (!existingIcon) {
|
||||||
const iconEl = document.createElement('div');
|
const iconEl = document.createElement('div');
|
||||||
iconEl.addClass('note-locker-icon');
|
iconEl.addClass('note-locker-icon');
|
||||||
setIcon(iconEl, 'lock');
|
setIcon(iconEl, iconName);
|
||||||
targetEl.appendChild(iconEl);
|
targetEl.appendChild(iconEl);
|
||||||
|
} else {
|
||||||
|
setIcon(existingIcon as HTMLElement, iconName);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (existingIcon) {
|
if (existingIcon) {
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,8 @@ export class StatusBarUI {
|
||||||
const iconSpan = this.statusBarItemEl.createSpan({ cls: 'locker-icon' });
|
const iconSpan = this.statusBarItemEl.createSpan({ cls: 'locker-icon' });
|
||||||
|
|
||||||
if (isStrictLocked) {
|
if (isStrictLocked) {
|
||||||
setIcon(iconSpan, 'lock');
|
setIcon(iconSpan, 'lock-keyhole');
|
||||||
iconSpan.setText('🔓');
|
this.statusBarItemEl.createSpan({ text: ' Strictly locked' });
|
||||||
this.statusBarItemEl.createSpan({ text: ' Strictly Locked' });
|
|
||||||
this.statusBarItemEl.setAttribute('aria-label', 'Strictly locked. Click to unlock.');
|
this.statusBarItemEl.setAttribute('aria-label', 'Strictly locked. Click to unlock.');
|
||||||
} else {
|
} else {
|
||||||
setIcon(iconSpan, isLocked ? 'lock' : 'unlock');
|
setIcon(iconSpan, isLocked ? 'lock' : 'unlock');
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export class StrictUnlockModal extends Modal {
|
||||||
constructor(
|
constructor(
|
||||||
app: App,
|
app: App,
|
||||||
onUnlock: () => void,
|
onUnlock: () => void,
|
||||||
title: string = "Strictly Locked Note",
|
title: string = "Strictly locked note",
|
||||||
message: string = "This note is strictly locked to prevent accidental editing.") {
|
message: string = "This note is strictly locked to prevent accidental editing.") {
|
||||||
super(app);
|
super(app);
|
||||||
this.onUnlock = onUnlock;
|
this.onUnlock = onUnlock;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue