Skip to content

Commit a7942f1

Browse files
authored
Optimize based on review comments #5
2 parents 7763f7f + 4a1994f commit a7942f1

13 files changed

Lines changed: 1797 additions & 988 deletions

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2025 Hunter Ji
3+
Copyright (c) 2026 Hunter Ji
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Track progress toward a single goal directly inside one file—just insert the r
2020

2121
1. Install and enable **Easy Tracker** in **Settings → Community plugins**.
2222
2. Open any note you want to use for tracking.
23-
3. Run the command palette and select `Insert Check-in Component` to add all modules at once.
23+
3. Run the command palette and select `Insert check-in component` to add all modules at once.
2424
4. Click a button each day to record your effort—no extra setup required.
2525
5. Optionally, use commands to insert or remove individual modules.
2626

@@ -37,6 +37,6 @@ All data is stored as plain text in your note—easy to search, sync, or edit.
3737

3838
- Edit button labels/values by changing the text inside the block.
3939
- Move or remove modules as you wish.
40-
- Adjust language and week start in **Settings → Easy Tracker**.
40+
- Adjust week start in **Settings → Easy Tracker**.
4141

4242
No complex setup—just insert, click, and track.

README.zh-CN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
1.**设置 → 社区插件** 启用 **Easy Tracker**
2222
2. 打开你想追踪目标的笔记。
23-
3. 通过命令面板选择 `Insert Check-in Component`,一键插入所有模块。
23+
3. 通过命令面板选择 `插入打卡组件`,一键插入所有模块。
2424
4. 每天点击按钮完成打卡,无需额外设置。
2525
5. 也可按需插入或删除单个模块。
2626

@@ -37,6 +37,6 @@
3737

3838
- 编辑按钮块内容即可自定义标签和数值。
3939
- 模块可自由移动、增删。
40-
-**设置 → Easy Tracker** 中切换语言和周起始日
40+
-**设置 → Easy Tracker** 中切换周起始日
4141

4242
无需复杂设置,插入即用,轻松追踪你的目标。

calendar-heatmap/index.js

Lines changed: 543 additions & 1 deletion
Large diffs are not rendered by default.

daily-overview.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ export const computeDailyOverview = (entries: Entry[]): DailyOverview => {
6868
return { hasEntries, hasToday, streak, lastMissing };
6969
}
7070

71-
const buildDailyOverview = (container: HTMLElement, overview: DailyOverview, t: Translator): void => {
72-
container.empty();
71+
export const buildDailyOverview = (container: HTMLElement, overview: DailyOverview, t: Translator): void => {
7372
container.addClass('easy-tracker-card');
7473
container.setAttr('id', 'easy-tracker-daily-overview');
7574
container.createEl('div', { cls: 'easy-tracker-card-title', text: t('overview.title') });

locales.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ const en = {
77
'notice.editorUnavailable': 'Editor instance not available',
88
'notice.onlyCheckInInEditMode': 'Please switch to edit mode to check in.',
99
'notice.checkInTooFast': 'You are checking in too frequently. Please wait a moment before trying again.',
10-
'card.goalTitle': 'My Goal',
10+
'card.goalTitle': 'My goal',
1111
'card.goalPlaceholder': 'Define your goal here!',
12-
'card.activityHistoryTitle': 'Activity History',
12+
'card.activityHistoryTitle': 'Activity history',
1313
'card.buttonsTitle': 'How did you do today?',
1414
'card.checkInCongrats': 'Another day done, you\'re making progress! 🎉',
1515
'card.defaultButton': 'Button',
1616
'command.insertCalendarHeatmap': 'Insert calendar heatmap',
17-
'command.insertCheckInComponent': 'Insert Check-in Component',
18-
'command.insertSingleCheckInComponent': 'Insert Single Check-in Component',
19-
'command.insertDailyOverview': 'Insert Daily Overview',
20-
'command.insertMyGoal': 'Insert My Goal',
17+
'command.insertCheckInComponent': 'Insert check-in component',
18+
'command.insertSingleCheckInComponent': 'Insert single check-in component',
19+
'command.insertDailyOverview': 'Insert daily overview',
20+
'command.insertMyGoal': 'Insert my goal',
2121
'snippet.justABit': 'Just a bit',
2222
'snippet.gotItDone': 'Got it done',
2323
'snippet.didExtra': 'Did extra',
@@ -26,11 +26,6 @@ const en = {
2626
'setting.weekStartDescription': 'Choose the first day of the week used by the calendar',
2727
'setting.weekStart.monday': 'Monday',
2828
'setting.weekStart.sunday': 'Sunday',
29-
'setting.languageName': 'Language',
30-
'setting.languageDescription': 'Choose how the plugin language is determined',
31-
'setting.languageOption.system': 'Follow Obsidian language',
32-
'setting.languageOption.en': 'English',
33-
'setting.languageOption.zhCN': 'Simplified Chinese',
3429
'overview.title': "Today's overview",
3530
'overview.statusLabel': "Today's status",
3631
'overview.statusValue.checkedIn': 'Checked in',
@@ -75,11 +70,6 @@ const zhCN: Record<LocaleKey, string> = {
7570
'setting.weekStartDescription': '选择热力图中的每周起始日',
7671
'setting.weekStart.monday': '周一',
7772
'setting.weekStart.sunday': '周日',
78-
'setting.languageName': '界面语言',
79-
'setting.languageDescription': '选择插件语言的来源',
80-
'setting.languageOption.system': '跟随 Obsidian 语言',
81-
'setting.languageOption.en': '英文',
82-
'setting.languageOption.zhCN': '简体中文',
8373
'overview.title': '今日概览',
8474
'overview.statusLabel': '今日状态',
8575
'overview.statusValue.checkedIn': '已打卡',
@@ -136,7 +126,3 @@ export const resolveLocale = (preference: LanguageSetting, systemLocale?: string
136126
}
137127
return preference;
138128
};
139-
140-
export const isLanguageSetting = (value: unknown): value is LanguageSetting => {
141-
return value === 'system' || value === 'en' || value === 'zh-CN';
142-
};

main.ts

Lines changed: 28 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
import { App, ButtonComponent, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting, getLanguage } from 'obsidian';
2-
import CalendarHeatmap, { CalendarHeatmapOptions } from './calendar-heatmap/index.js';
3-
import { hasTodayEntry, insertTodayEntry, parseEntries } from './utils';
4-
import { computeDailyOverview, renderDailyOverview, updateDailyOverview } from './daily-overview';
5-
import { createTranslator, isLanguageSetting, LanguageSetting, LocaleCode, LocaleKey, resolveLocale, Translator } from './locales';
1+
import { Editor, MarkdownView, Notice, Plugin, getLanguage } from 'obsidian';
2+
import { hasTodayEntry, insertTodayEntry } from './utils';
3+
import { createTranslator, LocaleCode, LocaleKey, normalizeLocaleCode, Translator } from './locales';
4+
import { HeatmapRenderChild, ButtonsRenderChild, DailyOverviewRenderChild } from './render-markdown-render-children';
5+
import { EasyTrackerPluginSettings, DEFAULT_SETTINGS, EasyTrackerSettingTab } from './setting-tab';
66

77
export default class EasyTrackerPlugin extends Plugin {
88
settings: EasyTrackerPluginSettings;
9-
private heatmaps: CalendarHeatmap[] = [];
10-
private overviewBlocks: HTMLElement[] = [];
11-
private locale: LocaleCode = 'en';
12-
private translator: Translator = createTranslator('en');
9+
public locale: LocaleCode = 'en';
10+
public translator: Translator = createTranslator('en');
1311
private lastCheckInTime = 0;
1412

1513
public t(key: LocaleKey, vars?: Record<string, string | number>): string {
@@ -21,16 +19,18 @@ export default class EasyTrackerPlugin extends Plugin {
2119
}
2220

2321
public refreshLocale(): void {
24-
const resolved = resolveLocale(this.settings.language, this.getSystemLocale());
22+
const resolved = normalizeLocaleCode(this.getSystemLocale());
2523
this.locale = resolved;
2624
this.translator = createTranslator(resolved);
27-
this.updateOverviews();
25+
this.triggerRefresh();
2826
}
2927

30-
public async updateLanguage(language: LanguageSetting): Promise<void> {
31-
this.settings.language = language;
32-
await this.saveSettings();
33-
this.refreshLocale();
28+
public triggerRefresh(): void {
29+
this.app.workspace.trigger('easy-tracker:refresh');
30+
}
31+
32+
public triggerSettingsRefresh(): void {
33+
this.app.workspace.trigger('easy-tracker-setting:refresh');
3434
}
3535

3636
// Get the active Markdown view or notify the user
@@ -44,12 +44,12 @@ export default class EasyTrackerPlugin extends Plugin {
4444
}
4545

4646
// Read the current note content safely
47-
private getActiveContent(): string {
47+
public getActiveContent(): string {
4848
const view = this.getActiveMarkdownView();
4949
return view ? (view.editor.getValue() || '') : '';
5050
}
5151

52-
private isTodayCheckedIn(): boolean {
52+
public isTodayCheckedIn(): boolean {
5353
const view = this.getActiveMarkdownView();
5454
if (!view) return false;
5555

@@ -58,7 +58,7 @@ export default class EasyTrackerPlugin extends Plugin {
5858
}
5959

6060
// Safely insert today's entry with a value (prevents duplicates)
61-
private insertEntry(value: number): boolean {
61+
public insertEntry(value: number): boolean {
6262
const now = Date.now();
6363
if (now - this.lastCheckInTime < 1000) {
6464
new Notice(this.t('notice.checkInTooFast'));
@@ -88,38 +88,11 @@ export default class EasyTrackerPlugin extends Plugin {
8888
insertTodayEntry(editor, value);
8989

9090
// update
91-
this.updateHeatmaps();
92-
this.updateOverviews();
91+
this.triggerRefresh();
9392

9493
return true;
9594
}
9695

97-
// Parse JSON options for the heatmap processor with fallback
98-
private parseHeatmapOptions(source: string): Partial<CalendarHeatmapOptions> {
99-
try {
100-
return source.trim() ? JSON.parse(source) : {};
101-
} catch {
102-
console.warn('calendar-heatmap: unable to parse options JSON, using defaults');
103-
return {};
104-
}
105-
}
106-
107-
private updateHeatmaps(): void {
108-
const data = parseEntries(this.getActiveContent());
109-
this.heatmaps.forEach(heatmap => heatmap.replaceData(data));
110-
}
111-
112-
private updateOverviews(): void {
113-
if (!this.overviewBlocks.length) return;
114-
115-
const entries = parseEntries(this.getActiveContent());
116-
const overview = computeDailyOverview(entries);
117-
118-
for (const block of this.overviewBlocks) {
119-
updateDailyOverview(block, overview, this.translator);
120-
}
121-
}
122-
12396
async onload() {
12497
// Load settings (includes migration from legacy weakStart)
12598
await this.loadSettings();
@@ -133,23 +106,8 @@ export default class EasyTrackerPlugin extends Plugin {
133106
});
134107

135108
// Render a yearly calendar heatmap from entries in the current note
136-
this.registerMarkdownCodeBlockProcessor('easy-tracker-year-calendar-heatmap', (source, el, _ctx) => {
137-
const data = parseEntries(this.getActiveContent());
138-
const options = this.parseHeatmapOptions(source);
139-
const container = el.createDiv({ cls: 'easy-tracker-card' });
140-
container.createEl('div', { cls: 'easy-tracker-card-title', text: this.t('card.activityHistoryTitle') });
141-
const heatmapElement = container.createDiv({ cls: 'easy-tracker-year-calendar-heatmap' });
142-
143-
const heatmap = new CalendarHeatmap(heatmapElement, data, {
144-
weekStart: this.settings.weekStart,
145-
view: "year",
146-
year: new Date().getFullYear(),
147-
legend: false,
148-
language: this.locale,
149-
...options,
150-
});
151-
152-
this.heatmaps.push(heatmap);
109+
this.registerMarkdownCodeBlockProcessor('easy-tracker-year-calendar-heatmap', (source, el, ctx) => {
110+
ctx.addChild(new HeatmapRenderChild(this, el, source));
153111
});
154112

155113
// Render a group of buttons that insert today's entry with a value
@@ -159,44 +117,12 @@ export default class EasyTrackerPlugin extends Plugin {
159117
// Enough | 2
160118
// More | 3
161119
// ```
162-
this.registerMarkdownCodeBlockProcessor("easy-tracker-buttons", (source, el) => {
163-
const container = el.createDiv({ cls: "easy-tracker-card" });
164-
container.setAttr('id', 'easy-tracker-buttons');
165-
container.createEl('div', { cls: 'easy-tracker-card-title', text: this.t('card.buttonsTitle') });
166-
167-
if (this.isTodayCheckedIn()) {
168-
container.createEl('div', { cls: 'easy-tracker-card-message', text: this.t('card.checkInCongrats') });
169-
return;
170-
}
171-
172-
const wrap = container.createDiv({ cls: "easy-tracker-button-group" });
173-
const lines = source.split("\n").map(s => s.trim()).filter(Boolean);
174-
175-
for (const [index, line] of lines.entries()) {
176-
const [text, val] = line.split('|').map(s => s.trim());
177-
const btn = new ButtonComponent(wrap);
178-
btn.buttonEl.addClass("btn");
179-
btn.setButtonText(text || this.t('card.defaultButton'));
180-
btn.onClick(() => {
181-
const n = Number(val);
182-
const valueToInsert = Number.isFinite(n) ? n : index + 1; // use provided number, fallback to index
183-
const checkInResult = this.insertEntry(valueToInsert);
184-
185-
if (checkInResult) {
186-
wrap.setCssStyles({ display: 'none' });
187-
container.createEl('div', { cls: 'easy-tracker-card-message', text: this.t('card.checkInCongrats') });
188-
}
189-
});
190-
}
120+
this.registerMarkdownCodeBlockProcessor("easy-tracker-buttons", (source, el, ctx) => {
121+
ctx.addChild(new ButtonsRenderChild(this, el, source));
191122
});
192123

193-
this.registerMarkdownCodeBlockProcessor("easy-tracker-daily-overview", (_source, el) => {
194-
const entries = parseEntries(this.getActiveContent());
195-
const overview = computeDailyOverview(entries);
196-
renderDailyOverview(el, overview, this.translator);
197-
198-
// Track this block for future updates
199-
this.overviewBlocks.push(el);
124+
this.registerMarkdownCodeBlockProcessor("easy-tracker-daily-overview", (_source, el, ctx) => {
125+
ctx.addChild(new DailyOverviewRenderChild(this, el));
200126
});
201127

202128
// Insert a bare heatmap block
@@ -218,6 +144,7 @@ export default class EasyTrackerPlugin extends Plugin {
218144
name: this.t('command.insertCheckInComponent'),
219145
editorCallback: (editor: Editor, _view: MarkdownView) => {
220146
editor.replaceSelection([
147+
'',
221148
'```easy-tracker-daily-overview', '```',
222149
'```easy-tracker-year-calendar-heatmap', '```',
223150
'```easy-tracker-buttons',
@@ -237,6 +164,7 @@ export default class EasyTrackerPlugin extends Plugin {
237164
name: this.t('command.insertSingleCheckInComponent'),
238165
editorCallback: (editor: Editor, _view: MarkdownView) => {
239166
editor.replaceSelection([
167+
'',
240168
'```easy-tracker-daily-overview', '```',
241169
'```easy-tracker-year-calendar-heatmap', '```',
242170
'```easy-tracker-buttons',
@@ -277,7 +205,6 @@ export default class EasyTrackerPlugin extends Plugin {
277205
const migrated = typeof legacy !== 'undefined'
278206
? (parseInt(String(legacy)) === 0 ? 0 : 1)
279207
: undefined;
280-
const language: LanguageSetting = isLanguageSetting(data?.language) ? data.language : DEFAULT_SETTINGS.language;
281208
const overrides: Partial<EasyTrackerPluginSettings> = {};
282209

283210
if (typeof data?.weekStart === 'number') {
@@ -291,65 +218,11 @@ export default class EasyTrackerPlugin extends Plugin {
291218
this.settings = {
292219
...DEFAULT_SETTINGS,
293220
...overrides,
294-
language,
295221
};
296222
}
297223

298224
async saveSettings() {
299225
await this.saveData(this.settings);
300-
}
301-
}
302-
303-
// Plugin settings: weekStart (0 = Sunday, 1 = Monday)
304-
interface EasyTrackerPluginSettings {
305-
weekStart: 0 | 1;
306-
language: LanguageSetting;
307-
}
308-
309-
const DEFAULT_SETTINGS: EasyTrackerPluginSettings = {
310-
weekStart: 1,
311-
language: 'system',
312-
};
313-
314-
class EasyTrackerSettingTab extends PluginSettingTab {
315-
plugin: EasyTrackerPlugin;
316-
317-
constructor(app: App, plugin: EasyTrackerPlugin) {
318-
super(app, plugin);
319-
this.plugin = plugin;
320-
}
321-
322-
display(): void {
323-
const { containerEl } = this;
324-
containerEl.empty();
325-
326-
new Setting(containerEl)
327-
.setName(this.plugin.t('setting.languageName'))
328-
.setDesc(this.plugin.t('setting.languageDescription'))
329-
.addDropdown(drop => {
330-
drop.addOption('system', this.plugin.t('setting.languageOption.system'));
331-
drop.addOption('en', this.plugin.t('setting.languageOption.en'));
332-
drop.addOption('zh-CN', this.plugin.t('setting.languageOption.zhCN'));
333-
drop.setValue(this.plugin.settings.language);
334-
drop.onChange(async (value) => {
335-
const next = isLanguageSetting(value) ? value : 'system';
336-
await this.plugin.updateLanguage(next);
337-
this.display();
338-
});
339-
});
340-
341-
new Setting(containerEl)
342-
.setName(this.plugin.t('setting.weekStartName'))
343-
.setDesc(this.plugin.t('setting.weekStartDescription'))
344-
.addDropdown(drop => {
345-
// 1 = Monday (default); 0 = Sunday
346-
drop.addOption('1', this.plugin.t('setting.weekStart.monday'));
347-
drop.addOption('0', this.plugin.t('setting.weekStart.sunday'));
348-
drop.setValue(String(this.plugin.settings.weekStart));
349-
drop.onChange(async (value) => {
350-
this.plugin.settings.weekStart = value === '0' ? 0 : 1;
351-
await this.plugin.saveSettings();
352-
});
353-
});
226+
this.triggerSettingsRefresh();
354227
}
355228
}

manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"id": "easy-tracker",
33
"name": "Easy Tracker",
4-
"version": "1.0.2",
5-
"minAppVersion": "0.15.0",
4+
"version": "1.0.4",
5+
"minAppVersion": "1.8.0",
66
"description": "Instantly track goals and habits. Simple, beautiful, and configuration-free.",
77
"author": "Hunter Ji",
88
"authorUrl": "https://hunterji.com",

0 commit comments

Comments
 (0)