caffa_Obsidian-Current-Fold.../main.ts
2025-03-11 15:32:03 +08:00

832 lines
28 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { App, Notice, Plugin, PluginSettingTab, Setting, TFile } from 'obsidian';
import { ItemView, WorkspaceLeaf } from "obsidian";
import { MarkdownView } from "obsidian";
import { TFolder } from "obsidian";
interface CurrentFolderNotesDisplaySettings {
excludeTitlesFilter: string;
includeTitleFilter: string;
prettyTitleCase: boolean;
includeSubfolderNotes: boolean;
includeCurrentFileOutline: boolean;
includeListFileOutlines: boolean;
displayMode: 'compact' | 'expanded';
styleMode: 'minimal' | 'fancy' | 'neobrutalist';
showNavigation: boolean;
biggerText: boolean; // Add this line
biggerTextMobileOnly: boolean; // Add this new setting
}
const DEFAULT_SETTINGS: Partial<CurrentFolderNotesDisplaySettings> = {
excludeTitlesFilter: '_index',
includeTitleFilter: '',
prettyTitleCase: true,
includeSubfolderNotes: false,
includeCurrentFileOutline: true,
includeListFileOutlines: false,
displayMode: 'expanded',
styleMode: 'fancy',
showNavigation: false,
biggerText: false,
biggerTextMobileOnly: false // Add default value
}
export default class CurrentFolderNotesDisplay extends Plugin {
settings: CurrentFolderNotesDisplaySettings;
public leaves: WorkspaceLeaf[] = [];
fileChangeHandler(file: TFile) {
if (file instanceof TFile) {
this.load();
}
}
async onload() {
await this.loadSettings();
// Load custom styles
this.loadStyles();
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new CurrentFolderNotesDisplaySettingTab(this.app, this));
// add a panel to the right sidebar - view
this.registerView(VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY, (leaf) => new CurrentFolderNotesDisplayView(leaf, this));
// Add a ribbon icon
this.addRibbonIcon('folder', 'Activate folder notes display', () => {
this.activateView();
});
// Add a command to open the view
this.addCommand({
id: 'activate-folder-notes-display',
name: 'Open Pane',
callback: () => {
this.activateView();
}
});
// when file is changes (opened) in the editor, update the view
this.registerEvent(this.app.workspace.on('file-open', async (file) => {
this.refreshView();
}));
// when a file is deleted, update the view
this.registerEvent(this.app.vault.on('delete', async (file) => {
this.refreshView();
}));
// when a file is created, update the view
this.registerEvent(this.app.vault.on('create', async (file) => {
this.refreshView();
}));
// when a file is renamed, update the view
this.registerEvent(this.app.vault.on('rename', async (file) => {
this.refreshView();
}));
}
async onunload() {
console.log('unloading plugin');
// Clean up by detaching any leaves created by this plugin
const leavesToDetach = this.app.workspace.getLeavesOfType(VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY);
leavesToDetach.forEach(leaf => {
leaf.detach();
});
// Clear the leaves array
this.leaves = [];
}
async activateView() {
const { workspace } = this.app;
// Clean up existing leaves first to prevent duplicates
const existingLeaves = workspace.getLeavesOfType(VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY);
// Keep only the first leaf if multiple exist
if (existingLeaves.length > 1) {
for (let i = 1; i < existingLeaves.length; i++) {
existingLeaves[i].detach();
}
}
let leaf: WorkspaceLeaf | null = null;
if (existingLeaves.length) {
// A leaf with our view already exists, use that
leaf = existingLeaves[0];
} else {
// Our view could not be found in the workspace, create a new leaf
// in the right sidebar for it
leaf = workspace.getRightLeaf(false);
if (leaf) {
await leaf.setViewState({ type: VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY, active: true });
// Only add to leaves array if it's a new leaf
this.leaves.push(leaf);
}
}
if (!leaf) {
new Notice('Could not create a new leaf for the view');
return;
}
workspace.revealLeaf(leaf);
}
async refreshView() {
const { workspace } = this.app;
// console.log("[CFN] Refresh view triggered");
const leaves = workspace.getLeavesOfType(VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY);
// console.log("[CFN] Found leaves:", leaves.length);
if (leaves.length > 1) {
// console.log("[CFN] Multiple leaves found, cleaning up extras");
for (let i = 1; i < leaves.length; i++) {
leaves[i].detach();
}
}
if (leaves.length === 1) {
// console.log("[CFN] Updating existing view");
const view = leaves[0].view as CurrentFolderNotesDisplayView;
await view.displayNotesInCurrentFolder();
} else if (leaves.length === 0) {
// console.log("[CFN] No view found, creating new one");
this.activateView();
}
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
// Add this method to load styles
loadStyles() {
// Load styles from the plugin's styles.css
const styleEl = document.createElement('style');
styleEl.id = 'current-folder-notes-styles';
document.head.appendChild(styleEl);
// Register the stylesheet to be removed when the plugin unloads
this.register(() => styleEl.remove());
// Load the CSS file from the plugin directory
this.loadData().then(data => {
// You can add default styles here if needed
const defaultStyles = `
.folder-notes-style { font-weight: 500; }
.folder-notes-style.current-file { font-weight: bold; }
.hover-style-file { text-decoration: underline; }
.hover-style-heading { font-style: italic; }
/* Add more default styles as needed */
`;
styleEl.textContent = defaultStyles;
});
}
}
export const VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY = "current-folder-notes-view";
export class CurrentFolderNotesDisplayView extends ItemView {
plugin: CurrentFolderNotesDisplay;
constructor(leaf: WorkspaceLeaf, plugin: CurrentFolderNotesDisplay) {
super(leaf);
this.plugin = plugin;
}
getViewType() {
return VIEW_TYPE_CURRENT_FOLDER_NOTES_DISPLAY;
}
getDisplayText() {
return "Current Folder Notes";
}
getIcon(): string {
return 'folder';
}
async onOpen() {
await this.displayNotesInCurrentFolder();
}
async onClose() {
// Remove this leaf from the plugin's leaves array to avoid memory leaks
const index = this.plugin.leaves.indexOf(this.leaf);
if (index > -1) {
this.plugin.leaves.splice(index, 1);
}
}
// main.ts
// Function to create clickable headings
createClickableHeadings(container: HTMLElement, currentFileContent: string, currentFilePath: string, addExtraHeadingCSS: boolean): void {
const headings: RegExpMatchArray | null = currentFileContent.match(/^(#+)\s+(.*)$/gm);
if (headings) {
headings.forEach((heading: string) => {
const headingLevelMatch: RegExpMatchArray | null = heading.match(/^(#+)/);
if (headingLevelMatch) {
const headingLevel: number = headingLevelMatch[0].length;
let headingText: string = heading.replace(/^(#+)\s+/, '');
// Use extractAlias to get the alias from the heading text
headingText = this.extractAlias(headingText);
// Add a right arrow symbol to the heading text
let headingLabel = '→ ' + headingText;
const p: HTMLElement = container.createEl('p', { text: headingLabel });
p.classList.add('basic-heading');
p.classList.add(`heading-level-${headingLevel}`);
if (addExtraHeadingCSS) {
p.classList.add('extra-heading-style');
}
p.addEventListener('click', async (event) => {
// Prevent default behavior
event.preventDefault();
// Use the openLinkText method to navigate to the heading
this.app.workspace.openLinkText('#' + headingText, currentFilePath);
// Clear any text selection
const selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
}
});
// Add hover effect
p.onmouseover = () => {
p.classList.add('hover-style-heading');
}
// Remove hover effect when not hovering
p.onmouseout = () => {
p.classList.remove('hover-style-heading');
}
}
});
}
}
// Function to extract alias from heading text
extractAlias(headingText: string): string {
const matches = headingText.match(/\[\[.*\|(.*?)\]\]/);
return matches ? matches[1] : headingText;
}
private getShortNoteName(basename: string): string {
// Try to find T numbers first (e.g., T1, T23)
const tMatch = basename.match(/T(\d+)/);
if (tMatch) return `T${tMatch[1]}`;
// Try to find Y numbers next (e.g., Y1, Y2023)
const yMatch = basename.match(/Y(\d+)/);
if (yMatch) return `Y${yMatch[1]}`;
// Try to find any numbers
const numberMatch = basename.match(/\d+/);
if (numberMatch) return numberMatch[0];
// Try to find text after dash
const dashMatch = basename.match(/-\s*(.+)$/);
if (dashMatch) return dashMatch[1];
// If nothing else matches, return the basename
return basename;
}
async displayNotesInCurrentFolder(): Promise<void> {
// console.log("[CFN] Starting displayNotesInCurrentFolder");
const container = this.containerEl.children[1] as HTMLElement;
container.empty();
// Get display settings
const displayMode = this.plugin.settings.displayMode;
const styleMode = this.plugin.settings.styleMode;
const showNavigation = this.plugin.settings.showNavigation;
// Create header
const headerContainer = container.createDiv({ cls: 'folder-view-header' });
headerContainer.createEl("h6", { text: "Current Folder Notes" });
// Get current file info and folder path
const activeFile = this.app.workspace.getActiveFile();
// console.log("[CFN] Active file:", activeFile?.path);
const currentFilePath = activeFile ? activeFile.path : '';
let parentFolderPath = ''; // Don't default to root
if (currentFilePath) {
const lastSlashIndex = currentFilePath.lastIndexOf('/');
parentFolderPath = lastSlashIndex > 0 ? currentFilePath.substring(0, lastSlashIndex) : '';
// console.log("[CFN] Parent folder path from current file:", parentFolderPath);
} else if (this.app.workspace.getActiveViewOfType(MarkdownView)) {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
// console.log("[CFN] Found active view:", activeView?.file?.path);
if (activeView?.file) {
const viewFilePath = activeView.file.path;
const lastSlashIndex = viewFilePath.lastIndexOf('/');
parentFolderPath = lastSlashIndex > 0 ? viewFilePath.substring(0, lastSlashIndex) : '';
// console.log("[CFN] Parent folder path from view:", parentFolderPath);
}
}
// Show warning if no valid folder path found
if (!parentFolderPath) {
const warningDiv = container.createDiv({ cls: 'empty-state-message' });
warningDiv.createEl('p', {
text: 'Please open a note in a folder to view related notes.',
cls: 'empty-state-highlight'
});
warningDiv.createEl('p', {
text: 'The root folder cannot be shown to prevent performance issues with large vaults.',
cls: 'empty-state-subtext'
});
return;
}
// Show current folder path
const pathDisplay = headerContainer.createEl('div', {
cls: 'folder-path-display',
text: parentFolderPath
});
pathDisplay.style.fontSize = 'var(--font-smaller)';
pathDisplay.style.color = 'var(--text-muted)';
pathDisplay.style.marginBottom = '10px';
pathDisplay.style.wordBreak = 'break-word';
// Get and filter files
let folder = this.app.vault.getAbstractFileByPath(parentFolderPath);
// console.log("[CFN] Found folder:", folder?.path);
let files: TFile[] = [];
if (folder instanceof TFolder) {
files = folder.children.filter((file: any) => file instanceof TFile) as TFile[];
// console.log("[CFN] Initial files count:", files.length);
}
files = this.applyFilters(files, parentFolderPath);
// console.log("[CFN] Files after filtering:", files.length);
// console.log("[CFN] Current filters - Include:", this.plugin.settings.includeTitleFilter, "Exclude:", this.plugin.settings.excludeTitlesFilter);
if (files.length === 0) {
this.showEmptyState(container, activeFile, currentFilePath, files);
return;
}
// Sort files by name/sequence
const sequenceWithPrefixOrLongest = (str: string) => {
const tMatches = str.match(/T(\d+)/);
if (tMatches) return parseInt(tMatches[1]);
const yMatches = str.match(/Y(\d+)/);
if (yMatches) return parseInt(yMatches[1]) + 1000;
const matches = str.match(/\d+/g) || [];
return Math.max(...matches.map(numStr => parseInt(numStr)), 0);
};
files.sort((a, b) => sequenceWithPrefixOrLongest(a.basename) - sequenceWithPrefixOrLongest(b.basename));
const currentFileIndex = files.findIndex(file => file.path === currentFilePath);
// Create main content container
const mainContent = container.createDiv({ cls: 'main-content' });
// Show navigation section if enabled and we have a current file
if (showNavigation && currentFileIndex !== -1) {
const navigationSection = mainContent.createDiv({ cls: 'navigation-section' });
// Navigation header with prev/next
const navHeader = navigationSection.createDiv({ cls: 'navigation-header' });
// Previous note
if (currentFileIndex > 0) {
const prevNote = files[currentFileIndex - 1];
const prevLink = navHeader.createDiv({ cls: 'nav-link prev-note' });
const prevIcon = prevLink.createSpan({ cls: 'nav-icon' });
prevIcon.innerHTML = `<svg viewBox="0 0 100 100" class="arrow-left" width="16" height="16"><path fill="currentColor" stroke="currentColor" d="M 60,20 L 30,50 L 60,80"></path></svg>`;
prevLink.createSpan({ cls: 'nav-direction', text: 'Previous' });
const prevTitle = prevLink.createSpan({
cls: 'nav-title',
text: this.getShortNoteName(prevNote.basename)
});
prevLink.setAttribute('aria-label', `Previous: ${prevNote.basename}`);
prevLink.addEventListener('click', () => {
this.app.workspace.openLinkText(prevNote.basename, parentFolderPath);
});
}
// Next note
if (currentFileIndex < files.length - 1) {
const nextNote = files[currentFileIndex + 1];
const nextLink = navHeader.createDiv({ cls: 'nav-link next-note' });
nextLink.createSpan({ cls: 'nav-direction', text: 'Next' });
const nextIcon = nextLink.createSpan({ cls: 'nav-icon' });
nextIcon.innerHTML = `<svg viewBox="0 0 100 100" class="arrow-right" width="16" height="16"><path fill="currentColor" stroke="currentColor" d="M 40,20 L 70,50 L 40,80"></path></svg>`;
const nextTitle = nextLink.createSpan({
cls: 'nav-title',
text: this.getShortNoteName(nextNote.basename)
});
nextLink.setAttribute('aria-label', `Next: ${nextNote.basename}`);
nextLink.addEventListener('click', () => {
this.app.workspace.openLinkText(nextNote.basename, parentFolderPath);
});
}
// Current note outline
if (this.plugin.settings.includeCurrentFileOutline) {
const currentFile = files[currentFileIndex];
const fileContent = await this.app.vault.read(currentFile);
if (fileContent) {
const outlineSection = navigationSection.createDiv({ cls: 'outline-section' });
// Add current note title
outlineSection.createEl('div', {
cls: 'current-note-title',
text: currentFile.basename
});
outlineSection.createEl('div', {
cls: 'folder-section-header',
text: 'CURRENT NOTE OUTLINE'
});
this.createClickableHeadings(outlineSection, fileContent, currentFile.path, true);
}
}
// Add separator
mainContent.createDiv({ cls: 'section-separator' });
}
// Always show flat list view
const listContainer = mainContent.createDiv({ cls: 'notes-flat-list' });
listContainer.createEl('div', {
cls: 'folder-section-header',
text: 'FOLDER NOTES'
});
for (const file of files) {
const isCurrentFile = file.path === currentFilePath;
const fileContainer = listContainer.createDiv({
cls: isCurrentFile ? 'file-container current' : 'file-container'
});
this.createFileLink(fileContainer, file, currentFilePath, parentFolderPath);
if (this.plugin.settings.includeListFileOutlines) {
const fileContent = await this.app.vault.read(file);
if (fileContent) {
this.createClickableHeadings(fileContainer, fileContent, file.path, isCurrentFile);
}
}
}
// Apply display classes
container.classList.toggle('compact-mode', displayMode === 'compact');
container.classList.toggle('expanded-mode', displayMode === 'expanded');
container.classList.toggle('minimal-style', styleMode === 'minimal');
container.classList.toggle('fancy-style', styleMode === 'fancy');
container.classList.toggle('neobrutalist-style', styleMode === 'neobrutalist');
const shouldApplyBiggerText = this.plugin.settings.biggerText &&
(!this.plugin.settings.biggerTextMobileOnly || (this.app as any).isMobile);
container.classList.toggle('bigger-text', shouldApplyBiggerText);
}
// Helper function to create file links with consistent styling
createFileLink(container: HTMLElement, file: any, currentFilePath: string, parentFolderPath: string): void {
const p = container.createEl('p');
const a = p.createEl('a', { text: file.basename });
if (this.plugin.settings.prettyTitleCase) {
a.innerText = a.innerText.replace(/\w\S*/g, function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
});
}
// Apply styling
a.className = 'folder-notes-style';
// If current file, add special styling
if (file.path === currentFilePath) {
a.className += ' current-file';
a.innerText = '\u2605 ' + a.innerText;
}
// Add hover effects
a.onmouseover = () => a.classList.add('hover-style-file');
a.onmouseout = () => a.classList.remove('hover-style-file');
// Add click handler to open the file
a.addEventListener('click', () => {
this.app.workspace.openLinkText(file.basename, parentFolderPath);
});
}
// Helper method to show empty state
private showEmptyState(container: HTMLElement, activeFile: TFile | null, currentFilePath: string, files: TFile[]): void {
const emptyStateDiv = container.createDiv({ cls: 'empty-state-message' });
if (files.length === 0) {
emptyStateDiv.createEl('p', {
text: 'No notes found. If you have recently added or moved notes, Obsidian might still be indexing them. Please wait a moment and try again.',
cls: 'empty-state-highlight'
});
}
if (this.plugin.settings.includeTitleFilter) {
emptyStateDiv.createEl('p', {
text: `🔍 No notes match the current filter "${this.plugin.settings.includeTitleFilter}".`,
cls: 'empty-state-highlight'
});
} else if (this.plugin.settings.excludeTitlesFilter) {
emptyStateDiv.createEl('p', {
text: `All notes are currently filtered out by the exclude pattern "${this.plugin.settings.excludeTitlesFilter}"`,
});
} else {
emptyStateDiv.createEl('p', {
text: 'This folder is empty',
});
}
if (currentFilePath && activeFile) {
emptyStateDiv.createEl('p', {
text: 'Showing outline of current note:',
cls: 'empty-state-subtext'
});
this.app.vault.read(activeFile).then(fileContent => {
if (fileContent) {
this.createClickableHeadings(container as HTMLElement, fileContent, currentFilePath, true);
}
});
}
}
private applyFilters(files: TFile[], parentFolderPath: string): TFile[] {
let filteredFiles = files;
// console.log("[CFN] Starting filter with files:", files.length);
// Apply subfolder filter
if (!this.plugin.settings.includeSubfolderNotes) {
const beforeCount = filteredFiles.length;
filteredFiles = filteredFiles.filter(file => !file.path.substring(parentFolderPath.length + 1).includes('/'));
// console.log("[CFN] After subfolder filter:", filteredFiles.length, "removed:", beforeCount - filteredFiles.length);
}
// Apply include filter
const includesFilter = this.plugin.settings.includeTitleFilter;
if (includesFilter && includesFilter.length > 0) {
const beforeCount = filteredFiles.length;
const includeWords = includesFilter.split(/[,\s]+/).map(word => word.trim().toLowerCase());
if (includeWords.length > 0) {
filteredFiles = filteredFiles.filter(file =>
includeWords.some(word => file.basename.toLowerCase().includes(word))
);
// console.log("[CFN] After include filter:", filteredFiles.length, "removed:", beforeCount - filteredFiles.length);
}
}
// Apply exclude filter
const excludeFilter = this.plugin.settings.excludeTitlesFilter;
if (excludeFilter && excludeFilter.length > 0) {
const beforeCount = filteredFiles.length;
const excludeWords = excludeFilter.split(/[,\s]+/).map(word => word.trim().toLowerCase());
if (excludeWords.length > 0) {
filteredFiles = filteredFiles.filter(file =>
!excludeWords.some(word => file.basename.toLowerCase().includes(word))
);
// console.log("[CFN] After exclude filter:", filteredFiles.length, "removed:", beforeCount - filteredFiles.length);
}
}
return filteredFiles;
}
}
class CurrentFolderNotesDisplaySettingTab extends PluginSettingTab {
plugin: CurrentFolderNotesDisplay;
constructor(app: App, plugin: CurrentFolderNotesDisplay) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
// Add some CSS for styling the settings
containerEl.createEl('style', {
text: `
.settings-section {
margin-bottom: 24px;
border-bottom: 1px solid var(--background-modifier-border);
padding-bottom: 16px;
}
.settings-section-header {
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
color: var(--text-normal);
}
.settings-section-description {
margin-bottom: 12px;
font-size: 13px;
color: var(--text-muted);
}
.setting-item {
border-top: none !important;
padding-top: 12px !important;
}
.setting-item-description {
font-size: 12px !important;
}
`
});
// Title section with plugin description
const titleEl = containerEl.createDiv({ cls: 'settings-section' });
titleEl.createEl('h2', { text: 'Current Folder Notes Settings' });
titleEl.createEl('p', {
cls: 'settings-section-description',
text: 'Configure how notes from the current folder are displayed in the panel.'
});
// Filter settings section
const filtersEl = containerEl.createDiv({ cls: 'settings-section' });
filtersEl.createEl('h3', { cls: 'settings-section-header', text: '📂 Filter Settings' });
filtersEl.createEl('p', {
cls: 'settings-section-description',
text: 'Control which notes appear in the folder notes panel.'
});
new Setting(filtersEl)
.setName('Exclude titles filter')
.setDesc('Notes containing these words will be hidden. Separate multiple words with commas.')
.addText(text => text
.setPlaceholder('_index, draft, template')
.setValue(this.plugin.settings.excludeTitlesFilter)
.onChange(async (value) => {
this.plugin.settings.excludeTitlesFilter = value;
await this.plugin.saveSettings();
}));
new Setting(filtersEl)
.setName('Include titles filter')
.setDesc('Only notes containing these words will be shown. Leave empty to show all notes. Separate multiple words with commas.')
.addText(text => text
.setPlaceholder('chapter, section, lesson')
.setValue(this.plugin.settings.includeTitleFilter)
.onChange(async (value) => {
this.plugin.settings.includeTitleFilter = value;
await this.plugin.saveSettings();
}));
// Display options section
const displayEl = containerEl.createDiv({ cls: 'settings-section' });
displayEl.createEl('h3', { cls: 'settings-section-header', text: '🖥️ Display Options' });
displayEl.createEl('p', {
cls: 'settings-section-description',
text: 'Control how notes and their contents are displayed in the panel.'
});
new Setting(displayEl)
.setName('Show navigation')
.setDesc('Show navigation links for previous and next notes.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.showNavigation)
.onChange(async (value) => {
this.plugin.settings.showNavigation = value;
await this.plugin.saveSettings();
this.plugin.refreshView();
}));
new Setting(displayEl)
.setName('Pretty title case')
.setDesc('Convert note titles to Title Case for better readability.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.prettyTitleCase)
.onChange(async (value) => {
this.plugin.settings.prettyTitleCase = value;
await this.plugin.saveSettings();
}));
new Setting(displayEl)
.setName('Display mode')
.setDesc('Choose between compact and expanded display modes.')
.addDropdown(dropdown => dropdown
.addOption('compact', 'Compact')
.addOption('expanded', 'Expanded')
.setValue(this.plugin.settings.displayMode)
.onChange(async (value) => {
this.plugin.settings.displayMode = value as 'compact' | 'expanded';
await this.plugin.saveSettings();
}));
new Setting(displayEl)
.setName('Style mode')
.setDesc('Choose between minimal, fancy, and neobrutalist styles.')
.addDropdown(dropdown => dropdown
.addOption('minimal', 'Minimal')
.addOption('fancy', 'Fancy')
.addOption('neobrutalist', 'Neobrutalist')
.setValue(this.plugin.settings.styleMode)
.onChange(async (value) => {
this.plugin.settings.styleMode = value as 'minimal' | 'fancy' | 'neobrutalist';
await this.plugin.saveSettings();
this.plugin.refreshView();
}));
new Setting(displayEl)
.setName('Bigger text')
.setDesc('Make all the text bigger for easier clicking.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.biggerText)
.onChange(async (value) => {
this.plugin.settings.biggerText = value;
await this.plugin.saveSettings();
this.plugin.refreshView();
}));
// Add this after the existing biggerText setting
new Setting(displayEl)
.setName('Mobile-only bigger text')
.setDesc('Only apply bigger text when using Obsidian on mobile devices.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.biggerTextMobileOnly)
.onChange(async (value) => {
this.plugin.settings.biggerTextMobileOnly = value;
await this.plugin.saveSettings();
this.plugin.refreshView();
}));
// Content options section
const contentEl = containerEl.createDiv({ cls: 'settings-section' });
contentEl.createEl('h3', { cls: 'settings-section-header', text: '📝 Content Options' });
contentEl.createEl('p', {
cls: 'settings-section-description',
text: 'Configure what content is included in the folder notes panel.'
});
new Setting(contentEl)
.setName('Include subfolder notes')
.setDesc('Show notes from subfolders within the current folder.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.includeSubfolderNotes)
.onChange(async (value) => {
this.plugin.settings.includeSubfolderNotes = value;
await this.plugin.saveSettings();
}));
new Setting(contentEl)
.setName('Show outline in current file section')
.setDesc('Display headings from the currently active file at the top of the panel.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.includeCurrentFileOutline)
.onChange(async (value) => {
this.plugin.settings.includeCurrentFileOutline = value;
await this.plugin.saveSettings();
}));
new Setting(contentEl)
.setName('Show outline in files list')
.setDesc('Display headings under each file in the folder notes list.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.includeListFileOutlines)
.onChange(async (value) => {
this.plugin.settings.includeListFileOutlines = value;
await this.plugin.saveSettings();
}));
// About section with help text
const aboutEl = containerEl.createDiv({ cls: 'settings-section' });
aboutEl.createEl('h3', { cls: 'settings-section-header', text: ' About' });
aboutEl.createEl('p', {
cls: 'settings-section-description',
text: 'Current Folder Notes displays notes and outlines from the current folder for quick navigation. Changes to settings will apply immediately.'
});
// Add a refresh button to explicitly refresh the view
new Setting(aboutEl)
.setName('Refresh panel')
.setDesc('Manually refresh the Current Folder Notes panel to apply changes.')
.addButton(button => button
.setButtonText('Refresh Now')
.setCta()
.onClick(() => {
this.plugin.refreshView();
new Notice('Current Folder Notes panel refreshed');
}));
}
}