mirror of
https://github.com/wenlzhang/obsidian-folder-navigator.git
synced 2026-07-22 12:10:25 +00:00
Compare commits
27 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4175b9dc30 | ||
|
|
28db3e9d50 | ||
|
|
d6f18b52c2 | ||
|
|
95e5d2937f | ||
|
|
5780b3e8bc | ||
|
|
b1af049004 | ||
|
|
658adcbfe4 | ||
|
|
89df8eb024 | ||
|
|
d6de79d372 | ||
|
|
97974cc801 | ||
|
|
3b440f3222 | ||
|
|
bf52a4b2a5 | ||
|
|
78f03608ea | ||
|
|
1dd71601ce | ||
|
|
71294a3b94 | ||
|
|
5929cd9cf2 | ||
|
|
72506484ee | ||
|
|
99e1d036aa | ||
|
|
eb222d3e52 | ||
|
|
5cfe391aec | ||
|
|
79dc260bb7 | ||
|
|
b339221329 | ||
|
|
1a4a369063 | ||
|
|
fad131f902 | ||
|
|
4227778ba0 | ||
|
|
c23b67f477 | ||
|
|
f2ef0c2795 |
12 changed files with 1438 additions and 211 deletions
57
CHANGELOG.md
57
CHANGELOG.md
|
|
@ -7,6 +7,63 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.5.0] - 2025-10-07
|
||||
|
||||
### Changes
|
||||
|
||||
- refactor: reorganize settings tab layout and improve section headings
|
||||
- feat: add create new folder option in folder navigator modal
|
||||
|
||||
## [0.4.1] - 2025-09-30
|
||||
|
||||
### Changes
|
||||
|
||||
- style: remove accent color from suggestion highlights
|
||||
|
||||
## [0.4.0] - 2025-09-30
|
||||
|
||||
### Changes
|
||||
|
||||
- refactor: improve folder search by excluding redundant child matches instead of deprioritizing
|
||||
- feat: implement keyword highlighting in folder search suggestions
|
||||
- refactor: remove unused match position tracking in folder search results
|
||||
- improve fuzzy search
|
||||
- Update README.md
|
||||
- Improve code format
|
||||
|
||||
## [0.3.1] - 2025-04-30
|
||||
|
||||
### Changes
|
||||
|
||||
- Refactor settings tab to dynamically show/hide folder history options based on mode
|
||||
- Rename FolderSortMode to FolderDisplayMode and update related UI text for clarity
|
||||
- Improve code format
|
||||
|
||||
## [0.3.0] - 2025-04-30
|
||||
|
||||
### Changes
|
||||
|
||||
- Add visual separators and confirmation modal for folder history reset
|
||||
- Add sort options
|
||||
- Add demo
|
||||
- Improve code format
|
||||
|
||||
## [0.2.1] - 2025-04-06
|
||||
|
||||
### Changes
|
||||
|
||||
- Clean up code
|
||||
- Fix folder expansion
|
||||
- Update README.md
|
||||
- Update README.md
|
||||
- Improve code format
|
||||
|
||||
## [0.2.0] - 2025-04-05
|
||||
|
||||
### Changes
|
||||
|
|
|
|||
68
README.md
68
README.md
|
|
@ -4,39 +4,61 @@
|
|||
|
||||
An [Obsidian](https://obsidian.md) plugin that allows you to quickly navigate to folders in your vault using fuzzy search.
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
|
||||
- Quick folder navigation using fuzzy search
|
||||
- Configurable maximum number of search results
|
||||
- Works with nested folders
|
||||
- Expand target folder on navigation
|
||||
- Smart folder prioritization options:
|
||||
- Default order (alphabetical)
|
||||
- Recently visited folders displayed at the top
|
||||
- Frequently visited folders displayed at the top
|
||||
- Option to reset folder navigation history if needed
|
||||
|
||||
## Usage
|
||||
|
||||
1. Press the hotkey (default: Cmd/Ctrl+Shift+O) to open the folder search modal
|
||||
2. Type to search for folders - the search is fuzzy, so you don't need to type the exact name
|
||||
3. Use arrow keys to navigate through the results
|
||||
4. Press Enter to select a folder and navigate to it in the file explorer
|
||||
## Videos and Articles
|
||||
|
||||
## Settings
|
||||
### Articles
|
||||
|
||||
- **Hotkey**: Customize the keyboard shortcut to open the folder navigator (requires restart)
|
||||
- **Maximum results**: Set the maximum number of folders to show in search results (5-50)
|
||||
- [The Strategy of Note Taking: Folders, Tags, Links, and Redundancy - PTKM](https://ptkm.net/blog-note-taking-strategy-folders-tags-links-redundancy)
|
||||
|
||||
## Installation
|
||||
## Why Folder Navigator?
|
||||
|
||||
1. Open Settings in Obsidian
|
||||
2. Navigate to Community Plugins and disable Safe Mode
|
||||
3. Click Browse and search for "Folder Navigator"
|
||||
4. Install the plugin
|
||||
5. Enable the plugin in the Community Plugins tab
|
||||
I really enjoy using Obsidian's file search feature, which allows us to quickly search for files and navigate to them. There are also third-party plugins that further enhance this experience.
|
||||
|
||||
## Manual Installation
|
||||
However, I need to navigate to folders in a similar way. Unfortunately, I couldn't find a method to do this, either by using Obsidian's official features or third-party plugins. That's why I'm developing this plugin to achieve that. **It allows you to navigate to folders just as you can when navigating to files.**
|
||||
|
||||
1. Download the latest [release](https://github.com/wenlzhang/obsidian-folder-navigator/releases)
|
||||
2. Extract the files into your vault's `.obsidian/plugins/obsidian-folder-navigator/` directory
|
||||
3. Reload Obsidian
|
||||
4. Enable the plugin in Settings > Community Plugins
|
||||
## Documentation
|
||||
|
||||
## Support
|
||||
📚 **[View Full Documentation](https://ptkm.net/obsidian-folder-navigator)**
|
||||
|
||||
<a href='https://ko-fi.com/C0C66C1TB' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi1.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||
Visit the documentation site to learn how to make the most of Folder Navigator in your Obsidian workflow.
|
||||
|
||||
## Support & Community
|
||||
|
||||
This plugin is developed and maintained during my free time. A lot of thought, energy, and care goes into making it reliable and user-friendly.
|
||||
|
||||
If you find Folder Navigator valuable in your daily workflow:
|
||||
|
||||
- If it helps you organize your folders more effectively
|
||||
- If it saves you time and mental energy navigating your knowledge base
|
||||
|
||||
Please consider supporting my work. Your support would help me dedicate more time and energy to:
|
||||
|
||||
- Developing new features
|
||||
- Maintaining code quality
|
||||
- Providing support and documentation
|
||||
- Making the plugin even better for everyone
|
||||
|
||||
### Ways to Support
|
||||
|
||||
You can support this project in several ways:
|
||||
|
||||
- ⭐ Star the project on GitHub
|
||||
- 💝 <a href='https://ko-fi.com/C0C66C1TB' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi1.png?v=3' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||
- 💌 Share your success stories and feedback
|
||||
- 📢 Spread the word about the plugin
|
||||
- 🐛 [Report issues](https://github.com/wenlzhang/obsidian-folder-navigator/issues) to help improve the plugin
|
||||
|
||||
Thank you for being part of this journey! 🙏
|
||||
|
|
|
|||
BIN
docs/attachment/demo.gif
Normal file
BIN
docs/attachment/demo.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 MiB |
169
docs/implementation/SEARCH_IMPROVEMENTS.md
Normal file
169
docs/implementation/SEARCH_IMPROVEMENTS.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Search Strategy Improvements
|
||||
|
||||
This document describes the improvements made to the folder search functionality to address issues #4 and #8.
|
||||
|
||||
## Issues Addressed
|
||||
|
||||
### Issue #4: Better Match Prioritization
|
||||
**Problem**: When searching for "abc", the plugin would show "abc" along with all its subdirectories like "abc/xxx", "abc/yyy", etc., making it difficult to find the target folder.
|
||||
|
||||
**Solution**: Implemented intelligent parent-child deduplication that:
|
||||
- Prioritizes parent folder matches over their children
|
||||
- Completely excludes child folders unless they have additional keyword matches beyond what the parent has
|
||||
- Only shows children when they contain additional matched keywords not found in the parent path
|
||||
|
||||
### Issue #8: Out-of-Order Keyword Matching
|
||||
**Problem**: Search only worked when keywords were in the correct order. For example, searching "bar foo" would not match "foo/bar/baz".
|
||||
|
||||
**Solution**: Implemented keyword-based matching that:
|
||||
- Splits the search query into space-separated keywords
|
||||
- Matches keywords independently in any order
|
||||
- Requires all keywords to be present in the folder path for a match
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Custom `getSuggestions` Method
|
||||
|
||||
The plugin now uses a custom `getSuggestions` method that overrides the default fuzzy search with a more sophisticated scoring system:
|
||||
|
||||
#### 1. Keyword Extraction
|
||||
```typescript
|
||||
const keywords = normalizedQuery.split(/\s+/).filter((k) => k.length > 0);
|
||||
```
|
||||
- Splits query by whitespace
|
||||
- Filters out empty strings
|
||||
- Allows matching keywords in any order
|
||||
|
||||
#### 2. Scoring System
|
||||
|
||||
Each folder is scored based on multiple factors:
|
||||
|
||||
**Keyword Match Bonuses:**
|
||||
- **+100**: Keyword matches in folder name (not just parent path)
|
||||
- **+200**: Exact folder name match
|
||||
- **+50**: Folder name starts with keyword
|
||||
- **+10**: Keyword only matches in parent path
|
||||
- **+0 to +50**: Earlier matches in path get higher scores
|
||||
|
||||
**Path-Based Adjustments:**
|
||||
- **-5 per level**: Penalty for nested depth (prefers shallower folders)
|
||||
- **+0 to +100**: Bonus for shorter paths (more specific matches)
|
||||
|
||||
#### 3. Parent-Child Deduplication
|
||||
|
||||
After initial scoring, the algorithm applies deduplication:
|
||||
|
||||
```typescript
|
||||
// Count total occurrences of all keywords in both parent and child
|
||||
let parentOccurrences = 0;
|
||||
let childOccurrences = 0;
|
||||
|
||||
for (const keyword of keywords) {
|
||||
// Count occurrences of this keyword in parent
|
||||
let pos = 0;
|
||||
while ((pos = parentLower.indexOf(keyword, pos)) !== -1) {
|
||||
parentOccurrences++;
|
||||
pos += keyword.length;
|
||||
}
|
||||
|
||||
// Count occurrences of this keyword in child
|
||||
pos = 0;
|
||||
while ((pos = childLower.indexOf(keyword, pos)) !== -1) {
|
||||
childOccurrences++;
|
||||
pos += keyword.length;
|
||||
}
|
||||
}
|
||||
|
||||
// If child doesn't have more keyword occurrences than parent, exclude it
|
||||
if (childOccurrences <= parentOccurrences) {
|
||||
shouldExclude = true;
|
||||
}
|
||||
```
|
||||
|
||||
This ensures that when searching for "abc":
|
||||
- "abc" is shown (1 occurrence)
|
||||
- "abc/xxx" is completely hidden (still 1 occurrence of "abc")
|
||||
- "abc/xxx/abc" would be shown (2 occurrences of "abc")
|
||||
|
||||
#### 4. Result Limiting
|
||||
|
||||
Results are sorted by final score and limited to the `maxResults` setting.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Out-of-Order Keywords (Issue #8)
|
||||
**Query:** "bar foo"
|
||||
|
||||
**Matches:**
|
||||
- ✅ `foo/bar/baz` - Both keywords present
|
||||
- ✅ `projects/foo/notes/bar` - Both keywords present
|
||||
- ❌ `foo/test` - Missing "bar" keyword
|
||||
- ❌ `bar/test` - Missing "foo" keyword
|
||||
|
||||
### Example 2: Parent-Child Prioritization (Issue #4)
|
||||
**Query:** "abc"
|
||||
|
||||
**Before (old behavior):**
|
||||
1. `abc` (score: 350)
|
||||
2. `abc/xxx` (score: 340)
|
||||
3. `abc/yyy` (score: 340)
|
||||
4. `abc/zzz` (score: 340)
|
||||
5. `projects/abc` (score: 150)
|
||||
|
||||
**After (new behavior):**
|
||||
1. `abc` (score: 350)
|
||||
2. `projects/abc` (score: 150)
|
||||
3. (Other non-related folders...)
|
||||
|
||||
Note: `abc/xxx`, `abc/yyy`, `abc/zzz` are completely excluded from results
|
||||
|
||||
### Example 3: Additional Keyword in Child
|
||||
**Query:** "abc test"
|
||||
|
||||
**Results:**
|
||||
1. `abc/test` (score: 450) - Matches both keywords, shown
|
||||
2. `abc` (score: 350) - Only matches "abc", shown
|
||||
3. `projects/abc/test` (score: 340) - Matches both, shown
|
||||
4. Other folders matching both keywords...
|
||||
|
||||
Note: `abc/test` is shown because it has 2 total keyword occurrences ("abc" + "test"), more than its parent `abc` which only has 1. However, `abc/notes` would be excluded because it still only has 1 occurrence of "abc", the same count as its parent.
|
||||
|
||||
### Example 4: Repeated Keywords
|
||||
**Query:** "ai prompt"
|
||||
|
||||
**Folder Structure:**
|
||||
- `AI/Prompt engineering` - Contains "ai" (1x) and "prompt" (1x) = 2 total occurrences
|
||||
- `AI/Prompt engineering/Prompt engineering` - Contains "ai" (1x) and "prompt" (2x) = 3 total occurrences
|
||||
- `AI/Prompt engineering/asset` - Contains "ai" (1x) and "prompt" (1x) = 2 total occurrences
|
||||
|
||||
**Results:**
|
||||
1. `AI/Prompt engineering` - Shown (2 occurrences)
|
||||
2. `AI/Prompt engineering/Prompt engineering` - Shown (3 occurrences, more than parent)
|
||||
3. `AI/Prompt engineering/asset` - Hidden (2 occurrences, same as parent)
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Type Safety
|
||||
- Added `FolderWithScore` interface for internal scoring
|
||||
- Maintains compatibility with Obsidian's `FuzzyMatch<TFolder>` type
|
||||
- Proper handling of separator items
|
||||
|
||||
### Debug Support
|
||||
- All search operations respect the `debugMode` setting
|
||||
- Logs query, keywords, and result counts when debug mode is enabled
|
||||
- Useful for troubleshooting search behavior
|
||||
|
||||
### Performance Considerations
|
||||
- Iterates through all folders once for scoring
|
||||
- Uses Set for efficient parent path lookups
|
||||
- Limits results to prevent UI overload
|
||||
- Complexity: O(n * k) where n = folders, k = keywords
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Potential enhancements for consideration:
|
||||
1. **Fuzzy character matching**: Allow typos and approximate matches
|
||||
2. **History-weighted scoring**: Boost scores for recently/frequently accessed folders
|
||||
3. **Custom weights**: Let users adjust scoring parameters
|
||||
4. **Regex support**: Enable advanced pattern matching
|
||||
5. **Path segment prioritization**: Give higher weight to matches in specific segments
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "folder-navigator",
|
||||
"name": "Folder Navigator",
|
||||
"version": "0.2.0",
|
||||
"version": "0.5.0",
|
||||
"minAppVersion": "1.5.3",
|
||||
"description": "Quickly navigate to folders in your vault using fuzzy search.",
|
||||
"author": "wenlzhang",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "plugin-template",
|
||||
"version": "0.2.0",
|
||||
"version": "0.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "plugin-template",
|
||||
"version": "0.2.0",
|
||||
"version": "0.5.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "folder-navigator",
|
||||
"version": "0.2.0",
|
||||
"version": "0.5.0",
|
||||
"description": "Quickly navigate to folders in your vault using fuzzy search.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,11 +1,35 @@
|
|||
import { App } from "obsidian";
|
||||
|
||||
export enum FolderDisplayMode {
|
||||
DEFAULT = "default",
|
||||
RECENCY = "recency",
|
||||
FREQUENCY = "frequency",
|
||||
}
|
||||
|
||||
export interface PluginSettings {
|
||||
maxResults: number;
|
||||
expandTargetFolder: boolean;
|
||||
debugMode: boolean;
|
||||
folderDisplayMode: FolderDisplayMode;
|
||||
recentFoldersToShow: number;
|
||||
frequentFoldersToShow: number;
|
||||
folderHistory: Record<
|
||||
string,
|
||||
{
|
||||
lastAccessed: number; // timestamp
|
||||
accessCount: number; // visit count
|
||||
}
|
||||
>;
|
||||
newFolderLocation: string; // Path where new folders should be created (empty string = root)
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: PluginSettings = {
|
||||
maxResults: 10,
|
||||
expandTargetFolder: true, // Enable by default for better navigation
|
||||
debugMode: false, // Disabled by default
|
||||
folderDisplayMode: FolderDisplayMode.DEFAULT,
|
||||
recentFoldersToShow: 5,
|
||||
frequentFoldersToShow: 5,
|
||||
folderHistory: {},
|
||||
newFolderLocation: "", // Empty string means root folder
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,171 @@
|
|||
import { App, PluginSettingTab, Setting } from "obsidian";
|
||||
import {
|
||||
App,
|
||||
PluginSettingTab,
|
||||
Setting,
|
||||
DropdownComponent,
|
||||
Modal,
|
||||
Notice,
|
||||
FuzzySuggestModal,
|
||||
TFolder,
|
||||
} from "obsidian";
|
||||
import FolderNavigatorPlugin from "./main";
|
||||
import { FolderDisplayMode } from "./settings";
|
||||
|
||||
/**
|
||||
* Modal for searching and selecting a folder for new folder creation location
|
||||
*/
|
||||
class FolderSelectionModal extends FuzzySuggestModal<TFolder> {
|
||||
plugin: FolderNavigatorPlugin;
|
||||
onSelect: (folder: TFolder) => void;
|
||||
|
||||
constructor(
|
||||
app: App,
|
||||
plugin: FolderNavigatorPlugin,
|
||||
onSelect: (folder: TFolder) => void,
|
||||
) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
this.onSelect = onSelect;
|
||||
this.setPlaceholder("Search for a folder...");
|
||||
}
|
||||
|
||||
getItems(): TFolder[] {
|
||||
return this.app.vault.getAllFolders();
|
||||
}
|
||||
|
||||
getItemText(folder: TFolder): string {
|
||||
return folder.path || "/";
|
||||
}
|
||||
|
||||
onChooseItem(folder: TFolder): void {
|
||||
this.onSelect(folder);
|
||||
}
|
||||
}
|
||||
|
||||
export class SettingsTab extends PluginSettingTab {
|
||||
plugin: FolderNavigatorPlugin;
|
||||
|
||||
// Keep track of settings elements for dynamic updates
|
||||
recentFoldersSettingEl: HTMLElement;
|
||||
frequentFoldersSettingEl: HTMLElement;
|
||||
resetHistorySettingEl: HTMLElement;
|
||||
|
||||
constructor(app: App, plugin: FolderNavigatorPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates visibility of settings based on the current display mode
|
||||
*/
|
||||
updateSettingsVisibility(displayMode: FolderDisplayMode): void {
|
||||
// Create settings elements if they don't exist yet
|
||||
if (
|
||||
!this.recentFoldersSettingEl ||
|
||||
!this.frequentFoldersSettingEl ||
|
||||
!this.resetHistorySettingEl
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle recent folders setting
|
||||
if (displayMode === FolderDisplayMode.RECENCY) {
|
||||
this.recentFoldersSettingEl.style.display = "block";
|
||||
this.frequentFoldersSettingEl.style.display = "none";
|
||||
} else if (displayMode === FolderDisplayMode.FREQUENCY) {
|
||||
this.recentFoldersSettingEl.style.display = "none";
|
||||
this.frequentFoldersSettingEl.style.display = "block";
|
||||
} else {
|
||||
// Default mode - hide both
|
||||
this.recentFoldersSettingEl.style.display = "none";
|
||||
this.frequentFoldersSettingEl.style.display = "none";
|
||||
}
|
||||
|
||||
// Only show reset history button for non-default modes
|
||||
if (displayMode === FolderDisplayMode.DEFAULT) {
|
||||
this.resetHistorySettingEl.style.display = "none";
|
||||
} else {
|
||||
this.resetHistorySettingEl.style.display = "block";
|
||||
}
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
containerEl.empty();
|
||||
|
||||
// Folder display
|
||||
new Setting(containerEl).setName("Folder display").setHeading();
|
||||
|
||||
// Display priority
|
||||
new Setting(containerEl)
|
||||
.setName("Display priority")
|
||||
.setDesc(
|
||||
"Control which folders appear at the top of the suggestion list before searching",
|
||||
)
|
||||
.addDropdown((dropdown: DropdownComponent) => {
|
||||
dropdown
|
||||
.addOption(
|
||||
FolderDisplayMode.DEFAULT,
|
||||
"Default order (alphabetical)",
|
||||
)
|
||||
.addOption(
|
||||
FolderDisplayMode.RECENCY,
|
||||
"Recently visited first",
|
||||
)
|
||||
.addOption(
|
||||
FolderDisplayMode.FREQUENCY,
|
||||
"Frequently visited first",
|
||||
)
|
||||
.setValue(this.plugin.settings.folderDisplayMode)
|
||||
.onChange(async (value: string) => {
|
||||
this.plugin.settings.folderDisplayMode =
|
||||
value as FolderDisplayMode;
|
||||
await this.plugin.saveSettings();
|
||||
|
||||
// Update the visibility of settings when selection changes
|
||||
this.updateSettingsVisibility(
|
||||
value as FolderDisplayMode,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Recent folders
|
||||
this.recentFoldersSettingEl = containerEl.createDiv();
|
||||
new Setting(this.recentFoldersSettingEl)
|
||||
.setName("Recent folders to show")
|
||||
.setDesc(
|
||||
"Number of recently visited folders to display at the top when using 'Recently visited first' mode",
|
||||
)
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(1, 20, 1)
|
||||
.setValue(this.plugin.settings.recentFoldersToShow)
|
||||
.setDynamicTooltip()
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.recentFoldersToShow = value;
|
||||
await this.plugin.saveSettings();
|
||||
}),
|
||||
);
|
||||
|
||||
// Frequent folders
|
||||
this.frequentFoldersSettingEl = containerEl.createDiv();
|
||||
new Setting(this.frequentFoldersSettingEl)
|
||||
.setName("Frequent folders to show")
|
||||
.setDesc(
|
||||
"Number of frequently visited folders to display at the top when using 'Frequently visited first' mode",
|
||||
)
|
||||
.addSlider((slider) =>
|
||||
slider
|
||||
.setLimits(1, 20, 1)
|
||||
.setValue(this.plugin.settings.frequentFoldersToShow)
|
||||
.setDynamicTooltip()
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.frequentFoldersToShow = value;
|
||||
await this.plugin.saveSettings();
|
||||
}),
|
||||
);
|
||||
|
||||
// Maximum results
|
||||
new Setting(containerEl)
|
||||
.setName("Maximum results")
|
||||
.setDesc("Maximum number of folders to show in search results")
|
||||
|
|
@ -27,6 +180,99 @@ export class SettingsTab extends PluginSettingTab {
|
|||
}),
|
||||
);
|
||||
|
||||
// Reset history
|
||||
this.resetHistorySettingEl = containerEl.createDiv();
|
||||
new Setting(this.resetHistorySettingEl)
|
||||
.setName("Reset folder history")
|
||||
.setDesc(
|
||||
"Clear the tracked history of visited folders. Warning: This will remove all recency and frequency data.",
|
||||
)
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Reset")
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
// Create a confirmation modal
|
||||
const confirmModal = new Modal(this.app);
|
||||
confirmModal.titleEl.setText("Reset folder history");
|
||||
confirmModal.contentEl.createEl("p", {
|
||||
text: "Are you sure? This will permanently delete all folder navigation history including recently visited and frequently used folder data.",
|
||||
cls: "mod-warning",
|
||||
});
|
||||
|
||||
const buttonContainer =
|
||||
confirmModal.contentEl.createDiv({
|
||||
cls: "modal-button-container",
|
||||
});
|
||||
|
||||
// Cancel button
|
||||
buttonContainer
|
||||
.createEl("button", {
|
||||
text: "Cancel",
|
||||
})
|
||||
.addEventListener("click", () => {
|
||||
confirmModal.close();
|
||||
});
|
||||
|
||||
// Confirm button
|
||||
const confirmButton = buttonContainer.createEl(
|
||||
"button",
|
||||
{
|
||||
text: "Reset folder history",
|
||||
cls: "mod-warning",
|
||||
},
|
||||
);
|
||||
confirmButton.addEventListener("click", async () => {
|
||||
this.plugin.settings.folderHistory = {};
|
||||
await this.plugin.saveSettings();
|
||||
confirmModal.close();
|
||||
new Notice("Folder history has been reset");
|
||||
});
|
||||
|
||||
confirmModal.open();
|
||||
}),
|
||||
);
|
||||
|
||||
// New folder location
|
||||
new Setting(containerEl).setName("Folder creation").setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Default location for new folders")
|
||||
.setDesc(
|
||||
"Choose where new folders will be created when using the 'Create folder' option. Default is the vault root.",
|
||||
)
|
||||
.addButton((button) => {
|
||||
const displayPath =
|
||||
this.plugin.settings.newFolderLocation || "/ (Root)";
|
||||
button.setButtonText(displayPath).onClick(() => {
|
||||
const modal = new FolderSelectionModal(
|
||||
this.app,
|
||||
this.plugin,
|
||||
async (folder: TFolder) => {
|
||||
this.plugin.settings.newFolderLocation =
|
||||
folder.path;
|
||||
await this.plugin.saveSettings();
|
||||
button.setButtonText(folder.path || "/ (Root)");
|
||||
},
|
||||
);
|
||||
modal.open();
|
||||
});
|
||||
})
|
||||
.addExtraButton((button) => {
|
||||
button
|
||||
.setIcon("reset")
|
||||
.setTooltip("Reset to root folder")
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.newFolderLocation = "";
|
||||
await this.plugin.saveSettings();
|
||||
// Update button text by recreating the setting
|
||||
this.display();
|
||||
});
|
||||
});
|
||||
|
||||
// Folder navigation
|
||||
new Setting(containerEl).setName("Folder navigation").setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Expand target folder on navigation")
|
||||
.setDesc(
|
||||
|
|
@ -40,5 +286,25 @@ export class SettingsTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}),
|
||||
);
|
||||
|
||||
// Initialize settings visibility based on current display mode
|
||||
this.updateSettingsVisibility(this.plugin.settings.folderDisplayMode);
|
||||
|
||||
// Add advanced section
|
||||
new Setting(containerEl).setName("Advanced").setHeading();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName("Debug mode")
|
||||
.setDesc(
|
||||
"Enable detailed logging to console for troubleshooting issues. This may affect performance and should be disabled during normal use.",
|
||||
)
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.debugMode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.debugMode = value;
|
||||
await this.plugin.saveSettings();
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
styles.css
31
styles.css
|
|
@ -3,3 +3,34 @@
|
|||
border-radius: 4px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.folder-separator {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85em;
|
||||
text-align: center;
|
||||
padding: 5px 0;
|
||||
font-style: italic;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
margin-bottom: 5px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.suggestion-highlight {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.folder-create-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
.folder-create-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.folder-create-text {
|
||||
font-style: italic;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
{
|
||||
"0.0.1": "0.0.1",
|
||||
"0.1.0": "1.0.0",
|
||||
"0.2.0": "1.5.3"
|
||||
"0.2.0": "1.5.3",
|
||||
"0.2.1": "1.5.3",
|
||||
"0.3.0": "1.5.3",
|
||||
"0.3.1": "1.5.3",
|
||||
"0.4.0": "1.5.3",
|
||||
"0.4.1": "1.5.3",
|
||||
"0.5.0": "1.5.3"
|
||||
}
|
||||
Loading…
Reference in a new issue