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
77export 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}
0 commit comments