diff --git a/main.js b/main.js index 2328deb..7065b4e 100644 --- a/main.js +++ b/main.js @@ -44,6 +44,7 @@ var DEFAULT_SETTINGS = { localRepoRoot: "", scanDepth: 2, demoMode: false, + showLegend: true, sidebarSide: "right", selectedYear: new Date().getFullYear(), sizePreset: "medium", @@ -83,6 +84,13 @@ var PALETTES = { light: ["#f5f0e8", "#f5d5a0", "#e8820c", "#b45200", "#7a3200"] } }; +function debounce(fn, ms) { + let timer; + return (...args) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), ms); + }; +} async function requestDeviceCode() { let res; try { @@ -536,7 +544,8 @@ var ContributionsView = class extends import_obsidian.ItemView { const weeks = buildMonthDays(this.displayYear, this.displayMonth, days); this.renderMonthGrid(container, weeks); } - this.renderLegend(container); + if (this.plugin.settings.showLegend) + this.renderLegend(container); } catch (e) { container.empty(); this.renderError(container, e.message); @@ -933,10 +942,19 @@ var GitHubContributionsSettingTab = class extends import_obsidian.PluginSettingT if (!isDesktop) { containerEl.createEl("p", { cls: "gh-settings-notice", text: "\u26A0 Local git scanning is not available on mobile." }); } else { - new import_obsidian.Setting(containerEl).setName("Local repo root").setDesc("Folder to scan for git repositories").addText((t) => t.setPlaceholder("C:\\Users\\Peter\\Projects").setValue(this.plugin.settings.localRepoRoot).onChange(async (v) => { - this.plugin.settings.localRepoRoot = v.trim(); - await this.plugin.saveSettings(); - })); + new import_obsidian.Setting(containerEl).setName("Local repo root").setDesc("Folder to scan for git repositories").addText((t) => { + t.setPlaceholder("C:\\Users\\You\\Projects").setValue(this.plugin.settings.localRepoRoot); + t.onChange((v) => { + this.plugin.settings.localRepoRoot = v.trim(); + }); + }).addButton( + (btn) => btn.setButtonText("Scan").setTooltip("Save path and scan for repositories").onClick(async () => { + btn.setButtonText("Scanning\u2026").setDisabled(true); + await this.plugin.saveSettings(); + btn.setButtonText("Scan").setDisabled(false); + new import_obsidian.Notice("Repo scan complete \u2014 refresh the panel to see results"); + }) + ); new import_obsidian.Setting(containerEl).setName("Scan depth").setDesc("How many folder levels deep to search for git repos").addDropdown( (d) => d.addOption("2", "2 levels").addOption("3", "3 levels").addOption("4", "4 levels").addOption("5", "5 levels").addOption("0", "Unlimited (slow on large drives)").setValue(String(this.plugin.settings.scanDepth)).onChange(async (v) => { this.plugin.settings.scanDepth = parseInt(v); @@ -981,15 +999,25 @@ var GitHubContributionsSettingTab = class extends import_obsidian.PluginSettingT this.plugin.settings.demoMode = v; await this.plugin.saveSettings(); })); + new import_obsidian.Setting(containerEl).setName("Show legend").setDesc("Show the Less / More colour legend below the graph").addToggle((t) => t.setValue(this.plugin.settings.showLegend).onChange(async (v) => { + this.plugin.settings.showLegend = v; + await this.plugin.saveSettings(); + })); containerEl.createEl("h3", { text: "Daily Notes" }); - new import_obsidian.Setting(containerEl).setName("Daily notes folder").setDesc("Leave blank for vault root").addText((t) => t.setPlaceholder("Daily Notes/").setValue(this.plugin.settings.dailyNoteFolder).onChange(async (v) => { - this.plugin.settings.dailyNoteFolder = v; - await this.plugin.saveSettings(); - })); - new import_obsidian.Setting(containerEl).setName("Date format").setDesc("Moment.js format for filenames. Default: YYYY-MM-DD").addText((t) => t.setPlaceholder("YYYY-MM-DD").setValue(this.plugin.settings.dailyNoteDateFormat).onChange(async (v) => { - this.plugin.settings.dailyNoteDateFormat = v; - await this.plugin.saveSettings(); - })); + new import_obsidian.Setting(containerEl).setName("Daily notes folder").setDesc("Leave blank for vault root").addText((t) => { + t.setPlaceholder("Daily Notes/").setValue(this.plugin.settings.dailyNoteFolder); + t.onChange(debounce(async (v) => { + this.plugin.settings.dailyNoteFolder = v; + await this.plugin.saveSettings(); + }, 800)); + }); + new import_obsidian.Setting(containerEl).setName("Date format").setDesc("Moment.js format for filenames. Default: YYYY-MM-DD").addText((t) => { + t.setPlaceholder("YYYY-MM-DD").setValue(this.plugin.settings.dailyNoteDateFormat); + t.onChange(debounce(async (v) => { + this.plugin.settings.dailyNoteDateFormat = v; + await this.plugin.saveSettings(); + }, 800)); + }); } }; var GitHubContributionsPlugin = class extends import_obsidian.Plugin { diff --git a/media/compact-cobalt.png b/media/compact-cobalt.png new file mode 100644 index 0000000..a9bab84 Binary files /dev/null and b/media/compact-cobalt.png differ diff --git a/media/compact-ember.png b/media/compact-ember.png new file mode 100644 index 0000000..16f8c46 Binary files /dev/null and b/media/compact-ember.png differ diff --git a/media/compact-highcontrast.png b/media/compact-highcontrast.png new file mode 100644 index 0000000..d71d2a8 Binary files /dev/null and b/media/compact-highcontrast.png differ diff --git a/media/compact-neon.png b/media/compact-neon.png new file mode 100644 index 0000000..3eccfff Binary files /dev/null and b/media/compact-neon.png differ diff --git a/media/grid-cobalt.png b/media/grid-cobalt.png new file mode 100644 index 0000000..d64701e Binary files /dev/null and b/media/grid-cobalt.png differ diff --git a/media/grid-ember.png b/media/grid-ember.png new file mode 100644 index 0000000..82da437 Binary files /dev/null and b/media/grid-ember.png differ diff --git a/media/grid-highcontrast.png b/media/grid-highcontrast.png new file mode 100644 index 0000000..1bd54d6 Binary files /dev/null and b/media/grid-highcontrast.png differ diff --git a/media/grid-neon.png b/media/grid-neon.png new file mode 100644 index 0000000..5ed2ef5 Binary files /dev/null and b/media/grid-neon.png differ diff --git a/media/month-cobalt-grid.png b/media/month-cobalt-grid.png new file mode 100644 index 0000000..d612268 Binary files /dev/null and b/media/month-cobalt-grid.png differ diff --git a/media/month-ember-default.png b/media/month-ember-default.png new file mode 100644 index 0000000..803eb37 Binary files /dev/null and b/media/month-ember-default.png differ diff --git a/media/month-highcontrast-compact.png b/media/month-highcontrast-compact.png new file mode 100644 index 0000000..db77a8d Binary files /dev/null and b/media/month-highcontrast-compact.png differ diff --git a/media/screenshot-cobalt.png b/media/screenshot-cobalt.png new file mode 100644 index 0000000..ac93754 Binary files /dev/null and b/media/screenshot-cobalt.png differ diff --git a/screenshot-default.png b/media/screenshot-default.png similarity index 100% rename from screenshot-default.png rename to media/screenshot-default.png diff --git a/screenshot-ember.png b/media/screenshot-ember.png similarity index 100% rename from screenshot-ember.png rename to media/screenshot-ember.png diff --git a/media/screenshot-neon.png b/media/screenshot-neon.png new file mode 100644 index 0000000..7ef053a Binary files /dev/null and b/media/screenshot-neon.png differ diff --git a/media/ultracompact-neon.png b/media/ultracompact-neon.png new file mode 100644 index 0000000..81fa369 Binary files /dev/null and b/media/ultracompact-neon.png differ diff --git a/src/main.ts b/src/main.ts index 6b9436d..4de91e9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -62,6 +62,7 @@ interface GitHubContributionsSettings { palette: Palette; statsStyle: StatsStyle; demoMode: boolean; + showLegend: boolean; // Daily notes dailyNoteFolder: string; dailyNoteDateFormat: string; @@ -75,6 +76,7 @@ const DEFAULT_SETTINGS: GitHubContributionsSettings = { localRepoRoot: "", scanDepth: 2, demoMode: false, + showLegend: true, sidebarSide: "right", selectedYear: new Date().getFullYear(), sizePreset: "medium", @@ -126,6 +128,15 @@ const PALETTES: Record = { }, }; +// ── Helpers ────────────────────────────────────────────────────────────────── +function debounce void>(fn: T, ms: number): T { + let timer: ReturnType; + return ((...args: unknown[]) => { + clearTimeout(timer); + timer = setTimeout(() => fn(...args), ms); + }) as T; +} + // ── OAuth Device Flow ──────────────────────────────────────────────────────── interface DeviceCodeResponse { @@ -625,7 +636,7 @@ export class ContributionsView extends ItemView { this.renderMonthGrid(container, weeks); } - this.renderLegend(container); + if (this.plugin.settings.showLegend) this.renderLegend(container); } catch (e) { container.empty(); this.renderError(container, (e as Error).message); @@ -1085,8 +1096,21 @@ class GitHubContributionsSettingTab extends PluginSettingTab { new Setting(containerEl) .setName("Local repo root") .setDesc("Folder to scan for git repositories") - .addText(t => t.setPlaceholder("C:\\Users\\Peter\\Projects").setValue(this.plugin.settings.localRepoRoot) - .onChange(async v => { this.plugin.settings.localRepoRoot = v.trim(); await this.plugin.saveSettings(); })); + .addText(t => { + t.setPlaceholder("C:\\Users\\You\\Projects").setValue(this.plugin.settings.localRepoRoot); + // Store value in memory as user types but don't save/scan yet + t.onChange(v => { this.plugin.settings.localRepoRoot = v.trim(); }); + }) + .addButton(btn => btn + .setButtonText("Scan") + .setTooltip("Save path and scan for repositories") + .onClick(async () => { + btn.setButtonText("Scanning…").setDisabled(true); + await this.plugin.saveSettings(); + btn.setButtonText("Scan").setDisabled(false); + new Notice("Repo scan complete — refresh the panel to see results"); + }) + ); new Setting(containerEl) .setName("Scan depth") @@ -1172,19 +1196,29 @@ class GitHubContributionsSettingTab extends PluginSettingTab { .addToggle(t => t.setValue(this.plugin.settings.demoMode) .onChange(async v => { this.plugin.settings.demoMode = v; await this.plugin.saveSettings(); })); + new Setting(containerEl) + .setName("Show legend") + .setDesc("Show the Less / More colour legend below the graph") + .addToggle(t => t.setValue(this.plugin.settings.showLegend) + .onChange(async v => { this.plugin.settings.showLegend = v; await this.plugin.saveSettings(); })); + containerEl.createEl("h3", { text: "Daily Notes" }); new Setting(containerEl) .setName("Daily notes folder") .setDesc("Leave blank for vault root") - .addText(t => t.setPlaceholder("Daily Notes/").setValue(this.plugin.settings.dailyNoteFolder) - .onChange(async v => { this.plugin.settings.dailyNoteFolder = v; await this.plugin.saveSettings(); })); + .addText(t => { + t.setPlaceholder("Daily Notes/").setValue(this.plugin.settings.dailyNoteFolder); + t.onChange(debounce(async (v: unknown) => { this.plugin.settings.dailyNoteFolder = v as string; await this.plugin.saveSettings(); }, 800)); + }); new Setting(containerEl) .setName("Date format") .setDesc("Moment.js format for filenames. Default: YYYY-MM-DD") - .addText(t => t.setPlaceholder("YYYY-MM-DD").setValue(this.plugin.settings.dailyNoteDateFormat) - .onChange(async v => { this.plugin.settings.dailyNoteDateFormat = v; await this.plugin.saveSettings(); })); + .addText(t => { + t.setPlaceholder("YYYY-MM-DD").setValue(this.plugin.settings.dailyNoteDateFormat); + t.onChange(debounce(async (v: unknown) => { this.plugin.settings.dailyNoteDateFormat = v as string; await this.plugin.saveSettings(); }, 800)); + }); } }