1
- import { Editor , EditorPosition , Notice , Setting } from "obsidian" ;
1
+ import { Editor , EditorPosition , Notice , Setting , MarkdownPostProcessorContext } from "obsidian" ;
2
2
import { CryptoHelper } from "../../services/CryptoHelper" ;
3
3
import { CryptoHelperObsolete } from "../../services/CryptoHelperObsolete" ;
4
4
import DecryptModal from "./DecryptModal" ;
@@ -42,6 +42,9 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
42
42
this . pluginSettings = settings ;
43
43
this . featureSettings = settings . featureInplaceEncrypt ;
44
44
45
+
46
+ this . plugin . registerMarkdownPostProcessor ( ( el , ctx ) => this . processEncryptedCodeBlockProcessor ( el , ctx ) ) ;
47
+
45
48
plugin . addCommand ( {
46
49
id : 'meld-encrypt' ,
47
50
name : 'Encrypt/Decrypt' ,
@@ -62,6 +65,137 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
62
65
63
66
}
64
67
68
+ private processEncryptedCodeBlockProcessor ( el : HTMLElement , ctx : MarkdownPostProcessorContext ) {
69
+
70
+ const si = ctx . getSectionInfo ( el ) ;
71
+ if ( si == null ) {
72
+ return ;
73
+ }
74
+
75
+ // isolate code block lines
76
+ const text = InplaceTextHelper . extractTextLines ( si . text , si . lineStart , si . lineEnd ) ;
77
+
78
+
79
+ const markerStart = InplaceTextHelper . findFirstMarker ( _PREFIXES , text ) ;
80
+ if ( markerStart == null || markerStart . marker != _PREFIX_A_VISIBLE ) {
81
+ //console.debug( 'not visible or null', markerStart );
82
+ return ;
83
+ }
84
+
85
+ const markerEnd = InplaceTextHelper . findFirstMarker ( _SUFFIXES , text , markerStart . position + markerStart . marker . length ) ;
86
+ if ( markerEnd == null ) {
87
+ return ;
88
+ }
89
+
90
+ const encryptedText = InplaceTextHelper . removeMarkers ( text , markerStart , markerEnd ) ;
91
+
92
+ const selectionAnalysis = new SelectionAnalysis ( encryptedText ) ;
93
+
94
+ if ( ! selectionAnalysis . canDecrypt ) {
95
+ return ;
96
+ }
97
+
98
+ const textBeforeIndicator = InplaceTextHelper . extractTextBeforeMarker ( text , markerStart ) ;
99
+ const textAfterIndicator = InplaceTextHelper . extractTextAfterMarker ( text , markerEnd ) ;
100
+
101
+
102
+ // create elements
103
+ const elPreIndicator = createSpan ( { text : textBeforeIndicator } ) ;
104
+ const elPostIndicator = createSpan ( { text : textAfterIndicator } ) ;
105
+
106
+ const elIndicator = createSpan ( { text : '🔐' } ) ;
107
+ elIndicator . style . cursor = 'pointer' ;
108
+
109
+ elIndicator . onClickEvent ( async ev => {
110
+ // indicator click handler
111
+
112
+ if ( await this . showDecryptedTextIfPasswordKnown ( ctx . sourcePath , selectionAnalysis . decryptable ) ) {
113
+ return ;
114
+ }
115
+
116
+ const pw = await this . fetchPasswordFromUser ( selectionAnalysis . decryptable . hint ) ;
117
+
118
+ if ( pw == null ) {
119
+ return ;
120
+ }
121
+
122
+ // decrypt
123
+ if ( await this . showDecryptedResultForPassword ( selectionAnalysis . decryptable , pw ) ) {
124
+ SessionPasswordService . putByPath (
125
+ {
126
+ password : pw ,
127
+ hint : selectionAnalysis . decryptable . hint
128
+ } ,
129
+ ctx . sourcePath
130
+ ) ;
131
+ } else {
132
+ new Notice ( '❌ Decryption failed!' ) ;
133
+ }
134
+
135
+ } ) ;
136
+
137
+ el . empty ( ) ;
138
+ el . append ( elPreIndicator , elIndicator , elPostIndicator ) ;
139
+
140
+ }
141
+
142
+ private async showDecryptedResultForPassword ( decryptable : Decryptable , pw :string ) : Promise < boolean > {
143
+ const crypto = new CryptoHelper ( ) ;
144
+ const decryptedText = await crypto . decryptFromBase64 (
145
+ decryptable . base64CipherText ,
146
+ pw
147
+ ) ;
148
+ // show result
149
+ if ( decryptedText === null ) {
150
+ return false ;
151
+ }
152
+
153
+ return new Promise < boolean > ( ( resolve ) => {
154
+ const decryptModal = new DecryptModal ( this . plugin . app , '🔓' , decryptedText ) ;
155
+ decryptModal . canDecryptInPlace = false ;
156
+ decryptModal . onClose = ( ) => {
157
+ resolve ( true ) ;
158
+ }
159
+ decryptModal . open ( ) ;
160
+ } )
161
+
162
+
163
+ }
164
+
165
+ private async fetchPasswordFromUser ( hint :string ) : Promise < string | null | undefined > {
166
+ // fetch password
167
+ return new Promise < string | null | undefined > ( ( resolve ) => {
168
+ const pwModal = new PasswordModal (
169
+ this . plugin . app ,
170
+ /*isEncrypting*/ false ,
171
+ /*confirmPassword*/ false ,
172
+ /*defaultShowInReadingView*/ true /* TODO: get from settings */ ,
173
+ '' ,
174
+ hint
175
+ ) ;
176
+
177
+ pwModal . onClose = ( ) => {
178
+ resolve ( pwModal . resultPassword ) ;
179
+ }
180
+
181
+ pwModal . open ( ) ;
182
+
183
+
184
+ } ) ;
185
+ }
186
+
187
+ private async showDecryptedTextIfPasswordKnown ( filePath : string , decryptable : Decryptable ) : Promise < boolean > {
188
+ const bestGuessPasswordAndHint = SessionPasswordService . getByPath ( filePath ) ;
189
+ if ( bestGuessPasswordAndHint . password == null ) {
190
+ return false ;
191
+ }
192
+
193
+ return await this . showDecryptedResultForPassword (
194
+ decryptable ,
195
+ bestGuessPasswordAndHint . password
196
+ ) ;
197
+ }
198
+
65
199
public buildSettingsUi (
66
200
containerEl : HTMLElement ,
67
201
saveSettingCallback : ( ) => Promise < void >
@@ -86,8 +220,6 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
86
220
;
87
221
}
88
222
89
-
90
-
91
223
private processEncryptDecryptCommand (
92
224
checking : boolean ,
93
225
editor : Editor ,
@@ -241,7 +373,7 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
241
373
let defaultPassword = '' ;
242
374
let defaultHint = selectionAnalysis . decryptable ?. hint ;
243
375
if ( this . pluginSettings . rememberPassword ) {
244
- const bestGuessPasswordAndHint = SessionPasswordService . get ( activeFile ) ;
376
+ const bestGuessPasswordAndHint = SessionPasswordService . getByPath ( activeFile . path ) ;
245
377
//console.debug({bestGuessPasswordAndHint});
246
378
247
379
defaultPassword = bestGuessPasswordAndHint . password ;
@@ -254,6 +386,7 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
254
386
this . plugin . app ,
255
387
selectionAnalysis . canEncrypt ,
256
388
confirmPassword ,
389
+ /*defaultShowInReadingView*/ true /* TODO: get from settings */ ,
257
390
defaultPassword ,
258
391
defaultHint
259
392
) ;
@@ -275,11 +408,12 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
275
408
encryptable ,
276
409
pw ,
277
410
finalSelectionStart ,
278
- finalSelectionEnd
411
+ finalSelectionEnd ,
412
+ pwModal . resultShowInReadingView ?? true /* TODO: get from settings */
279
413
) ;
280
414
281
415
// remember password
282
- SessionPasswordService . put ( { password :pw , hint : hint } , activeFile ) ;
416
+ SessionPasswordService . putByPath ( { password :pw , hint : hint } , activeFile . path ) ;
283
417
284
418
} else {
285
419
@@ -306,7 +440,7 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
306
440
307
441
// remember password?
308
442
if ( decryptSuccess ) {
309
- SessionPasswordService . put ( { password :pw , hint : hint } , activeFile ) ;
443
+ SessionPasswordService . putByPath ( { password :pw , hint : hint } , activeFile . path ) ;
310
444
}
311
445
312
446
}
@@ -322,12 +456,14 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
322
456
password : string ,
323
457
finalSelectionStart : CodeMirror . Position ,
324
458
finalSelectionEnd : CodeMirror . Position ,
459
+ showInReadingView : boolean
325
460
) {
326
461
//encrypt
327
462
const crypto = new CryptoHelper ( ) ;
328
463
const encodedText = this . encodeEncryption (
329
464
await crypto . encryptToBase64 ( encryptable . text , password ) ,
330
- encryptable . hint
465
+ encryptable . hint ,
466
+ showInReadingView
331
467
) ;
332
468
editor . setSelection ( finalSelectionStart , finalSelectionEnd ) ;
333
469
editor . replaceSelection ( encodedText ) ;
@@ -405,15 +541,18 @@ export default class FeatureInplaceEncrypt implements IMeldEncryptPluginFeature{
405
541
}
406
542
407
543
408
- private encodeEncryption ( encryptedText : string , hint : string ) : string {
544
+ private encodeEncryption ( encryptedText : string , hint : string , showInReadingView : boolean ) : string {
409
545
if (
410
546
! _PREFIXES . some ( ( prefix ) => encryptedText . contains ( prefix ) )
411
547
&& ! _SUFFIXES . some ( ( suffix ) => encryptedText . contains ( suffix ) )
412
548
) {
413
- if ( hint . length > 0 ) {
414
- return _PREFIX_A . concat ( _HINT , hint , _HINT , encryptedText , _SUFFIX_WITH_COMMENT ) ;
549
+ const prefix = showInReadingView ? _PREFIX_A_VISIBLE : _PREFIX_A ;
550
+ const suffix = showInReadingView ? _SUFFIX_NO_COMMENT : _SUFFIX_WITH_COMMENT ;
551
+
552
+ if ( hint . length > 0 ) {
553
+ return prefix . concat ( _HINT , hint , _HINT , encryptedText , suffix ) ;
415
554
}
416
- return _PREFIX_A . concat ( encryptedText , _SUFFIX_WITH_COMMENT ) ;
555
+ return prefix . concat ( encryptedText , suffix ) ;
417
556
}
418
557
return encryptedText ;
419
558
}
@@ -517,4 +656,54 @@ class Decryptable{
517
656
version : number ;
518
657
base64CipherText :string ;
519
658
hint :string ;
659
+ }
660
+
661
+
662
+ interface IMarkerPosition {
663
+ marker :string ;
664
+ position :number ;
665
+ }
666
+
667
+ class InplaceTextHelper {
668
+ static extractTextBeforeMarker ( text : string , marker : IMarkerPosition ) {
669
+ return text . substring ( 0 , marker . position ) ;
670
+ }
671
+ static extractTextAfterMarker ( text : string , marker : IMarkerPosition ) {
672
+ return text . substring ( marker . position + marker . marker . length ) ;
673
+ }
674
+
675
+ public static removeMarkers ( text : string , markerStart : IMarkerPosition , markerEnd : IMarkerPosition ) {
676
+ return text . substring ( markerStart . position , markerEnd . position + markerEnd . marker . length ) ;
677
+ }
678
+
679
+ public static extractTextLines ( text : string , lineStart : number , lineEnd : number ) {
680
+ return text . split ( '\n' ) . slice ( lineStart , lineEnd + 1 ) . join ( '\n' ) ;
681
+ }
682
+
683
+ public static findFirstMarker ( markers :string [ ] , text :string , startPos = 0 ) : IMarkerPosition | null {
684
+
685
+ let firstMarkerPos : number | null = null ;
686
+ let firstMarker : string | null = null ;
687
+
688
+ markers . forEach ( m => {
689
+ const pos = text . indexOf ( m , startPos ) ;
690
+ //console.debug({m,pos});
691
+ if ( pos != - 1 && ( firstMarkerPos == null || pos < firstMarkerPos ) ) {
692
+ firstMarkerPos = pos ;
693
+ firstMarker = m ;
694
+ }
695
+ } ) ;
696
+
697
+ if ( firstMarker == null || firstMarkerPos == null ) {
698
+ return null ;
699
+ }
700
+
701
+ return {
702
+ marker : firstMarker ,
703
+ position : firstMarkerPos
704
+ } ;
705
+ }
706
+
707
+
708
+
520
709
}
0 commit comments