1- import { Editor , Plugin , setTooltip , parseYaml , Menu , Notice , type TFolder } from 'obsidian' ;
1+ import { Editor , Plugin , setTooltip , parseYaml , Menu , Notice , type TFolder , debounce , type Debouncer } from 'obsidian' ;
22import { SettingTab , type PluginSettings , DEFAULT_SETTINGS } from './settings' ;
3- import { ADV_LIBRARY , ENV_LIBRARY , ADV_TEMPLATE , ENV_TEMPLATE , processAdversary , walkFolder } from './utils' ;
4- import { AdversaryCard , AdversaryModal , type Adversary } from './ui' ;
3+ import { ADV_LIBRARY , ENV_LIBRARY , ADV_TEMPLATE , ENV_TEMPLATE , walkFolder } from './utils' ;
4+ import { AdversaryCard , AdversaryModal , type RawAdversary } from './ui' ;
55
66export type PluginState = {
77 settings : PluginSettings ;
@@ -25,7 +25,8 @@ export default class BeastVault extends Plugin {
2525 saveTimer ?: number ;
2626 saving ?: Promise < void > ;
2727 battlePoints : HTMLElement ;
28- library : Adversary [ ] = [ ] ;
28+ library : RawAdversary [ ] = [ ] ;
29+ updateState : Debouncer < [ ] , Promise < void > > ;
2930
3031 updateStatusBar ( ) {
3132 const file = this . app . workspace . getActiveFile ( ) ;
@@ -75,9 +76,9 @@ export default class BeastVault extends Plugin {
7576 }
7677 return ;
7778 }
78- const newLibrary : Adversary [ ] = [ ] ;
79+ const newLibrary : RawAdversary [ ] = [ ] ;
7980 await walkFolder ( folder , async ( file ) => {
80- let content : Adversary | Adversary [ ] ;
81+ let content : RawAdversary | RawAdversary [ ] ;
8182
8283 if ( file . extension == 'json' ) {
8384 content = JSON . parse ( await this . app . vault . read ( file ) ) ;
@@ -90,15 +91,18 @@ export default class BeastVault extends Plugin {
9091 const lines = ( await this . app . vault . read ( file ) ) . split ( '\n' ) ;
9192 content = codeblocks
9293 . filter ( sec => lines [ sec . position . start . line ] . trim ( ) === '```daggerheart' )
93- . map ( sec => parseYaml ( lines . slice ( sec . position . start . line + 1 , sec . position . end . line ) . join ( "\n" ) ) ) ;
94+ . map ( sec => {
95+ const targetLines = lines . slice ( sec . position . start . line + 1 , sec . position . end . line ) . join ( "\n" ) ;
96+ return { raw : targetLines , ...parseYaml ( targetLines ) } ;
97+ } ) ;
9498 } else {
9599 return ;
96100 }
97101
98102 if ( ! Array . isArray ( content ) ) content = [ content ] ;
99103 for ( const item of content ) {
100104 if ( item && typeof item == 'object' && typeof item . name == 'string' ) {
101- newLibrary . push ( processAdversary ( { source : file . path , ...item } , file . path ) ) ;
105+ newLibrary . push ( { source : 'homebrew' , ...item } ) ;
102106 }
103107 }
104108 } )
@@ -123,18 +127,19 @@ export default class BeastVault extends Plugin {
123127 if ( length == 0 ) {
124128 new Notice ( `No valid stat blocks found in ${ this . state . settings . libraryFolder } ` ) ;
125129 } else {
130+ // TODO: message about duplicates?
126131 new Notice ( `Loaded ${ length } stat block${ length != 1 ? 's' : '' } ` )
127132 }
128133 }
129134
130135 return newLibrary ;
131136 }
132137
133- allAdversaries ( ) : Adversary [ ] {
138+ allAdversaries ( ) : RawAdversary [ ] {
134139 return this . library . filter ( adv => ( adv . hp && adv . hp > 0 ) || ( adv . stress && adv . stress > 0 ) ) . concat ( ADV_LIBRARY ) ;
135140 }
136141
137- allEnvironments ( ) : Adversary [ ] {
142+ allEnvironments ( ) : RawAdversary [ ] {
138143 return this . library . filter ( adv => ( ! adv . hp || adv . hp == 0 ) && ( ! adv . stress || adv . stress == 0 ) ) . concat ( ENV_LIBRARY ) ;
139144 }
140145
@@ -144,10 +149,10 @@ export default class BeastVault extends Plugin {
144149 this . battlePoints = this . addStatusBarItem ( ) ;
145150 this . registerEvent ( this . app . workspace . on ( 'active-leaf-change' , ( ) => this . updateStatusBar ( ) ) ) ;
146151 this . app . workspace . onLayoutReady ( ( ) => this . scanLibrary ( false , 'no' ) ) ;
152+ this . updateState = debounce ( ( ) => this . saveData ( this . state ) , 1000 , true ) ;
147153
148154 this . registerMarkdownCodeBlockProcessor ( "daggerheart" , ( src , el , ctx ) => {
149- const adv = processAdversary ( parseYaml ( src ) ?? { } , this . app . workspace . getActiveFile ( ) ! . path ) ;
150- const child = new AdversaryCard ( el , adv , this ) ;
155+ const child = new AdversaryCard ( el , parseYaml ( src ) ?? { } , this ) ;
151156 ctx . addChild ( child ) ;
152157 child . render ( ) ;
153158 // Track it so we can refresh on settings change:
@@ -250,9 +255,7 @@ export default class BeastVault extends Plugin {
250255 }
251256
252257 onunload ( ) {
253- if ( this . saveTimer != null ) {
254- void this . flushSave ( ) ;
255- }
258+ void this . updateState . run ( ) ;
256259 }
257260
258261 renderAll ( ) {
@@ -261,23 +264,6 @@ export default class BeastVault extends Plugin {
261264 }
262265 }
263266
264- updateState ( ) {
265- // Debounce writes
266- if ( this . saveTimer != null ) window . clearTimeout ( this . saveTimer ) ;
267- this . saveTimer = window . setTimeout ( ( ) => { void this . flushSave ( ) ; } , 1000 ) ;
268- }
269-
270- async flushSave ( ) {
271- if ( this . saveTimer != null ) {
272- window . clearTimeout ( this . saveTimer ) ;
273- this . saveTimer = undefined ;
274- }
275- const run = async ( ) => await this . saveData ( this . state ) ;
276- // Chain to the previous save if one is in-flight
277- this . saving = ( this . saving ?? Promise . resolve ( ) ) . then ( run , run ) ;
278- await this . saving ;
279- }
280-
281267 updateCard ( keys : ( string | number ) [ ] , value : string | number ) {
282268 type Data = { [ key : string ] : Data | number | string } ;
283269 let data : Data = this . state . cards ;
@@ -294,12 +280,11 @@ export default class BeastVault extends Plugin {
294280 getCardState ( keys : ( string | number ) [ ] ) : number | undefined {
295281 type Data = { [ key : string ] : Data | string | number }
296282 let data : Data = this . state . cards ;
297- for ( const key of keys ) {
283+ for ( const [ i , key ] of keys . entries ( ) ) {
298284 if ( ! data [ key ] ) return undefined ;
285+ if ( i === keys . length - 1 ) return data [ key ] as number ;
299286 data = data [ key ] as Data ;
300287 }
301- return data as any ;
302288 }
303289}
304290
305-
0 commit comments