diff --git a/src/ui/basedClasses/BaseSettingComponent.ts b/src/ui/basedClasses/BaseSettingComponent.ts
index bdeed80..47a2341 100644
--- a/src/ui/basedClasses/BaseSettingComponent.ts
+++ b/src/ui/basedClasses/BaseSettingComponent.ts
@@ -25,4 +25,16 @@ export abstract class BaseSettingComponent implements ISettingComponent {
public setChangeListener(listener: () => void): void {
this.onChangeCallback = listener;
}
-}
\ No newline at end of file
+
+ /**
+ * Creates a DocumentFragment from an HTML string.
+ * Useful for setting complex descriptions with HTML content.
+ * @param html The HTML string.
+ * @returns A DocumentFragment containing the parsed HTML.
+ */
+ protected createFragmentWithHTML(html: string): DocumentFragment {
+ return createFragment((documentFragment) => {
+ documentFragment.createDiv().innerHTML = html;
+ });
+ }
+}
diff --git a/src/ui/components/pathGroup/PathGlobsSettingComponent.ts b/src/ui/components/pathGroup/PathGlobsSettingComponent.ts
index 0dbbf7b..3c317f1 100644
--- a/src/ui/components/pathGroup/PathGlobsSettingComponent.ts
+++ b/src/ui/components/pathGroup/PathGlobsSettingComponent.ts
@@ -3,89 +3,130 @@ 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;
+ private textInput: TextAreaComponent;
- getSettingKey(): keyof CheckboxSyncPluginSettings {
- return 'pathGlobs';
- }
+ 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 '';
- }
+ 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);
+ 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();
- });
- });
- }
+ const descHtml =
+ `
List files or folders that the plugin should IGNORE.
+ This uses "glob patterns" (similar to .gitignore syntax) to match paths.
+
+ - Each pattern on a new line. If this list is empty, no files are ignored (plugin processes all).
+ - Example to IGNORE:
My Folder/ or *.log
+ - Example to PROCESS an item within an IGNORED path:
!My Folder/My Important File.md
+ - The last rule that matches a file determines its fate.
+ - For more on glob syntax, search "glob patterns online" or see
+ Glob (programming) on Wikipedia.
+
+
`;
- 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.`);
- }
+ this.setting = new Setting(container)
+ .setName('Ignored Files & Folders')
+ // Используем createFragmentWithHTML для описания
+ .setDesc(this.createFragmentWithHTML(descHtml))
+ .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/**'
+ );
- 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.`);
- }
- }
+ text.setValue(multilineStringValue);
- validate(): ValidationError | null {
- try {
- this.getValueFromUi();
- } catch (e: any) {
- return { field: this.getSettingKey(), message: e.message };
- }
- return null;
- }
+ text.onChange(() => {
+ this.onChangeCallback();
+ });
+ });
+
+ // Применяем стиль "многострочной настройки" как в Tasks плагине
+ this.makeMultilineTextSetting();
+ }
+
+ /**
+ * Изменяет стиль Setting, чтобы описание было над TextArea, а TextArea занимала всю ширину.
+ */
+ private makeMultilineTextSetting(): void {
+ if (!this.setting) return;
+
+ const { settingEl, infoEl, controlEl } = this.setting;
+ const textAreaEl: HTMLTextAreaElement | null = controlEl.querySelector('textarea');
+
+ if (textAreaEl === null) {
+ // Это не настройка с TextArea, ничего не делаем
+ return;
+ }
+
+ settingEl.style.display = 'block';
+ // infoEl может не существовать, если .setName() и .setDesc() не вызывались или были очищены.
+ // В нашем случае они вызываются, так что infoEl должен быть.
+ if (infoEl) {
+ infoEl.style.marginBottom = 'var(--size-4-2)'; // Небольшой отступ под описанием
+ infoEl.style.marginRight = '0px'; // Убираем отступ справа, если он был
+ }
+
+ // Для controlEl (контейнер для textarea)
+ controlEl.style.width = '100%'; // Заставляем контейнер контрола быть на всю ширину
+
+ // Для самой textarea
+ textAreaEl.style.width = '100%'; // Занимает всю ширину controlEl
+ textAreaEl.style.minHeight = '120px'; // Минимальная высота для нескольких строк
+ textAreaEl.rows = 8;
+ }
+
+
+ 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;
+ }
+ 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;
+ }
}