@@ -45,23 +45,36 @@ class ExtensionFDD {
4545 let html = this . template . innerHTML ;
4646
4747 // Advanced Handlebars-style Arrays: {{#each list}}...{{/each}}
48- html = html . replace ( / \{ \{ # e a c h \s + ( .* ?) \} \} ( [ \s \S ] * ?) \{ \{ \/ e a c h \} \} / g, ( match , arrayKey , loopBody ) => {
49- const arr = context [ arrayKey . trim ( ) ] ;
50- if ( ! Array . isArray ( arr ) ) return '' ;
51- return arr . map ( item => {
52- return loopBody . replace ( / \{ \{ ( .* ?) \} \} / g, ( m , k ) => {
53- const key = k . trim ( ) ;
54- return ( item [ key ] !== undefined && item [ key ] !== null ) ? item [ key ] : '' ;
48+ html = html . replace ( / \{ \{ # e a c h \s + ( .* ?) \} \} ( [ \s \S ] * ?) \{ \{ \/ e a c h \} \} / g, ( match , param , body ) => {
49+ const key = param . trim ( ) ;
50+ const arr = context [ key ] ;
51+ if ( ! arr || ! Array . isArray ( arr ) ) {
52+ console . warn ( "FDD Array not found for" , key ) ;
53+ return '' ;
54+ }
55+ let out = "" ;
56+ for ( let i = 0 ; i < arr . length ; i ++ ) {
57+ let itemBody = body ;
58+ Object . keys ( arr [ i ] ) . forEach ( k => {
59+ const val = arr [ i ] [ k ] !== null && arr [ i ] [ k ] !== undefined ? arr [ i ] [ k ] : '' ;
60+ itemBody = itemBody . replace ( new RegExp ( `\\{\\{\\s*${ k } \\s*\\}\\}` , 'g' ) , val ) ;
5561 } ) ;
56- } ) . join ( '' ) ;
62+ out += itemBody ;
63+ }
64+ return out ;
5765 } ) ;
5866
59- // Basic Handlebars Binding: {{field}}
60- html = html . replace ( / \{ \{ ( .* ?) \} \} / g, ( match , key ) => {
61- const k = key . trim ( ) ;
62- return ( context [ k ] !== undefined && context [ k ] !== null ) ? context [ k ] : '' ;
67+ // Global Key Binding: {{field}}
68+ Object . keys ( context ) . forEach ( k => {
69+ if ( typeof context [ k ] !== 'object' ) {
70+ const val = context [ k ] !== null && context [ k ] !== undefined ? context [ k ] : '' ;
71+ html = html . replace ( new RegExp ( `\\{\\{\\s*${ k } \\s*\\}\\}` , 'g' ) , val ) ;
72+ }
6373 } ) ;
6474
75+ // Any remaining hanging mustache brackets will just be stripped to keep the UI clean
76+ html = html . replace ( / \{ \{ ( .* ?) \} \} / g, '' ) ;
77+
6578 const shadowRoot = this . container . attachShadow ( { mode : 'open' } ) ;
6679 shadowRoot . innerHTML = html ;
6780
@@ -70,7 +83,6 @@ class ExtensionFDD {
7083 }
7184
7285 setupInteractivity ( root ) {
73- // Allows declarative CSS filtering by driving native filtering through data attributes over the Shadow Root
7486 const searchInputs = root . querySelectorAll ( 'input[data-filter-target]' ) ;
7587 searchInputs . forEach ( input => {
7688 input . addEventListener ( 'input' , e => {
@@ -92,10 +104,9 @@ class ExtensionFDD {
92104 setupAutoSave ( root ) {
93105 const inputs = root . querySelectorAll ( '[autosave]' ) ;
94106 inputs . forEach ( input => {
95-
96- // Request persistent file write-handle purely on interaction intent
97- input . addEventListener ( 'focus' , async ( ) => {
98- if ( ! this . fileHandle && window . showOpenFilePicker ) {
107+ // Prompt user for local file persistence capability on intersection/interaction click
108+ input . addEventListener ( 'pointerdown' , async ( ) => {
109+ if ( ! this . fileHandle && window . showOpenFilePicker && document . contentType !== 'text/plain' ) {
99110 try {
100111 const [ handle ] = await window . showOpenFilePicker ( {
101112 types : [ { description : 'Formatted Data Document' , accept : { 'text/html' : [ '.fdd' ] } } ] ,
@@ -108,14 +119,22 @@ class ExtensionFDD {
108119 this . fileHandle = handle ;
109120 console . log ( "FDD: Auto-save File Handle persistently bound!" ) ;
110121 }
111- } catch ( e ) { console . warn ( "FDD Interaction missing valid write handle confirmation ." ) ; }
122+ } catch ( e ) { console . warn ( "FDD Permission denied by user. Download fallback will be used ." ) ; }
112123 }
113124 } ) ;
114125
115126 // Commit to persistent disk layer via handler mechanism
127+ input . addEventListener ( 'change' , async ( e ) => {
128+ const path = e . target . name . split ( '.' ) ;
129+ if ( path [ 0 ] === 'user-notes' && path [ 1 ] ) {
130+ this . mutableData [ path [ 1 ] ] = e . target . value ;
131+ await this . syncFile ( ) ;
132+ }
133+ } ) ;
134+
116135 input . addEventListener ( 'blur' , async ( e ) => {
117136 const path = e . target . name . split ( '.' ) ;
118- if ( path [ 0 ] === 'user-notes' && path [ 1 ] && this . fileHandle ) {
137+ if ( path [ 0 ] === 'user-notes' && path [ 1 ] ) {
119138 this . mutableData [ path [ 1 ] ] = e . target . value ;
120139 await this . syncFile ( ) ;
121140 }
@@ -133,20 +152,47 @@ class ExtensionFDD {
133152 mutableScript . textContent = "\n " + JSON . stringify ( this . mutableData , null , 2 ) + "\n " ;
134153 }
135154
136- try {
137- const writable = await this . fileHandle . createWritable ( ) ;
138- const content = "<!DOCTYPE html>\n" + originalDocParser . documentElement . outerHTML ;
139- await writable . write ( content ) ;
140- await writable . close ( ) ;
141-
142- const badge = document . createElement ( 'div' ) ;
143- badge . style . cssText = "position:fixed;bottom:20px;right:20px;background:#10b981;color:white;padding:8px 16px;border-radius:999px;z-index:9999;font-family:system-ui;box-shadow:0 4px 6px rgba(0,0,0,0.1);font-weight:bold;" ;
144- badge . innerText = "✓ FDD Synced to Disk" ;
145- document . body . appendChild ( badge ) ;
146- setTimeout ( ( ) => badge . remove ( ) , 2500 ) ;
147- } catch ( e ) {
148- console . error ( "FDD Persist Error" , e ) ;
155+ const content = "<!DOCTYPE html>\n" + originalDocParser . documentElement . outerHTML ;
156+
157+ // Primary Write Strategy (Windows / macOS / Chromium Standards)
158+ if ( this . fileHandle ) {
159+ try {
160+ const writable = await this . fileHandle . createWritable ( ) ;
161+ await writable . write ( content ) ;
162+ await writable . close ( ) ;
163+ this . showBadge ( "✓ FDD Synced Directly to Disk" , "#10b981" ) ;
164+ return ;
165+ } catch ( e ) {
166+ console . error ( "Direct FDD Persist Error, falling back..." , e ) ;
167+ }
149168 }
169+
170+ // Secondary Linux / Missing FileURL Permission Fallback Strategy
171+ console . warn ( "Executing native Download Fallback mapping for persistent storage" ) ;
172+ const blob = new Blob ( [ content ] , { type : 'text/html' } ) ;
173+ const url = URL . createObjectURL ( blob ) ;
174+ const a = document . createElement ( 'a' ) ;
175+ a . href = url ;
176+ a . download = 'updated.fdd' ;
177+ document . body . appendChild ( a ) ;
178+ a . click ( ) ;
179+ document . body . removeChild ( a ) ;
180+ URL . revokeObjectURL ( url ) ;
181+ this . showBadge ( "⚠️ Downloaded New FDD Backup" , "#fbbf24" ) ;
182+ }
183+
184+ showBadge ( msg , color ) {
185+ const badge = document . createElement ( 'div' ) ;
186+ badge . style . cssText = `position:fixed;bottom:20px;right:20px;background:${ color } ;color:white;padding:10px 18px;border-radius:999px;z-index:9999;font-family:system-ui;font-weight:bold;box-shadow:0 10px 15px -3px rgba(0,0,0,0.1);` ;
187+ badge . innerText = msg ;
188+
189+ // Since container is a shadowRoot, we append directly to the host body
190+ document . body . appendChild ( badge ) ;
191+ setTimeout ( ( ) => {
192+ badge . style . transition = 'opacity 0.5s ease' ;
193+ badge . style . opacity = '0' ;
194+ setTimeout ( ( ) => badge . remove ( ) , 500 ) ;
195+ } , 3000 ) ;
150196 }
151197}
152198
0 commit comments