feat: add dynamic descriptions to settings reflecting current state
Eight settings now show live contextual info (file counts, entry usage, folder existence) that updates as values change. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
94c2822340
commit
72f64bcbd9
2 changed files with 78 additions and 17 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Notice, PluginSettingTab, Setting } from 'obsidian';
|
||||
import { App, Notice, PluginSettingTab, Setting, TFile, TFolder } from 'obsidian';
|
||||
import ClaudeContextPlugin from './main';
|
||||
import { ContextSource, getSourceIcon, SourceRegistry } from './sources';
|
||||
import { SourceModal } from './source-modal';
|
||||
|
|
@ -238,19 +238,54 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
}
|
||||
}
|
||||
|
||||
private setDynamicDesc(setting: Setting, base: string, dynamic: string): void {
|
||||
setting.descEl.empty();
|
||||
setting.descEl.appendText(base);
|
||||
if (dynamic) {
|
||||
setting.descEl.createSpan({ text: ` (${dynamic})`, cls: 'cc-dynamic-desc' });
|
||||
}
|
||||
}
|
||||
|
||||
private describeContextFolder(setting: Setting, path: string): void {
|
||||
const folder = this.app.vault.getAbstractFileByPath(path);
|
||||
let count = 0;
|
||||
if (folder instanceof TFolder) {
|
||||
for (const child of folder.children) {
|
||||
if (child instanceof TFile && child.extension === 'md') count++;
|
||||
}
|
||||
}
|
||||
this.setDynamicDesc(setting, 'Folder containing your context files',
|
||||
count > 0 ? `${count} .md file${count !== 1 ? 's' : ''}` : '');
|
||||
}
|
||||
|
||||
private describeOutputFolder(setting: Setting, path: string): void {
|
||||
const exists = this.app.vault.getAbstractFileByPath(path) instanceof TFolder;
|
||||
this.setDynamicDesc(setting, 'Folder for secondary target files',
|
||||
exists ? 'folder exists' : '');
|
||||
}
|
||||
|
||||
private countHistoryEntries(): number {
|
||||
const folder = this.app.vault.getAbstractFileByPath(this.plugin.settings.history.storageFolder);
|
||||
if (folder instanceof TFolder) {
|
||||
return folder.children.filter(c => c instanceof TFile).length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// === TAB: General ===
|
||||
|
||||
private renderGeneralTab(el: HTMLElement) {
|
||||
new Setting(el)
|
||||
const contextFolderSetting = new Setting(el)
|
||||
.setName('Context folder')
|
||||
.setDesc('Folder containing your context files')
|
||||
.addText(text => text
|
||||
.setPlaceholder('_claude')
|
||||
.setValue(this.plugin.settings.contextFolder)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.contextFolder = value;
|
||||
await this.plugin.saveSettings();
|
||||
this.describeContextFolder(contextFolderSetting, value);
|
||||
}));
|
||||
this.describeContextFolder(contextFolderSetting, this.plugin.settings.contextFolder);
|
||||
|
||||
new Setting(el)
|
||||
.setName('Separator')
|
||||
|
|
@ -293,9 +328,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(el)
|
||||
const excludedSetting = new Setting(el)
|
||||
.setName('Excluded files')
|
||||
.setDesc('Comma-separated filenames to exclude (e.g. "examples.md, drafts.md")')
|
||||
.addText(text => text
|
||||
.setPlaceholder('file1.md, file2.md')
|
||||
.setValue(this.plugin.settings.excludedFiles.join(', '))
|
||||
|
|
@ -305,7 +339,13 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
.map(s => s.trim())
|
||||
.filter(s => s.length > 0);
|
||||
await this.plugin.saveSettings();
|
||||
const n = this.plugin.settings.excludedFiles.length;
|
||||
this.setDynamicDesc(excludedSetting, 'Comma-separated filenames to exclude',
|
||||
n > 0 ? `${n} excluded` : '');
|
||||
}));
|
||||
const excludedCount = this.plugin.settings.excludedFiles.length;
|
||||
this.setDynamicDesc(excludedSetting, 'Comma-separated filenames to exclude',
|
||||
excludedCount > 0 ? `${excludedCount} excluded` : '');
|
||||
}
|
||||
|
||||
// === TAB: Sources ===
|
||||
|
|
@ -341,15 +381,17 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
// Section: Options
|
||||
const optionsSection = new CollapsibleSection(el, 'sources-options', 'Options', this.plugin);
|
||||
|
||||
new Setting(optionsSection.contentEl)
|
||||
const sourceLabelsSetting = new Setting(optionsSection.contentEl)
|
||||
.setName('Show source labels')
|
||||
.setDesc('Add position and name labels to source output')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showSourceLabels)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showSourceLabels = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
const enabledSources = this.plugin.settings.sources.filter(s => s.enabled).length;
|
||||
this.setDynamicDesc(sourceLabelsSetting, 'Add position and name labels to source output',
|
||||
`${enabledSources} source${enabledSources !== 1 ? 's' : ''} enabled`);
|
||||
}
|
||||
|
||||
// === TAB: Templates ===
|
||||
|
|
@ -408,9 +450,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
// Section: Defaults
|
||||
const defaultsSection = new CollapsibleSection(el, 'templates-defaults', 'Defaults', this.plugin);
|
||||
|
||||
new Setting(defaultsSection.contentEl)
|
||||
const defaultTemplateSetting = new Setting(defaultsSection.contentEl)
|
||||
.setName('Default template')
|
||||
.setDesc('Template to use by default when copying context')
|
||||
.addDropdown(dropdown => {
|
||||
dropdown.addOption('', 'None (plain context)');
|
||||
for (const template of this.plugin.settings.promptTemplates) {
|
||||
|
|
@ -422,6 +463,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
this.setDynamicDesc(defaultTemplateSetting, 'Template to use by default when copying context',
|
||||
`${this.plugin.settings.promptTemplates.length} available`);
|
||||
}
|
||||
|
||||
// === TAB: Output ===
|
||||
|
|
@ -463,9 +506,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
const settingsSection = new CollapsibleSection(el, 'output-settings', 'Output settings', this.plugin);
|
||||
const oc = settingsSection.contentEl;
|
||||
|
||||
new Setting(oc)
|
||||
const primaryTargetSetting = new Setting(oc)
|
||||
.setName('Primary target')
|
||||
.setDesc('This target\'s output is copied to clipboard')
|
||||
.addDropdown(dropdown => {
|
||||
dropdown.addOption('', 'First enabled target');
|
||||
for (const target of this.plugin.settings.targets) {
|
||||
|
|
@ -477,17 +519,22 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
});
|
||||
});
|
||||
const enabledTargets = this.plugin.settings.targets.filter(t => t.enabled).length;
|
||||
const totalTargets = this.plugin.settings.targets.length;
|
||||
this.setDynamicDesc(primaryTargetSetting, 'This target\'s output is copied to clipboard',
|
||||
totalTargets > 0 ? `${enabledTargets} of ${totalTargets} enabled` : '');
|
||||
|
||||
new Setting(oc)
|
||||
const outputFolderSetting = new Setting(oc)
|
||||
.setName('Output folder')
|
||||
.setDesc('Folder for secondary target files')
|
||||
.addText(text => text
|
||||
.setPlaceholder('_claude/outputs')
|
||||
.setValue(this.plugin.settings.targetOutputFolder)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.targetOutputFolder = value || '_claude/outputs';
|
||||
await this.plugin.saveSettings();
|
||||
this.describeOutputFolder(outputFolderSetting, this.plugin.settings.targetOutputFolder);
|
||||
}));
|
||||
this.describeOutputFolder(outputFolderSetting, this.plugin.settings.targetOutputFolder);
|
||||
}
|
||||
|
||||
// === TAB: History ===
|
||||
|
|
@ -499,9 +546,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
});
|
||||
desc.style.marginBottom = '10px';
|
||||
|
||||
new Setting(el)
|
||||
const enableHistorySetting = new Setting(el)
|
||||
.setName('Enable history')
|
||||
.setDesc('Save generated contexts for later review and comparison')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.history.enabled)
|
||||
.onChange(async (value) => {
|
||||
|
|
@ -509,6 +555,9 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
}));
|
||||
const entryCount = this.countHistoryEntries();
|
||||
this.setDynamicDesc(enableHistorySetting, 'Save generated contexts for later review and comparison',
|
||||
this.plugin.settings.history.enabled && entryCount > 0 ? `${entryCount} entries stored` : '');
|
||||
|
||||
if (this.plugin.settings.history.enabled) {
|
||||
new Setting(el)
|
||||
|
|
@ -522,9 +571,8 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(el)
|
||||
const maxEntriesSetting = new Setting(el)
|
||||
.setName('Maximum entries')
|
||||
.setDesc('Oldest entries will be deleted when limit is exceeded')
|
||||
.addText(text => text
|
||||
.setPlaceholder('50')
|
||||
.setValue(String(this.plugin.settings.history.maxEntries))
|
||||
|
|
@ -533,8 +581,17 @@ export class ClaudeContextSettingTab extends PluginSettingTab {
|
|||
if (!isNaN(num) && num > 0) {
|
||||
this.plugin.settings.history.maxEntries = num;
|
||||
await this.plugin.saveSettings();
|
||||
const used = this.countHistoryEntries();
|
||||
this.setDynamicDesc(maxEntriesSetting, 'Oldest entries will be deleted when limit is exceeded',
|
||||
`${used} of ${num} used`);
|
||||
}
|
||||
}));
|
||||
{
|
||||
const used = this.countHistoryEntries();
|
||||
const max = this.plugin.settings.history.maxEntries;
|
||||
this.setDynamicDesc(maxEntriesSetting, 'Oldest entries will be deleted when limit is exceeded',
|
||||
`${used} of ${max} used`);
|
||||
}
|
||||
|
||||
new Setting(el)
|
||||
.setName('Auto-cleanup (days)')
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@
|
|||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.cc-dynamic-desc {
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
/* Tab navigation for settings */
|
||||
.cc-settings-tabs {
|
||||
display: flex;
|
||||
|
|
|
|||
Loading…
Reference in a new issue