lexgui 0.1.20 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,14 +21,33 @@ function swapArrayElements( array, id0, id1 ) {
21
21
  [array[id0], array[id1]] = [array[id1], array[id0]];
22
22
  };
23
23
 
24
- function sliceChar( str, idx ) {
25
- return str.substr(0, idx) + str.substr(idx + 1);
24
+ function sliceChars( str, idx, n = 1 ) {
25
+ return str.substr(0, idx) + str.substr(idx + n);
26
26
  }
27
27
 
28
28
  function firstNonspaceIndex( str ) {
29
29
  return str.search(/\S|$/);
30
30
  }
31
31
 
32
+ function indexOfFrom( str, reg, from, reverse ) {
33
+
34
+ from = from ?? 0;
35
+
36
+ if( reverse )
37
+ {
38
+ str = str.substr( 0, from );
39
+ var k = from - 1;
40
+ while( str[ k ] && str[ k ] != reg )
41
+ k--;
42
+ return str[ k ] ? k : -1;
43
+ }
44
+ else
45
+ {
46
+ str = str.substr( from );
47
+ return from + str.indexOf( reg );
48
+ }
49
+ }
50
+
32
51
  function deleteElement( el ) {
33
52
  if( el ) el.remove();
34
53
  }
@@ -44,10 +63,11 @@ function doAsync( fn, ms ) {
44
63
 
45
64
  class CodeSelection {
46
65
 
47
- constructor( editor, ix, iy ) {
66
+ constructor( editor, ix, iy, className = "lexcodeselection" ) {
48
67
 
49
68
  this.editor = editor;
50
69
  this.chars = 0;
70
+ this.className = className;
51
71
 
52
72
  this.fromX = ix;
53
73
  this.toX = ix;
@@ -88,7 +108,7 @@ class CodeSelection {
88
108
  this.fromY = this.toY = y;
89
109
 
90
110
  var domEl = document.createElement( 'div' );
91
- domEl.className = "lexcodeselection";
111
+ domEl.className = this.className;
92
112
 
93
113
  domEl._top = y * this.editor.lineHeight;
94
114
  domEl.style.top = domEl._top + "px";
@@ -281,7 +301,7 @@ class CodeEditor {
281
301
 
282
302
  if( !this.disableEdition )
283
303
  {
284
- this.root.addEventListener( 'keydown', this.processKey.bind( this), true );
304
+ this.root.addEventListener( 'keydown', this.processKey.bind( this) );
285
305
  this.root.addEventListener( 'focus', this.processFocus.bind( this, true ) );
286
306
  this.root.addEventListener( 'focusout', this.processFocus.bind( this, false ) );
287
307
  }
@@ -427,6 +447,29 @@ class CodeEditor {
427
447
  this.isAutoCompleteActive = false;
428
448
  }
429
449
 
450
+ // Add search box
451
+ {
452
+ var box = document.createElement( 'div' );
453
+ box.className = "searchbox";
454
+
455
+ var searchPanel = new LX.Panel();
456
+ box.appendChild( searchPanel.root );
457
+
458
+ searchPanel.sameLine( 4 );
459
+ searchPanel.addText( null, "", null, { placeholder: "Find" } );
460
+ searchPanel.addButton( null, "up", () => this.search( null, true ), { className: 'micro', icon: "fa fa-arrow-up" } );
461
+ searchPanel.addButton( null, "down", () => this.search(), { className: 'micro', icon: "fa fa-arrow-down" } );
462
+ searchPanel.addButton( null, "x", this.hideSearchBox.bind( this ), { className: 'micro', icon: "fa fa-xmark" } );
463
+
464
+ box.querySelector( 'input' ).addEventListener( 'keyup', e => {
465
+ if( e.key == 'Escape' ) this.hideSearchBox();
466
+ else if( e.key == 'Enter' ) this.search( e.target.value, !!e.shiftKey );
467
+ } );
468
+
469
+ this.searchbox = box;
470
+ this.tabs.area.attach( box );
471
+ }
472
+
430
473
  // Add code-sizer
431
474
  {
432
475
  this.codeSizer = document.createElement( 'div' );
@@ -458,7 +501,8 @@ class CodeEditor {
458
501
  this.tabSpaces = 4;
459
502
  this.maxUndoSteps = 16;
460
503
  this.lineHeight = 20;
461
- this.defaultSingleLineCommentToken = "//";
504
+ this.defaultSingleLineCommentToken = '//';
505
+ this.defaultBlockCommentTokens = [ '/*', '*/' ];
462
506
  this.charWidth = 7; //this._measureChar();
463
507
  this._lastTime = null;
464
508
 
@@ -479,18 +523,19 @@ class CodeEditor {
479
523
  // setInterval( this.scanWordSuggestions.bind( this ), 2000 );
480
524
 
481
525
  this.languages = {
482
- 'Plain Text': { ext: 'txt' },
526
+ 'Plain Text': { ext: 'txt', blockComments: false, singleLineComments: false },
483
527
  'JavaScript': { ext: 'js' },
484
528
  'C++': { ext: 'cpp' },
485
529
  'CSS': { ext: 'css' },
486
530
  'GLSL': { ext: 'glsl' },
487
531
  'WGSL': { ext: 'wgsl' },
488
- 'JSON': { ext: 'json' },
489
- 'XML': { ext: 'xml' },
532
+ 'JSON': { ext: 'json', blockComments: false, singleLineComments: false },
533
+ 'XML': { ext: 'xml', tags: true },
490
534
  'Rust': { ext: 'rs' },
491
535
  'Python': { ext: 'py', singleLineCommentToken: '#' },
492
- 'HTML': { ext: 'html' },
493
- 'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::' }
536
+ 'HTML': { ext: 'html', tags: true, singleLineComments: false, blockCommentsTokens: [ '<!--', '-->' ] },
537
+ 'Batch': { ext: 'bat', blockComments: false, singleLineCommentToken: '::' },
538
+ 'Markdown': { ext: 'md', blockComments: false, singleLineCommentToken: '::', tags: true }
494
539
  };
495
540
 
496
541
  this.specialKeys = [
@@ -516,7 +561,8 @@ class CodeEditor {
516
561
  'Python': ['False', 'def', 'None', 'True', 'in', 'is', 'and', 'lambda', 'nonlocal', 'not', 'or'],
517
562
  'Batch': ['set', 'SET', 'echo', 'ECHO', 'off', 'OFF', 'del', 'DEL', 'defined', 'DEFINED', 'setlocal', 'SETLOCAL', 'enabledelayedexpansion', 'ENABLEDELAYEDEXPANSION', 'driverquery',
518
563
  'DRIVERQUERY', 'print', 'PRINT'],
519
- 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head'],
564
+ 'HTML': ['html', 'meta', 'title', 'link', 'script', 'body', 'DOCTYPE', 'head', 'br', 'i', 'a', 'li', 'img', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
565
+ 'Markdown': ['br', 'i', 'a', 'li', 'img', 'table', 'title', 'tr', 'td', 'h1', 'h2', 'h3', 'h4', 'h5'],
520
566
  };
521
567
  this.utils = { // These ones don't have hightlight, used as suggestions to autocomplete only...
522
568
  'JavaScript': ['querySelector', 'body', 'addEventListener', 'removeEventListener', 'remove', 'sort', 'keys', 'filter', 'isNaN', 'parseFloat', 'parseInt', 'EPSILON', 'isFinite',
@@ -541,7 +587,8 @@ class CodeEditor {
541
587
  'JavaScript': ['document', 'console', 'window', 'navigator', 'performance'],
542
588
  'CSS': ['*', '!important'],
543
589
  'C++': ['vector', 'list', 'map'],
544
- 'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'lang', 'href', 'rel', 'content', 'xml'], // attributes
590
+ 'HTML': ['type', 'xmlns', 'PUBLIC', 'http-equiv', 'src', 'style', 'lang', 'href', 'rel', 'content', 'xml', 'alt'], // attributes
591
+ 'Markdown': ['type', 'src', 'style', 'lang', 'href', 'rel', 'content', 'valign', 'alt'], // attributes
545
592
  };
546
593
  this.statementsAndDeclarations = {
547
594
  'JavaScript': ['for', 'if', 'else', 'case', 'switch', 'return', 'while', 'continue', 'break', 'do', 'import', 'from', 'throw', 'async', 'try', 'catch', 'await'],
@@ -579,6 +626,7 @@ class CodeEditor {
579
626
 
580
627
  this.action( 'Escape', false, ( ln, cursor, e ) => {
581
628
  this.hideAutoCompleteBox();
629
+ this.hideSearchBox();
582
630
  });
583
631
 
584
632
  this.action( 'Backspace', false, ( ln, cursor, e ) => {
@@ -595,16 +643,37 @@ class CodeEditor {
595
643
  }
596
644
  }
597
645
  else {
646
+
598
647
  var letter = this.getCharAtPos( cursor, -1 );
599
648
  if( letter ) {
600
- this.code.lines[ ln ] = sliceChar( this.code.lines[ ln ], cursor.position - 1 );
601
- this.cursorToLeft( letter );
649
+
650
+ var deleteFromPosition = cursor.position - 1;
651
+ var numCharsDeleted = 1;
652
+
653
+ // Delete full word
654
+ if( e.shiftKey )
655
+ {
656
+ const [word, from, to] = this.getWordAtPos( cursor, -1 );
657
+
658
+ if( word.length > 1 )
659
+ {
660
+ deleteFromPosition = from;
661
+ numCharsDeleted = word.length;
662
+ }
663
+ }
664
+
665
+ this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], deleteFromPosition, numCharsDeleted );
602
666
  this.processLine( ln );
667
+
668
+ this.cursorToPosition( cursor, deleteFromPosition );
669
+
603
670
  if( this.useAutoComplete )
604
671
  this.showAutoCompleteBox( 'foo', cursor );
605
672
  }
606
673
  else if( this.code.lines[ ln - 1 ] != undefined ) {
674
+
607
675
  this.lineUp();
676
+ e.cancelShift = true;
608
677
  this.actions[ 'End' ].callback( cursor.line, cursor, e );
609
678
  // Move line on top
610
679
  this.code.lines[ ln - 1 ] += this.code.lines[ ln ];
@@ -626,7 +695,7 @@ class CodeEditor {
626
695
  {
627
696
  var letter = this.getCharAtPos( cursor );
628
697
  if( letter ) {
629
- this.code.lines[ ln ] = sliceChar( this.code.lines[ ln ], cursor.position );
698
+ this.code.lines[ ln ] = sliceChars( this.code.lines[ ln ], cursor.position );
630
699
  this.processLine( ln );
631
700
  }
632
701
  else if(this.code.lines[ ln + 1 ] != undefined) {
@@ -1741,7 +1810,7 @@ class CodeEditor {
1741
1810
 
1742
1811
  async processKey( e ) {
1743
1812
 
1744
- if( !this.code )
1813
+ if( !this.code || e.srcElement.constructor != HTMLDivElement )
1745
1814
  return;
1746
1815
 
1747
1816
  var key = e.key ?? e.detail.key;
@@ -1783,6 +1852,10 @@ class CodeEditor {
1783
1852
  this.processLines();
1784
1853
  this.hideAutoCompleteBox();
1785
1854
  return;
1855
+ case 'f': // find/search
1856
+ e.preventDefault();
1857
+ this.showSearchBox();
1858
+ return;
1786
1859
  case 's': // save
1787
1860
  e.preventDefault();
1788
1861
  this.onsave( this.getText() );
@@ -2080,6 +2153,7 @@ class CodeEditor {
2080
2153
 
2081
2154
  processLine( linenum, force ) {
2082
2155
 
2156
+ const lang = this.languages[ this.highlight ];
2083
2157
  const local_line_num = this.toLocalLine( linenum );
2084
2158
  const gutter_line = "<span class='line-gutter'>" + (linenum + 1) + "</span>";
2085
2159
 
@@ -2096,6 +2170,7 @@ class CodeEditor {
2096
2170
  // multi-line strings not supported by now
2097
2171
  delete this._buildingString;
2098
2172
  delete this._pendingString;
2173
+ delete this._markdownHeader;
2099
2174
 
2100
2175
  let linestring = this.code.lines[ linenum ];
2101
2176
 
@@ -2138,44 +2213,36 @@ class CodeEditor {
2138
2213
 
2139
2214
  const token = tokensToEvaluate[ i ];
2140
2215
 
2141
- if( this.languages[ this.highlight ].blockComments ?? true )
2216
+ if( lang.blockComments ?? true )
2142
2217
  {
2143
- if( token.substr( 0, 2 ) == '/*' )
2218
+ const blockCommentsToken = ( lang.blockCommentsTokens ?? this.defaultBlockCommentTokens )[ 0 ];
2219
+ if( token.substr( 0, blockCommentsToken.length ) == blockCommentsToken )
2144
2220
  this._buildingBlockComment = true;
2145
2221
  }
2146
2222
 
2147
- line_inner_html += this._evaluateToken( token, prev, next, (i == tokensToEvaluate.length - 1) );
2223
+ line_inner_html += this._evaluateToken( {
2224
+ token: token,
2225
+ prev: prev,
2226
+ prevWithSpaces: tokensToEvaluate[ i - 1 ],
2227
+ next: next,
2228
+ nextWithSpaces: tokensToEvaluate[ i + 1 ],
2229
+ tokenIndex: i,
2230
+ isFirstToken: (i == 0),
2231
+ isLastToken: (i == tokensToEvaluate.length - 1)
2232
+ } );
2148
2233
  }
2149
2234
 
2150
2235
  return UPDATE_LINE( line_inner_html );
2151
2236
  }
2152
2237
 
2153
- _processTokens( tokens, offset = 0 ) {
2154
-
2155
- if( this.highlight == 'C++' || this.highlight == 'CSS' )
2156
- {
2157
- var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.isNumber( value ) );
2158
- if( idx > -1 )
2159
- {
2160
- idx += offset; // Add offset to compute within the whole array of tokens
2161
- let data = tokens[ idx ] + tokens[ ++idx ];
2162
- while( this.isNumber( data ) )
2163
- {
2164
- tokens[ idx - 1 ] += tokens[ idx ];
2165
- tokens.splice( idx, 1 );
2166
- data += tokens[ idx ];
2167
- }
2168
- // Scan for numbers again
2169
- return this._processTokens( tokens, idx );
2170
- }
2171
- }
2238
+ _lineHasComment( linestring ) {
2172
2239
 
2173
- return tokens;
2174
- }
2240
+ const lang = this.languages[ this.highlight ];
2175
2241
 
2176
- _lineHasComment( linestring ) {
2242
+ if( !(lang.singleLineComments ?? true) )
2243
+ return;
2177
2244
 
2178
- const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2245
+ const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2179
2246
  const idx = linestring.indexOf( singleLineCommentToken );
2180
2247
 
2181
2248
  if( idx > -1 )
@@ -2199,6 +2266,8 @@ class CodeEditor {
2199
2266
 
2200
2267
  _getTokensFromLine( linestring, skipNonWords ) {
2201
2268
 
2269
+ this._currentLineString = linestring;
2270
+
2202
2271
  // Check if line comment
2203
2272
  const ogLine = linestring;
2204
2273
  const hasCommentIdx = this._lineHasComment( linestring );
@@ -2209,14 +2278,19 @@ class CodeEditor {
2209
2278
  }
2210
2279
 
2211
2280
  let tokensToEvaluate = []; // store in a temp array so we know prev and next tokens...
2281
+ let charCounterList = [];
2282
+ let charCounter = 0;
2212
2283
 
2213
2284
  const pushToken = function( t ) {
2214
2285
  if( (skipNonWords && ( t.includes('"') || t.length < 3 )) )
2215
2286
  return;
2216
2287
  tokensToEvaluate.push( t );
2288
+ charCounterList.push( charCounter );
2289
+ // Update positions
2290
+ charCounter += t.length;
2217
2291
  };
2218
2292
 
2219
- let iter = linestring.matchAll(/(\*\/|\/\*|::|[\[\](){}<>.,;:*"'%@!/= ])/g);
2293
+ let iter = linestring.matchAll(/(<!--|-->|\*\/|\/\*|::|[\[\](){}<>.,;:*"'%@!/= ])/g);
2220
2294
  let subtokens = iter.next();
2221
2295
  if( subtokens.value )
2222
2296
  {
@@ -2234,47 +2308,103 @@ class CodeEditor {
2234
2308
  }
2235
2309
  }
2236
2310
  }
2237
- else tokensToEvaluate.push( linestring );
2311
+ else pushToken( linestring );
2238
2312
 
2239
2313
  if( hasCommentIdx != undefined )
2240
2314
  {
2241
2315
  pushToken( ogLine.substring( hasCommentIdx ) );
2242
2316
  }
2243
2317
 
2318
+ this._currentTokenPositions = charCounterList;
2319
+
2244
2320
  return this._processTokens( tokensToEvaluate );
2245
2321
  }
2246
2322
 
2323
+ _processTokens( tokens, offset = 0 ) {
2324
+
2325
+ if( this.highlight == 'C++' || this.highlight == 'CSS' )
2326
+ {
2327
+ var idx = tokens.slice( offset ).findIndex( ( value, index ) => this.isNumber( value ) );
2328
+ if( idx > -1 )
2329
+ {
2330
+ idx += offset; // Add offset to compute within the whole array of tokens
2331
+ let data = tokens[ idx ] + tokens[ ++idx ];
2332
+ while( this.isNumber( data ) )
2333
+ {
2334
+ tokens[ idx - 1 ] += tokens[ idx ];
2335
+ tokens.splice( idx, 1 );
2336
+ data += tokens[ idx ];
2337
+ }
2338
+ // Scan for numbers again
2339
+ return this._processTokens( tokens, idx );
2340
+ }
2341
+ }
2342
+
2343
+ return tokens;
2344
+ }
2345
+
2247
2346
  _mustHightlightWord( token, kindArray ) {
2248
2347
 
2249
2348
  return kindArray[this.highlight] && kindArray[this.highlight][token] != undefined;
2250
2349
  }
2251
2350
 
2252
- _evaluateToken( token, prev, next, isLastToken ) {
2351
+ _evaluateToken( ctxData ) {
2352
+
2353
+ let token = ctxData.token,
2354
+ prev = ctxData.prev,
2355
+ next = ctxData.next,
2356
+ tokenIndex = ctxData.tokenIndex,
2357
+ isFirstToken = ctxData.isFirstToken,
2358
+ isLastToken = ctxData.isLastToken;
2359
+
2360
+ const lang = this.languages[ this.highlight ],
2361
+ highlight = this.highlight.replace( /\s/g, '' ).replaceAll( "+", "p" ).toLowerCase(),
2362
+ customStringKeys = Object.assign( {}, this.stringKeys );
2253
2363
 
2254
- const highlight = this.highlight.replace(/\s/g, '').replaceAll("+", "p").toLowerCase();
2255
- const customStringKeys = Object.assign( {}, this.stringKeys );
2364
+ var usePreviousTokenToCheckString = false;
2256
2365
 
2257
- if( highlight == 'cpp' && prev && prev.includes('#') ) // preprocessor code..
2366
+ if( highlight == 'cpp' && prev && prev.includes( '#' ) ) // preprocessor code..
2258
2367
  {
2259
2368
  customStringKeys['@<'] = '>';
2369
+ }
2370
+ else if( highlight == 'markdown' && ( ctxData.prevWithSpaces == '[' || ctxData.nextWithSpaces == ']' ) )
2371
+ {
2372
+ // console.warn(prev, token, next)
2373
+ usePreviousTokenToCheckString = true;
2374
+ customStringKeys['@['] = ']';
2260
2375
  }
2261
2376
 
2262
2377
  // Manage strings
2263
2378
  this._stringEnded = false;
2264
- if( this._buildingString != undefined )
2379
+
2380
+ if( usePreviousTokenToCheckString || ( !this._buildingBlockComment && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) ) )
2265
2381
  {
2266
- const idx = Object.values(customStringKeys).indexOf( token );
2267
- this._stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
2268
- }
2269
- else if( customStringKeys[ '@' + token ] )
2270
- {
2271
- // Start new string
2272
- this._buildingString = token;
2273
- }
2382
+ const checkIfStringEnded = t => {
2383
+ const idx = Object.values( customStringKeys ).indexOf( t );
2384
+ this._stringEnded = (idx > -1) && (idx == Object.values(customStringKeys).indexOf( customStringKeys[ '@' + this._buildingString ] ));
2385
+ };
2274
2386
 
2275
- const usesBlockComments = this.languages[ this.highlight ].blockComments ?? true;
2387
+ if( this._buildingString != undefined )
2388
+ {
2389
+ checkIfStringEnded( usePreviousTokenToCheckString ? ctxData.nextWithSpaces : token );
2390
+ }
2391
+ else if( customStringKeys[ '@' + ( usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token ) ] )
2392
+ {
2393
+ // Start new string
2394
+ this._buildingString = ( usePreviousTokenToCheckString ? ctxData.prevWithSpaces : token );
2276
2395
 
2277
- if(token == ' ')
2396
+ // Check if string ended in same token using next...
2397
+ if( usePreviousTokenToCheckString )
2398
+ {
2399
+ checkIfStringEnded( ctxData.nextWithSpaces );
2400
+ }
2401
+ }
2402
+ }
2403
+
2404
+ const usesBlockComments = lang.blockComments ?? true;
2405
+ const blockCommentsTokens = lang.blockCommentsTokens ?? this.defaultBlockCommentTokens;
2406
+
2407
+ if( !usePreviousTokenToCheckString && token == ' ' )
2278
2408
  {
2279
2409
  if( this._buildingString != undefined )
2280
2410
  {
@@ -2285,7 +2415,7 @@ class CodeEditor {
2285
2415
  }
2286
2416
  else
2287
2417
  {
2288
- const singleLineCommentToken = this.languages[ this.highlight ].singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2418
+ const singleLineCommentToken = lang.singleLineCommentToken ?? this.defaultSingleLineCommentToken;
2289
2419
 
2290
2420
  let token_classname = "";
2291
2421
  let discardToken = false;
@@ -2296,10 +2426,10 @@ class CodeEditor {
2296
2426
  else if( this._buildingString != undefined )
2297
2427
  discardToken = this._appendStringToken( token );
2298
2428
 
2299
- else if( this._mustHightlightWord( token, this.keywords ) )
2429
+ else if( this._mustHightlightWord( token, this.keywords ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2300
2430
  token_classname = "cm-kwd";
2301
2431
 
2302
- else if( this._mustHightlightWord( token, this.builtin ) )
2432
+ else if( this._mustHightlightWord( token, this.builtin ) && ( lang.tags ?? false ? ( this._enclosedByTokens( token, tokenIndex, '<', '>' ) ) : true ) )
2303
2433
  token_classname = "cm-bln";
2304
2434
 
2305
2435
  else if( this._mustHightlightWord( token, this.statementsAndDeclarations ) )
@@ -2337,12 +2467,19 @@ class CodeEditor {
2337
2467
 
2338
2468
  else if ( highlight == 'css' && prev == undefined && next == ':' ) // CSS attribute
2339
2469
  token_classname = "cm-typ";
2470
+
2471
+ else if ( this._markdownHeader || ( highlight == 'markdown' && isFirstToken && token.replaceAll('#', '').length != token.length ) ) // Header
2472
+ {
2473
+ token_classname = "cm-kwd";
2474
+ this._markdownHeader = true;
2475
+ }
2340
2476
 
2341
2477
  else if ( token[ 0 ] != '@' && token[ 0 ] != ',' && next == '(' )
2342
2478
  token_classname = "cm-mtd";
2343
2479
 
2344
2480
 
2345
- if( usesBlockComments && this._buildingBlockComment && token.substr( 0, 2 ) == '*/' )
2481
+ if( usesBlockComments && this._buildingBlockComment
2482
+ && token.substr( 0, blockCommentsTokens[ 1 ].length ) == blockCommentsTokens[ 1 ] )
2346
2483
  {
2347
2484
  delete this._buildingBlockComment;
2348
2485
  }
@@ -2389,6 +2526,19 @@ class CodeEditor {
2389
2526
  return chars;
2390
2527
  }
2391
2528
 
2529
+ _enclosedByTokens( token, tokenIndex, tagStart, tagEnd ) {
2530
+
2531
+ const tokenStartIndex = this._currentTokenPositions[ tokenIndex ];
2532
+ const tagStartIndex = indexOfFrom(this._currentLineString, tagStart, tokenStartIndex, true );
2533
+ if( tagStartIndex < 0 ) // Not found..
2534
+ return;
2535
+ const tagEndIndex = indexOfFrom(this._currentLineString, tagEnd, tokenStartIndex );
2536
+ if( tagEndIndex < 0 ) // Not found..
2537
+ return;
2538
+
2539
+ return ( tagStartIndex < tokenStartIndex ) && ( tagEndIndex >= ( tokenStartIndex + token.length ) );
2540
+ }
2541
+
2392
2542
  _isCSSClass( token, prev, next ) {
2393
2543
  return this.highlight == 'CSS' && prev == '.';
2394
2544
  }
@@ -3030,15 +3180,15 @@ class CodeEditor {
3030
3180
  suggestions = suggestions.filter( (value, index) => value.length > 2 && suggestions.indexOf(value) === index );
3031
3181
 
3032
3182
  // Order...
3033
- suggestions = suggestions.sort( (a, b) => a.localeCompare(b) );
3183
+ suggestions = suggestions.sort( ( a, b ) => a.localeCompare( b ) );
3034
3184
 
3035
3185
  for( let s of suggestions )
3036
3186
  {
3037
- if( !s.toLowerCase().includes(word.toLowerCase()) )
3187
+ if( !s.toLowerCase().includes( word.toLowerCase() ) )
3038
3188
  continue;
3039
3189
 
3040
3190
  var pre = document.createElement( 'pre' );
3041
- this.autocomplete.appendChild(pre);
3191
+ this.autocomplete.appendChild( pre );
3042
3192
 
3043
3193
  var icon = document.createElement( 'a' );
3044
3194
 
@@ -3049,27 +3199,27 @@ class CodeEditor {
3049
3199
  else
3050
3200
  icon.className = "fa fa-font";
3051
3201
 
3052
- pre.appendChild(icon);
3202
+ pre.appendChild( icon );
3053
3203
 
3054
3204
  pre.addEventListener( 'click', () => {
3055
3205
  this.autoCompleteWord( cursor, s );
3056
3206
  } );
3057
3207
 
3058
3208
  // Highlight the written part
3059
- const index = s.toLowerCase().indexOf(word.toLowerCase());
3209
+ const index = s.toLowerCase().indexOf( word.toLowerCase() );
3060
3210
 
3061
- var preWord = document.createElement('span');
3062
- preWord.innerHTML = s.substring(0, index);
3063
- pre.appendChild(preWord);
3211
+ var preWord = document.createElement( 'span' );
3212
+ preWord.innerHTML = s.substring( 0, index );
3213
+ pre.appendChild( preWord );
3064
3214
 
3065
3215
  var actualWord = document.createElement('span');
3066
- actualWord.innerHTML = s.substr(index, word.length);
3216
+ actualWord.innerHTML = s.substr( index, word.length );
3067
3217
  actualWord.classList.add( 'word-highlight' );
3068
- pre.appendChild(actualWord);
3218
+ pre.appendChild( actualWord );
3069
3219
 
3070
3220
  var postWord = document.createElement('span');
3071
- postWord.innerHTML = s.substring(index + word.length);
3072
- pre.appendChild(postWord);
3221
+ postWord.innerHTML = s.substring( index + word.length );
3222
+ pre.appendChild( postWord );
3073
3223
  }
3074
3224
 
3075
3225
  if( !this.autocomplete.childElementCount )
@@ -3156,6 +3306,113 @@ class CodeEditor {
3156
3306
  this.autocomplete.childNodes[ idx + offset ].classList.add('selected');
3157
3307
  }
3158
3308
 
3309
+ showSearchBox( clear ) {
3310
+
3311
+ this.searchbox.classList.add( 'opened' );
3312
+ this.searchboxActive = true;
3313
+
3314
+ const input = this.searchbox.querySelector( 'input' );
3315
+
3316
+ if( clear )
3317
+ {
3318
+ input.value = "";
3319
+ }
3320
+
3321
+ input.focus();
3322
+ }
3323
+
3324
+ hideSearchBox() {
3325
+
3326
+ if( this.searchboxActive )
3327
+ {
3328
+ this.searchbox.classList.remove( 'opened' );
3329
+ this.searchboxActive = false;
3330
+ }
3331
+
3332
+ else if( this._lastResult )
3333
+ {
3334
+ this._lastResult.dom.remove();
3335
+ delete this._lastResult;
3336
+ }
3337
+ }
3338
+
3339
+ search( text, reverse ) {
3340
+
3341
+ text = text ?? this._lastTextFound;
3342
+
3343
+ if( !text )
3344
+ return;
3345
+
3346
+ let cursorData = new LX.vec2( this.position, this.line );
3347
+ let found = null;
3348
+ let idx = -1;
3349
+
3350
+ if( this._lastResult )
3351
+ {
3352
+ this._lastResult.dom.remove();
3353
+ cursorData = this._lastResult.pos;
3354
+ delete this._lastResult;
3355
+ }
3356
+
3357
+ const getIndex = line => {
3358
+ return this.code.lines[ line ].substr( line == cursorData.y ? cursorData.x : 0 ).indexOf( text );
3359
+ };
3360
+
3361
+ if( reverse )
3362
+ {
3363
+ for( var j = cursorData.y; j >= 0; --j )
3364
+ {
3365
+ idx = getIndex( j );
3366
+ if( idx > -1 )
3367
+ {
3368
+ found = j;
3369
+ break;
3370
+ }
3371
+ }
3372
+ }
3373
+ else
3374
+ {
3375
+ for( var j = cursorData.y; j < this.code.lines.length; ++j )
3376
+ {
3377
+ idx = getIndex( j );
3378
+ if( idx > -1 )
3379
+ {
3380
+ found = j;
3381
+ break;
3382
+ }
3383
+ }
3384
+ }
3385
+
3386
+ if( found == null)
3387
+ {
3388
+ alert("No results!")
3389
+ return;
3390
+ }
3391
+
3392
+ // Text found..
3393
+
3394
+ this._lastTextFound = text;
3395
+
3396
+ // console.warn("FOUND!! --", text, "-- at", "[" + idx + ", " + j + "]")
3397
+
3398
+ this.codeScroller.scrollTo(
3399
+ Math.max( idx * this.charWidth - this.codeScroller.clientWidth ),
3400
+ Math.max( found - 10 ) * this.lineHeight
3401
+ );
3402
+
3403
+ // Show elements
3404
+ this.selections.classList.add( 'show' );
3405
+
3406
+ // Create new selection instance
3407
+ this.selection = new CodeSelection( this, 0, 0, "lexcodesearchresult" );
3408
+ this.selection.selectInline( idx, found, this.measureString( text ) );
3409
+ this._lastResult = {
3410
+ 'dom': this.selections.lastChild,
3411
+ 'pos': new LX.vec2( idx + text.length, found )
3412
+ };
3413
+
3414
+ }
3415
+
3159
3416
  _updateDataInfoPanel( signal, value ) {
3160
3417
 
3161
3418
  if( !this.skipCodeInfo )
@@ -3190,9 +3447,11 @@ class CodeEditor {
3190
3447
 
3191
3448
  _clearTmpVariables() {
3192
3449
 
3450
+ delete this._currentLineString;
3193
3451
  delete this._buildingString;
3194
3452
  delete this._pendingString;
3195
3453
  delete this._buildingBlockComment;
3454
+ delete this._markdownHeader;
3196
3455
  }
3197
3456
  }
3198
3457
 
package/build/lexgui.css CHANGED
@@ -2971,7 +2971,7 @@ pre .line-gutter {
2971
2971
  background-color: #bbbbbb8c !important;
2972
2972
  }
2973
2973
 
2974
- .lexcodeeditor .lexcodeselection {
2974
+ .lexcodeeditor .lexcodeselection, .lexcodeeditor .lexcodesearchresult {
2975
2975
  -webkit-text-size-adjust: 100%;
2976
2976
  font-family: monospace;
2977
2977
  color: #AAA !important;
@@ -2992,6 +2992,34 @@ pre .line-gutter {
2992
2992
  opacity: 0.4;
2993
2993
  }
2994
2994
 
2995
+ .lexcodeeditor .lexcodesearchresult {
2996
+ background-color: #f5f115;
2997
+ opacity: 0.5;
2998
+ }
2999
+
3000
+ .lexcodeeditor .searchbox {
3001
+ background-color: var(--global-branch-darker);
3002
+ width: 256px;
3003
+ position: absolute;
3004
+ right: 6px;
3005
+ top: 26px;
3006
+ z-index: 100;
3007
+ border-radius: 4px;
3008
+ border: 1px solid var(--global-button-color);
3009
+ box-shadow: 0 0px 4px #101010;
3010
+ overflow-y: scroll;
3011
+ transform: translateY(-72px);
3012
+ transition: transform 0.2s ease-in;
3013
+ }
3014
+
3015
+ .lexcodeeditor .searchbox.opened {
3016
+ transform: translateY(0px);
3017
+ }
3018
+
3019
+ .lexcodeeditor .searchbox .lexpanel span {
3020
+ height: 11px;
3021
+ }
3022
+
2995
3023
  .lexcodeeditor .autocomplete {
2996
3024
  background-color: var(--global-branch-darker);
2997
3025
  width: 256px;
@@ -3020,6 +3048,7 @@ pre .line-gutter {
3020
3048
  pointer-events: unset;
3021
3049
  cursor: default;
3022
3050
  height: 22px;
3051
+ padding-left: 6px;
3023
3052
  }
3024
3053
 
3025
3054
  .lexcodeeditor .autocomplete pre a {
@@ -3050,102 +3079,64 @@ pre .line-gutter {
3050
3079
  color: #95a0e1;
3051
3080
  }
3052
3081
 
3053
- .cm-str.javascript { color: #ca7d59; } /* string */
3054
- .cm-kwd.javascript { color: #218cce; } /* keyword */
3055
- .cm-com.javascript { color: #5cab5a; } /* comment */
3056
- .cm-typ.javascript { color: #36c0b0; } /* type */
3057
- .cm-std.javascript { color: #cf6dcf; } /* statements & declarations */
3058
- .cm-bln.javascript { color: inherit; } /* builtin */
3059
- .cm-dec.javascript { color: #b1ce9b; } /* decimal */
3060
- .cm-sym.javascript { color: #e7ded2; } /* symbol */
3061
- .cm-mtd.javascript { color: #e0cc68 } /* method */
3062
-
3063
- .cm-str.cpp { color: #ca7d59; } /* string */
3064
- .cm-kwd.cpp { color: #218cce; } /* keyword */
3065
- .cm-com.cpp { color: #5cab5a; } /* comment */
3066
- .cm-typ.cpp { color: #36c0b0; } /* type */
3067
- .cm-std.cpp { color: #cf6dcf; } /* statements & declarations */
3068
- .cm-bln.cpp { color: #d44141; } /* builtin */
3069
- .cm-dec.cpp { color: #2ddf53; } /* decimal */
3070
- .cm-sym.cpp { color: #e7ded2; } /* symbol */
3071
- .cm-mtd.cpp { color: #7a9ae0 } /* method */
3072
- .cm-ppc.cpp { color: #969696 } /* preprocessor */
3073
-
3074
- .cm-str.css { color: #ca7d59; } /* string */
3075
- .cm-kwd.css { color: #e8be53; } /* keyword */
3076
- .cm-com.css { color: #5cab5a; } /* comment */
3077
- .cm-typ.css { color: #b7c3ec; } /* type */
3078
- .cm-std.css { color: #cf6dcf; } /* statements & declarations */
3079
- .cm-bln.css { color: #2194ce; } /* builtin */
3080
- .cm-dec.css { color: #b1ce9b; } /* decimal */
3081
- .cm-sym.css { color: #f9d620; } /* symbol */
3082
- .cm-mtd.css { color: #e0cc68; } /* method */
3083
-
3084
- .cm-str.json { color: #ca7d59; } /* string */
3085
- .cm-kwd.json { color: inherit; } /* keyword */
3086
- .cm-com.json { color: inherit; } /* comment */
3087
- .cm-typ.json { color: inherit; } /* type */
3088
- .cm-std.json { color: inherit; } /* statements & declarations */
3089
- .cm-bln.json { color: inherit; } /* builtin */
3090
- .cm-dec.json { color: #b1ce9b; } /* decimal */
3091
- .cm-sym.json { color: #cf6dcf; } /* symbol */
3092
- .cm-mtd.json { color: inherit; } /* method */
3093
-
3094
- .cm-str.glsl { color: #ca7d59; } /* string */
3095
- .cm-kwd.glsl { color: #2194ce; } /* keyword */
3096
- .cm-com.glsl { color: #5cab5a; } /* comment */
3097
- .cm-typ.glsl { color: #36c0b0; } /* type */
3098
- .cm-std.glsl { color: #cf6dcf; } /* statements & declarations */
3099
- .cm-bln.glsl { color: #cfc159; } /* builtin */
3100
- .cm-dec.glsl { color: #b1ce9b; } /* decimal */
3101
- .cm-sym.glsl { color: #f9cb20; } /* symbol */
3102
- .cm-mtd.glsl { color: #e0cc68; } /* method */
3103
-
3104
- .cm-str.wgsl { color: #ca7d59; } /* string */
3105
- .cm-kwd.wgsl { color: #2194ce; } /* keyword */
3106
- .cm-com.wgsl { color: #5cab5a; } /* comment */
3107
- .cm-typ.wgsl { color: #36c0b0; } /* type */
3108
- .cm-std.wgsl { color: #cf6dcf; } /* statements & declarations */
3109
- .cm-bln.wgsl { color: #cfc159; } /* builtin */
3110
- .cm-dec.wgsl { color: #b1ce9b; } /* decimal */
3111
- .cm-sym.wgsl { color: #f9cb20; } /* symbol */
3112
- .cm-mtd.wgsl { color: #e0cc68; } /* method */
3113
-
3114
- .cm-str.rust { color: #ca7d59; } /* string */
3115
- .cm-kwd.rust { color: #218cce; } /* keyword */
3116
- .cm-com.rust { color: #5cab5a; } /* comment */
3117
- .cm-typ.rust { color: #36c0b0; } /* type */
3118
- .cm-std.rust { color: #cf6dcf; } /* statements & declarations */
3119
- .cm-bln.rust { color: inherit; } /* builtin */
3120
- .cm-dec.rust { color: #b1ce9b; } /* decimal */
3121
- .cm-sym.rust { color: #e7ded2; } /* symbol */
3122
- .cm-mtd.rust { color: #e0cc68 } /* method */
3123
-
3124
- .cm-str.python { color: #ca7d59; } /* string */
3125
- .cm-kwd.python { color: #218cce; } /* keyword */
3126
- .cm-com.python { color: #5cab5a; } /* comment */
3127
- .cm-typ.python { color: #36c0b0; } /* type */
3128
- .cm-std.python { color: #cf6dcf; } /* statements & declarations */
3129
- .cm-bln.python { color: inherit; } /* builtin */
3130
- .cm-dec.python { color: #b1ce9b; } /* decimal */
3131
- .cm-sym.python { color: #e7ded2; } /* symbol */
3132
- .cm-mtd.python { color: #e0cc68 } /* method */
3133
-
3134
- .cm-str.batch { color: #ca7d59; } /* string */
3135
- .cm-kwd.batch { color: #218cce; } /* keyword */
3136
- .cm-com.batch { color: #5cab5a; } /* comment */
3137
- .cm-typ.batch { color: #36c0b0; } /* type */
3138
- .cm-std.batch { color: #cf6dcf; } /* statements & declarations */
3139
- .cm-bln.batch { color: inherit; } /* builtin */
3140
- .cm-dec.batch { color: #b1ce9b; } /* decimal */
3141
- .cm-sym.batch { color: #dfd85e; } /* symbol */
3142
- .cm-mtd.batch { color: inherit } /* method */
3143
-
3144
- .cm-str.html { color: #ca7d59; } /* string */
3145
- .cm-kwd.html { color: #2194ce; } /* keyword */
3146
- .cm-bln.html { color: #b4d7ec; } /* builtin */
3147
- .cm-sym.html { color: #929292; } /* symbol */
3148
-
3082
+ /* Common */
3083
+ .cm-str { color: #ca7d59; } /* string */
3084
+ .cm-std { color: #cf6dcf; } /* statements & declarations */
3085
+ .cm-kwd { color: #2194ce; } /* keyword */
3086
+ .cm-com { color: #5cab5a; } /* comment */
3087
+
3088
+ .cm-typ.javascript { color: #36c0b0; } /* type */
3089
+ .cm-dec.javascript { color: #b1ce9b; } /* decimal */
3090
+ .cm-sym.javascript { color: #e7ded2; } /* symbol */
3091
+ .cm-mtd.javascript { color: #e0cc68 } /* method */
3092
+
3093
+ .cm-typ.cpp { color: #36c0b0; } /* type */
3094
+ .cm-bln.cpp { color: #d44141; } /* builtin */
3095
+ .cm-dec.cpp { color: #2ddf53; } /* decimal */
3096
+ .cm-sym.cpp { color: #e7ded2; } /* symbol */
3097
+ .cm-mtd.cpp { color: #7a9ae0 } /* method */
3098
+ .cm-ppc.cpp { color: #969696 } /* preprocessor */
3099
+
3100
+ .cm-kwd.css { color: #e8be53; } /* keyword */
3101
+ .cm-typ.css { color: #b7c3ec; } /* type */
3102
+ .cm-bln.css { color: #2194ce; } /* builtin */
3103
+ .cm-dec.css { color: #b1ce9b; } /* decimal */
3104
+ .cm-sym.css { color: #f9d620; } /* symbol */
3105
+ .cm-mtd.css { color: #e0cc68; } /* method */
3106
+
3107
+ .cm-dec.json { color: #b1ce9b; } /* decimal */
3108
+ .cm-sym.json { color: #cf6dcf; } /* symbol */
3109
+
3110
+ .cm-typ.glsl { color: #36c0b0; } /* type */
3111
+ .cm-bln.glsl { color: #cfc159; } /* builtin */
3112
+ .cm-dec.glsl { color: #b1ce9b; } /* decimal */
3113
+ .cm-sym.glsl { color: #f9cb20; } /* symbol */
3114
+ .cm-mtd.glsl { color: #e0cc68; } /* method */
3115
+
3116
+ .cm-typ.wgsl { color: #36c0b0; } /* type */
3117
+ .cm-bln.wgsl { color: #cfc159; } /* builtin */
3118
+ .cm-dec.wgsl { color: #b1ce9b; } /* decimal */
3119
+ .cm-sym.wgsl { color: #f9cb20; } /* symbol */
3120
+ .cm-mtd.wgsl { color: #e0cc68; } /* method */
3121
+
3122
+ .cm-typ.rust { color: #36c0b0; } /* type */
3123
+ .cm-dec.rust { color: #b1ce9b; } /* decimal */
3124
+ .cm-sym.rust { color: #e7ded2; } /* symbol */
3125
+ .cm-mtd.rust { color: #e0cc68 } /* method */
3126
+
3127
+ .cm-typ.python { color: #36c0b0; } /* type */
3128
+ .cm-dec.python { color: #b1ce9b; } /* decimal */
3129
+ .cm-sym.python { color: #e7ded2; } /* symbol */
3130
+ .cm-mtd.python { color: #e0cc68 } /* method */
3131
+
3132
+ .cm-typ.batch { color: #36c0b0; } /* type */
3133
+ .cm-dec.batch { color: #b1ce9b; } /* decimal */
3134
+ .cm-sym.batch { color: #dfd85e; } /* symbol */
3135
+
3136
+ .cm-bln.html { color: #a1d2f0; } /* builtin */
3137
+ .cm-sym.html { color: #929292; } /* symbol */
3138
+
3139
+ .cm-bln.markdown { color: #a1d2f0; } /* builtin */
3149
3140
 
3150
3141
  /* Node Graph */
3151
3142
 
package/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = global.LX = {
15
- version: "0.1.20",
15
+ version: "0.1.21",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -3568,9 +3568,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3568
3568
  return wValue.innerText;
3569
3569
  };
3570
3570
  widget.onSetValue = (new_value) => {
3571
- wValue.innerHTML = "<span>" +
3571
+ wValue.innerHTML =
3572
3572
  (options.icon ? "<a class='" + options.icon + "'></a>" :
3573
- ( options.img ? "<img src='" + options.img + "'>" : (new_value || ""))) + "</span>";
3573
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (new_value || "") + "</span>" ));
3574
3574
  };
3575
3575
 
3576
3576
  let element = widget.domEl;
@@ -3583,9 +3583,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3583
3583
  wValue.classList.add("selected");
3584
3584
  if(options.buttonClass)
3585
3585
  wValue.classList.add(options.buttonClass);
3586
- wValue.innerHTML = "<span>" +
3586
+ wValue.innerHTML =
3587
3587
  (options.icon ? "<a class='" + options.icon + "'></a>" :
3588
- ( options.img ? "<img src='" + options.img + "'>" : (value || ""))) + "</span>";
3588
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
3589
3589
 
3590
3590
  wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
3591
3591
 
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.20",
11
+ version: "0.1.21",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -3564,9 +3564,9 @@ class Panel {
3564
3564
  return wValue.innerText;
3565
3565
  };
3566
3566
  widget.onSetValue = (new_value) => {
3567
- wValue.innerHTML = "<span>" +
3567
+ wValue.innerHTML =
3568
3568
  (options.icon ? "<a class='" + options.icon + "'></a>" :
3569
- ( options.img ? "<img src='" + options.img + "'>" : (new_value || ""))) + "</span>";
3569
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (new_value || "") + "</span>" ));
3570
3570
  };
3571
3571
 
3572
3572
  let element = widget.domEl;
@@ -3579,9 +3579,9 @@ class Panel {
3579
3579
  wValue.classList.add("selected");
3580
3580
  if(options.buttonClass)
3581
3581
  wValue.classList.add(options.buttonClass);
3582
- wValue.innerHTML = "<span>" +
3582
+ wValue.innerHTML =
3583
3583
  (options.icon ? "<a class='" + options.icon + "'></a>" :
3584
- ( options.img ? "<img src='" + options.img + "'>" : (value || ""))) + "</span>";
3584
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
3585
3585
 
3586
3586
  wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
3587
3587
 
package/changelog.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # lexgui.js changelog
2
2
 
3
+ ## 0.1.21
4
+
5
+ Added "Ctrl+F" to find text in code tabs.
6
+ "Shift+Backspace" deletes word at current position.
7
+ Added "Markdown" syntax highlighting.
8
+ Improved hightlighting of tag languages (HTML, Markdown, XML).
9
+
3
10
  ## 0.1.20
4
11
 
5
12
  Active line is now hightlighted.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.20",
3
+ "version": "0.1.21",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",