diff --git a/docs/index.html b/docs/index.html
index fc519be..e67a79f 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -972,6 +972,14 @@
+
🔐
diff --git a/documentation/FEATURES.md b/documentation/FEATURES.md
index 0c52d73..2a3e674 100644
--- a/documentation/FEATURES.md
+++ b/documentation/FEATURES.md
@@ -286,6 +286,7 @@ date: {{date}}
| Windows/Linux | Mac | Action |
|---------------|-----|--------|
+| `Ctrl+Alt+P` | `Cmd+Option+P` | Quick Switcher (jump to any note) |
| `Ctrl+S` | `Cmd+S` | Save note |
| `Ctrl+Alt+N` | `Cmd+Option+N` | New note |
| `Ctrl+Alt+F` | `Cmd+Option+F` | New folder |
@@ -304,9 +305,11 @@ date: {{date}}
|---------------|-----|--------|--------|
| `Ctrl+B` | `Cmd+B` | Bold | `**text**` |
| `Ctrl+I` | `Cmd+I` | Italic | `*text*` |
-| `Ctrl+K` | `Cmd+K` | Insert link | `[text](url)` |
+| `Ctrl+K` | `Cmd+K` | Insert link (in editor) | `[text](url)` |
| `Ctrl+Alt+T` | `Cmd+Option+T` | Insert table | 3x3 table placeholder |
+> **Tip:** Use `Ctrl+Alt+P` to quickly jump to any note from anywhere in the app.
+
## 🧘 Zen Mode
Full immersive distraction-free writing experience:
diff --git a/frontend/app.js b/frontend/app.js
index 592fd2f..7ad6f66 100644
--- a/frontend/app.js
+++ b/frontend/app.js
@@ -262,6 +262,12 @@ function noteApp() {
shareLinkCopied: false,
_sharedNotePaths: new Set(), // O(1) lookup for shared note indicators
+ // Quick Switcher state (Ctrl+Alt+P)
+ showQuickSwitcher: false,
+ quickSwitcherQuery: '',
+ quickSwitcherIndex: 0,
+ quickSwitcherResults: [],
+
// Homepage state
selectedHomepageFolder: '',
_homepageCache: {
@@ -569,6 +575,13 @@ function noteApp() {
this.saveNote();
}
+ // Ctrl/Cmd + Alt + P for Quick Switcher
+ if ((e.ctrlKey || e.metaKey) && e.altKey && e.code === 'KeyP') {
+ e.preventDefault();
+ this.openQuickSwitcher();
+ return;
+ }
+
// Ctrl/Cmd + Alt/Option + N for new note
if ((e.ctrlKey || e.metaKey) && e.altKey && e.code === 'KeyN') {
e.preventDefault();
@@ -5040,6 +5053,101 @@ function noteApp() {
return this._sharedNotePaths.has(notePath);
},
+ // ============================================
+ // Quick Switcher (Ctrl+Alt+P)
+ // ============================================
+
+ openQuickSwitcher() {
+ this.showQuickSwitcher = true;
+ this.quickSwitcherQuery = '';
+ this.quickSwitcherIndex = 0;
+ // Populate initial results
+ this.quickSwitcherResults = (this.allNotes || []).slice(0, 10);
+ // Focus the input after the modal renders
+ this.$nextTick(() => {
+ const input = document.getElementById('quickSwitcherInput');
+ if (input) input.focus();
+ });
+ },
+
+ closeQuickSwitcher() {
+ this.showQuickSwitcher = false;
+ this.quickSwitcherQuery = '';
+ this.quickSwitcherIndex = 0;
+ },
+
+ // Filter notes for quick switcher based on query
+ filterQuickSwitcher(query) {
+ // Only include actual notes, not images
+ const notes = (this.notes || []).filter(n => n.type === 'note');
+ if (!query || !query.trim()) {
+ // Show recent notes when no query
+ return notes.slice(0, 10);
+ }
+ const q = query.toLowerCase();
+ return notes
+ .filter(n =>
+ n.name.toLowerCase().includes(q) ||
+ n.path.toLowerCase().includes(q)
+ )
+ .slice(0, 10);
+ },
+
+ // Handle keyboard navigation in quick switcher
+ handleQuickSwitcherKeydown(e) {
+ const results = this.quickSwitcherResults;
+
+ if (e.key === 'ArrowDown') {
+ e.preventDefault();
+ this.quickSwitcherIndex = Math.min(this.quickSwitcherIndex + 1, results.length - 1);
+ this.scrollQuickSwitcherIntoView();
+ } else if (e.key === 'ArrowUp') {
+ e.preventDefault();
+ this.quickSwitcherIndex = Math.max(this.quickSwitcherIndex - 1, 0);
+ this.scrollQuickSwitcherIntoView();
+ } else if (e.key === 'Enter') {
+ e.preventDefault();
+ const note = results[this.quickSwitcherIndex];
+ if (note) {
+ this.loadNote(note.path);
+ this.closeQuickSwitcher();
+ }
+ } else if (e.key === 'Escape') {
+ e.preventDefault();
+ this.closeQuickSwitcher();
+ }
+ },
+
+ // Scroll selected item into view in quick switcher
+ scrollQuickSwitcherIntoView() {
+ const container = document.getElementById('quickSwitcherResults');
+ if (!container) return;
+
+ // Get all result items (skip the "no results" template)
+ const items = container.querySelectorAll('[data-quick-switcher-item]');
+ const selectedItem = items[this.quickSwitcherIndex];
+
+ if (selectedItem) {
+ // Calculate if we need to scroll
+ const containerRect = container.getBoundingClientRect();
+ const itemRect = selectedItem.getBoundingClientRect();
+
+ if (itemRect.top < containerRect.top) {
+ // Item is above visible area
+ container.scrollTop -= (containerRect.top - itemRect.top);
+ } else if (itemRect.bottom > containerRect.bottom) {
+ // Item is below visible area
+ container.scrollTop += (itemRect.bottom - containerRect.bottom);
+ }
+ }
+ },
+
+ // Select note from quick switcher by click
+ selectQuickSwitcherNote(note) {
+ this.loadNote(note.path);
+ this.closeQuickSwitcher();
+ },
+
// Open share modal and fetch current share status
async openShareModal() {
if (!this.currentNote) return;
diff --git a/frontend/index.html b/frontend/index.html
index d30edf3..3802665 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2784,6 +2784,72 @@
+
+