feat(settings): Add UI for file filtering glob patterns

This commit is contained in:
Grol Grol 2025-05-16 00:50:09 +03:00
parent a69f23159a
commit f3f0685bdc
2 changed files with 101 additions and 0 deletions

View file

@ -16,6 +16,7 @@ import { ConfirmModal, InfoModal, SaveConfirmModal } from "./modals";
import { SettingsValidator } from "./validation/SettingsValidator";
import { ValidationError } from "./validation/types";
import { EnableConsoleLogSettingComponent } from "./components/devGroup/EnableDebug";
import { PathGlobsSettingComponent } from "./components/pathGroup/PathGlobsSettingComponent";
export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
plugin: CheckboxSyncPlugin;
@ -59,6 +60,14 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
[parentToggleComp, childrenToggleComp, automaticFileSyncToggleComp]
);
const pathGlobsComp = new PathGlobsSettingComponent();
const filteringGroup = new SettingGroup(
"File Scope & Filtering",
[pathGlobsComp],
"Define rules to include or exclude files/folders from plugin processing."
);
const consoleLog = new EnableConsoleLogSettingComponent();
const devGroup = new SettingGroup(
@ -70,6 +79,7 @@ export class CheckboxSyncPluginSettingTab extends PluginSettingTab {
this.settingGroups = [
symbolGroup,
behaviorGroup,
filteringGroup,
devGroup
];

View file

@ -0,0 +1,91 @@
import { Setting, TextAreaComponent } from 'obsidian';
import { CheckboxSyncPluginSettings } from 'src/types';
import { BaseSettingComponent } from 'src/ui/basedClasses/BaseSettingComponent';
import { ValidationError } from 'src/ui/validation/types';
export class PathGlobsSettingComponent extends BaseSettingComponent {
private textInput: TextAreaComponent;
getSettingKey(): keyof CheckboxSyncPluginSettings {
return 'pathGlobs';
}
private arrayToMultilineString(value: string[] | undefined | null): string {
if (Array.isArray(value)) {
try {
return value.join('\n');
} catch (e) {
console.error(`[${this.getSettingKey()}] Error joining array to multiline string: `, e);
return '';
}
}
return '';
}
render(container: HTMLElement, currentValue: any): void {
const multilineStringValue = this.arrayToMultilineString(currentValue as string[] | undefined);
this.setting = new Setting(container)
.setName('Ignored Files & Folders')
.setDesc('List files or folders that the plugin should IGNORE. ' +
'This uses "glob patterns" (similar to .gitignore syntax) to match paths.\n' +
'- Each pattern on a new line. If this list is empty, no files are ignored (plugin processes all).\n' +
'- Example to IGNORE: `My Folder/` or `*.log`\n' +
'- Example to PROCESS an item within an IGNORED path: `!My Folder/My Important File.md`\n' +
'- The last rule that matches a file determines its fate.'
)
.addTextArea(text => {
this.textInput = text;
text.setPlaceholder(
'# Lines starting with # are comments and will be ignored\n' +
'Drafts/\n' +
'*.tmp\n' +
'!Drafts/ReadyToPublish.md\n\n' +
'# To process ONLY files in "Projects" folder:\n' +
'*\n' +
'!Projects/**'
);
text.inputEl.setAttr('rows', 10); // Увеличил немного для плейсхолдера
text.inputEl.style.width = '100%';
text.setValue(multilineStringValue);
text.onChange(() => {
this.onChangeCallback();
});
});
}
getValueFromUi(): string[] {
if (this.textInput) {
const rawValue = this.textInput.getValue();
return rawValue
.split('\n')
.map(s => s.trim())
.filter(s => s.length > 0 && !s.startsWith('#')); // Игнорируем пустые строки и комментарии
}
throw new Error(`[${this.getSettingKey()}] Cannot get value from UI: TextArea component not rendered or available.`);
}
setValueInUi(value: any): void {
if (this.textInput) {
if (value === undefined) {
this.textInput.setValue('');
return;
}
// Ожидаем, что value это string[]. Если нет, будет ошибка времени выполнения при .join()
this.textInput.setValue((value as string[]).join('\n'));
} else {
throw new Error(`[${this.getSettingKey()}] Cannot set value in UI: TextArea component not rendered or available.`);
}
}
validate(): ValidationError | null {
try {
this.getValueFromUi();
} catch (e: any) {
return { field: this.getSettingKey(), message: e.message };
}
return null;
}
}