2025-05-15 21:50:09 +00:00
|
|
|
|
import { Setting, TextAreaComponent } from 'obsidian';
|
|
|
|
|
|
import { CheckboxSyncPluginSettings } from 'src/types';
|
|
|
|
|
|
import { BaseSettingComponent } from 'src/ui/basedClasses/BaseSettingComponent';
|
|
|
|
|
|
import { ValidationError } from 'src/ui/validation/types';
|
|
|
|
|
|
|
2025-05-15 22:41:36 +00:00
|
|
|
|
|
2025-05-15 21:50:09 +00:00
|
|
|
|
export class PathGlobsSettingComponent extends BaseSettingComponent {
|
2025-05-15 22:41:36 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
|
|
const descHtml =
|
2025-05-18 15:21:50 +00:00
|
|
|
|
`<p>Define rules to specify which files and folders the plugin should IGNORE.
|
|
|
|
|
|
Uses the same pattern syntax as <code>.gitignore</code> files (powered by the 'ignore' library).</p>
|
|
|
|
|
|
<p>For detailed syntax, refer to the
|
|
|
|
|
|
<a href="https://git-scm.com/docs/gitignore" target="_blank" rel="noopener noreferrer">official .gitignore documentation</a>.
|
|
|
|
|
|
</p>`;
|
2025-05-15 22:41:36 +00:00
|
|
|
|
|
|
|
|
|
|
this.setting = new Setting(container)
|
2025-05-18 15:21:50 +00:00
|
|
|
|
.setName('File Ignore Rules (.gitignore style)')
|
2025-05-15 22:41:36 +00:00
|
|
|
|
// Используем createFragmentWithHTML для описания
|
|
|
|
|
|
.setDesc(this.createFragmentWithHTML(descHtml))
|
|
|
|
|
|
.addTextArea(text => {
|
|
|
|
|
|
this.textInput = text;
|
|
|
|
|
|
text.setPlaceholder(
|
2025-05-18 15:21:50 +00:00
|
|
|
|
'# Examples:\n' +
|
|
|
|
|
|
'# Ignore all files in the "Drafts" folder\n' +
|
|
|
|
|
|
'Drafts/\n\n'
|
2025-05-15 22:41:36 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
text.setValue(multilineStringValue);
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2025-05-15 21:50:09 +00:00
|
|
|
|
}
|