2020-05-22 05:48:27 -06:00
// NOTE: Notes for myself:
// # tkWidgets methods and getters
// innerheight: how much height the widget takes up
// height: how much height the parent widget takes up
// scrollTop_: index of scrolled lines from the top of the note
// maxScrollTop_: Number of lines I can scroll from the top
// scrollableHeight_: total height of the widget including what is not visible
// scrollUp and scrollDown change the scrollTop_ variable by 1 (up -1, down +1) however, their values can be changed for however much you want to scroll
// x/y will give you relative x/y to widget. absoluteX/absoluteY will give it to the terminal window
// .renderedText includes line breaks but also regex of the links. .text is just plain text with non-wrapped line breaks (\n).
// Blueprint:
// I can get the lines with \n but that doesn't account for line wrap.
// the rendered text link is wrapped: '\x1B[34m\x1B[4m' on either side. except 34m is 24m on the right
// term():
// you can use term.eraseArea() to erase the rectangle you need, then term('<string>') for whatever you want to write
// term('string') writes the string wherever the cursor is.
// Other:
// Use stdout.(_(<command>)) as your console.log. It will show in the app console.
2018-03-09 20:59:12 +00:00
const { Logger } = require ( 'lib/logger.js' ) ;
const Folder = require ( 'lib/models/Folder.js' ) ;
2018-05-09 12:50:50 +01:00
const BaseItem = require ( 'lib/models/BaseItem.js' ) ;
2018-03-09 20:59:12 +00:00
const Tag = require ( 'lib/models/Tag.js' ) ;
const BaseModel = require ( 'lib/BaseModel.js' ) ;
const Note = require ( 'lib/models/Note.js' ) ;
const Resource = require ( 'lib/models/Resource.js' ) ;
2020-05-09 20:42:09 +05:30
const Setting = require ( 'lib/models/Setting.js' ) ;
2018-03-09 20:59:12 +00:00
const { reducer , defaultState } = require ( 'lib/reducer.js' ) ;
const { splitCommandString } = require ( 'lib/string-utils.js' ) ;
const { reg } = require ( 'lib/registry.js' ) ;
const { _ } = require ( 'lib/locale.js' ) ;
2018-05-09 12:50:50 +01:00
const Entities = require ( 'html-entities' ) . AllHtmlEntities ;
2019-07-30 09:35:42 +02:00
const htmlentities = new Entities ( ) . encode ;
2020-05-22 05:48:27 -06:00
const open = require ( 'open' ) ;
2018-03-09 20:59:12 +00:00
const chalk = require ( 'chalk' ) ;
const tk = require ( 'terminal-kit' ) ;
const TermWrapper = require ( 'tkwidgets/framework/TermWrapper.js' ) ;
const Renderer = require ( 'tkwidgets/framework/Renderer.js' ) ;
const DecryptionWorker = require ( 'lib/services/DecryptionWorker' ) ;
const BaseWidget = require ( 'tkwidgets/BaseWidget.js' ) ;
const TextWidget = require ( 'tkwidgets/TextWidget.js' ) ;
const HLayoutWidget = require ( 'tkwidgets/HLayoutWidget.js' ) ;
const VLayoutWidget = require ( 'tkwidgets/VLayoutWidget.js' ) ;
const ReduxRootWidget = require ( 'tkwidgets/ReduxRootWidget.js' ) ;
const WindowWidget = require ( 'tkwidgets/WindowWidget.js' ) ;
const NoteWidget = require ( './gui/NoteWidget.js' ) ;
const ResourceServer = require ( './ResourceServer.js' ) ;
const NoteMetadataWidget = require ( './gui/NoteMetadataWidget.js' ) ;
const FolderListWidget = require ( './gui/FolderListWidget.js' ) ;
const NoteListWidget = require ( './gui/NoteListWidget.js' ) ;
const StatusBarWidget = require ( './gui/StatusBarWidget.js' ) ;
const ConsoleWidget = require ( './gui/ConsoleWidget.js' ) ;
2017-10-07 22:01:03 +01:00
2017-10-05 18:17:56 +01:00
class AppGui {
2018-02-16 22:53:53 +00:00
constructor ( app , store , keymap ) {
try {
this . app _ = app ;
this . store _ = store ;
BaseWidget . setLogger ( app . logger ( ) ) ;
this . term _ = new TermWrapper ( tk . terminal ) ;
// Some keys are directly handled by the tkwidget framework
// so they need to be remapped in a different way.
this . tkWidgetKeys _ = {
2019-07-30 09:35:42 +02:00
focus _next : 'TAB' ,
focus _previous : 'SHIFT_TAB' ,
move _up : 'UP' ,
move _down : 'DOWN' ,
page _down : 'PAGE_DOWN' ,
page _up : 'PAGE_UP' ,
2018-02-16 22:53:53 +00:00
} ;
2017-10-13 18:21:57 +00:00
2018-02-16 22:53:53 +00:00
this . renderer _ = null ;
this . logger _ = new Logger ( ) ;
this . buildUi ( ) ;
2017-10-07 22:01:03 +01:00
2018-02-16 22:53:53 +00:00
this . renderer _ = new Renderer ( this . term ( ) , this . rootWidget _ ) ;
2017-10-06 18:38:17 +01:00
2020-05-21 09:14:33 +01:00
this . app _ . on ( 'modelAction' , async event => {
2018-02-16 22:53:53 +00:00
await this . handleModelAction ( event . action ) ;
} ) ;
2017-10-07 17:30:27 +01:00
2018-02-16 22:53:53 +00:00
this . keymap _ = this . setupKeymap ( keymap ) ;
2017-10-08 23:34:01 +01:00
2018-02-16 22:53:53 +00:00
this . inputMode _ = AppGui . INPUT _MODE _NORMAL ;
2017-10-08 23:34:01 +01:00
2018-02-16 22:53:53 +00:00
this . commandCancelCalled _ = false ;
2017-10-14 19:03:23 +01:00
2018-02-16 22:53:53 +00:00
this . currentShortcutKeys _ = [ ] ;
this . lastShortcutKeyTime _ = 0 ;
2017-10-14 19:03:23 +01:00
2018-02-16 22:53:53 +00:00
// Recurrent sync is setup only when the GUI is started. In
// a regular command it's not necessary since the process
// exits right away.
reg . setupRecurrentSync ( ) ;
DecryptionWorker . instance ( ) . scheduleStart ( ) ;
} catch ( error ) {
2019-08-29 12:05:20 -04:00
if ( this . term _ ) { this . fullScreen ( false ) ; }
2018-02-16 22:53:53 +00:00
console . error ( error ) ;
process . exit ( 1 ) ;
}
2017-10-14 19:03:23 +01:00
}
2017-10-23 22:48:29 +01:00
store ( ) {
return this . store _ ;
}
2017-10-14 19:03:23 +01:00
renderer ( ) {
return this . renderer _ ;
2017-10-06 18:38:17 +01:00
}
2017-10-05 18:17:56 +01:00
2017-10-14 22:44:50 +01:00
async forceRender ( ) {
2018-03-09 20:59:12 +00:00
this . widget ( 'root' ) . invalidate ( ) ;
2017-10-14 22:44:50 +01:00
await this . renderer _ . renderRoot ( ) ;
}
2017-12-28 19:14:03 +00:00
termSaveState ( ) {
return this . term ( ) . saveState ( ) ;
}
termRestoreState ( state ) {
return this . term ( ) . restoreState ( state ) ;
}
2018-03-09 20:59:12 +00:00
prompt ( initialText = '' , promptString = ':' , options = null ) {
return this . widget ( 'statusBar' ) . prompt ( initialText , promptString , options ) ;
2017-10-19 23:02:13 +01:00
}
2017-10-24 21:22:57 +01:00
stdoutMaxWidth ( ) {
2018-03-09 20:59:12 +00:00
return this . widget ( 'console' ) . innerWidth - 1 ;
2017-10-24 21:22:57 +01:00
}
2017-10-24 22:37:36 +01:00
isDummy ( ) {
return false ;
}
2017-10-06 18:38:17 +01:00
buildUi ( ) {
2017-10-07 22:01:03 +01:00
this . rootWidget _ = new ReduxRootWidget ( this . store _ ) ;
2018-03-09 20:59:12 +00:00
this . rootWidget _ . name = 'root' ;
2018-02-16 22:53:53 +00:00
this . rootWidget _ . autoShortcutsEnabled = false ;
2017-10-05 18:17:56 +01:00
2017-10-07 23:17:10 +01:00
const folderList = new FolderListWidget ( ) ;
2017-10-22 13:45:56 +01:00
folderList . style = {
borderBottomWidth : 1 ,
borderRightWidth : 1 ,
} ;
2018-03-09 20:59:12 +00:00
folderList . name = 'folderList' ;
2017-10-09 18:05:01 +00:00
folderList . vStretch = true ;
2020-05-21 09:14:33 +01:00
folderList . on ( 'currentItemChange' , async event => {
2017-10-22 18:12:16 +01:00
const item = folderList . currentItem ;
2018-03-09 20:59:12 +00:00
if ( item === '-' ) {
2020-03-13 23:46:14 +00:00
const newIndex = event . currentIndex + ( event . previousIndex < event . currentIndex ? + 1 : - 1 ) ;
2017-10-23 22:48:29 +01:00
let nextItem = folderList . itemAt ( newIndex ) ;
if ( ! nextItem ) nextItem = folderList . itemAt ( event . previousIndex ) ;
if ( ! nextItem ) return ; // Normally not possible
2018-03-09 20:59:12 +00:00
let actionType = 'FOLDER_SELECT' ;
if ( nextItem . type _ === BaseModel . TYPE _TAG ) actionType = 'TAG_SELECT' ;
if ( nextItem . type _ === BaseModel . TYPE _SEARCH ) actionType = 'SEARCH_SELECT' ;
2017-10-23 22:48:29 +01:00
this . store _ . dispatch ( {
type : actionType ,
id : nextItem . id ,
} ) ;
} else if ( item . type _ === Folder . modelType ( ) ) {
2017-10-22 18:12:16 +01:00
this . store _ . dispatch ( {
2018-03-09 20:59:12 +00:00
type : 'FOLDER_SELECT' ,
2017-10-23 22:48:29 +01:00
id : item ? item . id : null ,
2017-10-22 18:12:16 +01:00
} ) ;
} else if ( item . type _ === Tag . modelType ( ) ) {
this . store _ . dispatch ( {
2018-03-09 20:59:12 +00:00
type : 'TAG_SELECT' ,
2017-10-23 22:48:29 +01:00
id : item ? item . id : null ,
2017-10-22 18:12:16 +01:00
} ) ;
2017-10-23 21:34:04 +01:00
} else if ( item . type _ === BaseModel . TYPE _SEARCH ) {
this . store _ . dispatch ( {
2018-03-09 20:59:12 +00:00
type : 'SEARCH_SELECT' ,
2017-10-23 22:48:29 +01:00
id : item ? item . id : null ,
2017-10-23 21:34:04 +01:00
} ) ;
2017-10-22 18:12:16 +01:00
}
2017-10-07 23:17:10 +01:00
} ) ;
2020-05-21 09:14:33 +01:00
this . rootWidget _ . connect ( folderList , state => {
2017-10-07 23:17:10 +01:00
return {
selectedFolderId : state . selectedFolderId ,
2017-10-22 18:12:16 +01:00
selectedTagId : state . selectedTagId ,
2017-10-23 21:34:04 +01:00
selectedSearchId : state . selectedSearchId ,
2017-10-22 18:12:16 +01:00
notesParentType : state . notesParentType ,
folders : state . folders ,
tags : state . tags ,
2017-10-23 21:34:04 +01:00
searches : state . searches ,
2017-10-07 23:17:10 +01:00
} ;
2017-10-05 18:17:56 +01:00
} ) ;
2017-10-09 20:57:00 +01:00
const noteList = new NoteListWidget ( ) ;
2018-03-09 20:59:12 +00:00
noteList . name = 'noteList' ;
2017-10-09 18:05:01 +00:00
noteList . vStretch = true ;
noteList . style = {
2017-10-05 18:17:56 +01:00
borderBottomWidth : 1 ,
borderLeftWidth : 1 ,
borderRightWidth : 1 ,
2017-10-09 18:05:01 +00:00
} ;
2018-03-09 20:59:12 +00:00
noteList . on ( 'currentItemChange' , async ( ) => {
2020-03-13 23:46:14 +00:00
const note = noteList . currentItem ;
2017-10-07 22:01:03 +01:00
this . store _ . dispatch ( {
2018-03-09 20:59:12 +00:00
type : 'NOTE_SELECT' ,
2017-11-08 21:22:24 +00:00
id : note ? note . id : null ,
2017-10-07 22:01:03 +01:00
} ) ;
2017-10-07 23:17:10 +01:00
} ) ;
2020-05-21 09:14:33 +01:00
this . rootWidget _ . connect ( noteList , state => {
2017-10-07 23:17:10 +01:00
return {
2017-11-22 18:35:31 +00:00
selectedNoteId : state . selectedNoteIds . length ? state . selectedNoteIds [ 0 ] : null ,
2017-10-07 23:17:10 +01:00
items : state . notes ,
} ;
2017-10-05 18:17:56 +01:00
} ) ;
2017-10-07 22:01:03 +01:00
const noteText = new NoteWidget ( ) ;
2017-10-22 13:45:56 +01:00
noteText . hStretch = true ;
2018-03-09 20:59:12 +00:00
noteText . name = 'noteText' ;
2017-10-22 13:45:56 +01:00
noteText . style = {
borderBottomWidth : 1 ,
borderLeftWidth : 1 ,
} ;
2020-05-21 09:14:33 +01:00
this . rootWidget _ . connect ( noteText , state => {
2017-10-28 18:44:28 +01:00
return {
2017-11-22 18:35:31 +00:00
noteId : state . selectedNoteIds . length ? state . selectedNoteIds [ 0 ] : null ,
2017-10-28 18:44:28 +01:00
notes : state . notes ,
} ;
2017-10-07 22:01:03 +01:00
} ) ;
2017-10-22 13:45:56 +01:00
const noteMetadata = new NoteMetadataWidget ( ) ;
noteMetadata . hStretch = true ;
2018-03-09 20:59:12 +00:00
noteMetadata . name = 'noteMetadata' ;
2017-10-22 13:45:56 +01:00
noteMetadata . style = {
borderBottomWidth : 1 ,
borderLeftWidth : 1 ,
borderRightWidth : 1 ,
} ;
2020-05-21 09:14:33 +01:00
this . rootWidget _ . connect ( noteMetadata , state => {
2017-11-22 18:35:31 +00:00
return { noteId : state . selectedNoteIds . length ? state . selectedNoteIds [ 0 ] : null } ;
2017-10-22 13:45:56 +01:00
} ) ;
noteMetadata . hide ( ) ;
2017-10-06 18:38:17 +01:00
const consoleWidget = new ConsoleWidget ( ) ;
2017-10-09 18:05:01 +00:00
consoleWidget . hStretch = true ;
2017-10-22 13:45:56 +01:00
consoleWidget . style = {
borderBottomWidth : 1 ,
} ;
2017-10-17 18:18:31 +01:00
consoleWidget . hide ( ) ;
2017-10-15 17:57:09 +01:00
const statusBar = new StatusBarWidget ( ) ;
statusBar . hStretch = true ;
2017-10-06 18:38:17 +01:00
2017-10-22 13:45:56 +01:00
const noteLayout = new VLayoutWidget ( ) ;
2018-03-09 20:59:12 +00:00
noteLayout . name = 'noteLayout' ;
noteLayout . addChild ( noteText , { type : 'stretch' , factor : 1 } ) ;
noteLayout . addChild ( noteMetadata , { type : 'stretch' , factor : 1 } ) ;
2017-10-22 13:45:56 +01:00
2017-10-06 18:01:10 +00:00
const hLayout = new HLayoutWidget ( ) ;
2018-03-09 20:59:12 +00:00
hLayout . name = 'hLayout' ;
2020-05-09 20:42:09 +05:30
hLayout . addChild ( folderList , { type : 'stretch' , factor : Setting . value ( 'layout.folderList.factor' ) } ) ;
hLayout . addChild ( noteList , { type : 'stretch' , factor : Setting . value ( 'layout.noteList.factor' ) } ) ;
hLayout . addChild ( noteLayout , { type : 'stretch' , factor : Setting . value ( 'layout.note.factor' ) } ) ;
2017-10-05 18:17:56 +01:00
2017-10-06 18:38:17 +01:00
const vLayout = new VLayoutWidget ( ) ;
2018-03-09 20:59:12 +00:00
vLayout . name = 'vLayout' ;
vLayout . addChild ( hLayout , { type : 'stretch' , factor : 2 } ) ;
vLayout . addChild ( consoleWidget , { type : 'stretch' , factor : 1 } ) ;
vLayout . addChild ( statusBar , { type : 'fixed' , factor : 1 } ) ;
2017-10-05 18:17:56 +01:00
const win1 = new WindowWidget ( ) ;
2017-10-06 18:38:17 +01:00
win1 . addChild ( vLayout ) ;
2018-03-09 20:59:12 +00:00
win1 . name = 'mainWindow' ;
2017-10-05 18:17:56 +01:00
2017-10-07 22:01:03 +01:00
this . rootWidget _ . addChild ( win1 ) ;
2017-10-05 18:17:56 +01:00
}
2017-10-24 20:52:26 +01:00
showModalOverlay ( text ) {
2018-03-09 20:59:12 +00:00
if ( ! this . widget ( 'overlayWindow' ) ) {
2017-10-24 20:52:26 +01:00
const textWidget = new TextWidget ( ) ;
textWidget . hStretch = true ;
textWidget . vStretch = true ;
2018-03-09 20:59:12 +00:00
textWidget . text = 'testing' ;
textWidget . name = 'overlayText' ;
2017-10-24 20:52:26 +01:00
const win = new WindowWidget ( ) ;
2018-03-09 20:59:12 +00:00
win . name = 'overlayWindow' ;
2017-10-24 20:52:26 +01:00
win . addChild ( textWidget ) ;
this . rootWidget _ . addChild ( win ) ;
}
2018-03-09 20:59:12 +00:00
this . widget ( 'overlayWindow' ) . activate ( ) ;
this . widget ( 'overlayText' ) . text = text ;
2017-10-24 20:52:26 +01:00
}
hideModalOverlay ( ) {
2018-03-09 20:59:12 +00:00
if ( this . widget ( 'overlayWindow' ) ) this . widget ( 'overlayWindow' ) . hide ( ) ;
this . widget ( 'mainWindow' ) . activate ( ) ;
2017-10-24 20:52:26 +01:00
}
2017-10-24 20:40:15 +01:00
addCommandToConsole ( cmd ) {
2017-10-28 18:55:45 +01:00
if ( ! cmd ) return ;
2018-03-09 20:59:12 +00:00
const isConfigPassword = cmd . indexOf ( 'config ' ) >= 0 && cmd . indexOf ( 'password' ) >= 0 ;
2018-02-26 19:25:54 +00:00
if ( isConfigPassword ) return ;
2019-09-19 22:51:18 +01:00
this . stdout ( chalk . cyan . bold ( ` > ${ cmd } ` ) ) ;
2017-10-24 20:40:15 +01:00
}
2018-02-16 22:53:53 +00:00
setupKeymap ( keymap ) {
const output = [ ] ;
2017-10-07 17:30:27 +01:00
2018-02-16 22:53:53 +00:00
for ( let i = 0 ; i < keymap . length ; i ++ ) {
const item = Object . assign ( { } , keymap [ i ] ) ;
2017-10-29 15:41:30 +00:00
2019-09-19 22:51:18 +01:00
if ( ! item . command ) throw new Error ( ` Missing command for keymap item: ${ JSON . stringify ( item ) } ` ) ;
2017-10-29 15:41:30 +00:00
2018-03-09 20:59:12 +00:00
if ( ! ( 'type' in item ) ) item . type = 'exec' ;
2017-10-29 15:41:30 +00:00
2018-02-16 22:53:53 +00:00
if ( item . command in this . tkWidgetKeys _ ) {
2018-03-09 20:59:12 +00:00
item . type = 'tkwidgets' ;
2017-10-23 22:48:29 +01:00
}
2017-10-30 18:39:20 +00:00
2018-03-09 20:59:12 +00:00
item . canRunAlongOtherCommands = item . type === 'function' && [ 'toggle_metadata' , 'toggle_console' ] . indexOf ( item . command ) >= 0 ;
2017-10-08 18:50:43 +01:00
2018-02-16 22:53:53 +00:00
output . push ( item ) ;
2017-10-08 23:34:01 +01:00
}
2018-02-16 22:53:53 +00:00
return output ;
2017-10-07 17:30:27 +01:00
}
2017-10-17 18:18:31 +01:00
toggleConsole ( ) {
this . showConsole ( ! this . consoleIsShown ( ) ) ;
}
showConsole ( doShow = true ) {
2018-03-09 20:59:12 +00:00
this . widget ( 'console' ) . show ( doShow ) ;
2017-10-17 18:18:31 +01:00
}
hideConsole ( ) {
this . showConsole ( false ) ;
}
consoleIsShown ( ) {
2018-03-09 20:59:12 +00:00
return this . widget ( 'console' ) . shown ;
2017-10-08 23:34:01 +01:00
}
maximizeConsole ( doMaximize = true ) {
2018-03-09 20:59:12 +00:00
const consoleWidget = this . widget ( 'console' ) ;
2017-10-08 23:34:01 +01:00
2017-10-17 18:18:31 +01:00
if ( consoleWidget . isMaximized _ _ === undefined ) {
consoleWidget . isMaximized _ _ = false ;
}
2017-10-08 23:34:01 +01:00
2017-10-17 18:18:31 +01:00
if ( consoleWidget . isMaximized _ _ === doMaximize ) return ;
2017-10-08 23:34:01 +01:00
2020-03-13 23:46:14 +00:00
const constraints = {
2018-03-09 20:59:12 +00:00
type : 'stretch' ,
2017-10-17 18:18:31 +01:00
factor : ! doMaximize ? 1 : 4 ,
} ;
2017-10-08 23:34:01 +01:00
2017-10-17 18:18:31 +01:00
consoleWidget . isMaximized _ _ = doMaximize ;
2017-10-08 23:34:01 +01:00
2018-03-09 20:59:12 +00:00
this . widget ( 'vLayout' ) . setWidgetConstraints ( consoleWidget , constraints ) ;
2017-10-08 23:34:01 +01:00
}
minimizeConsole ( ) {
2017-10-17 18:18:31 +01:00
this . maximizeConsole ( false ) ;
2017-10-08 23:34:01 +01:00
}
consoleIsMaximized ( ) {
2018-03-09 20:59:12 +00:00
return this . widget ( 'console' ) . isMaximized _ _ === true ;
2017-10-08 23:34:01 +01:00
}
2017-10-22 13:45:56 +01:00
showNoteMetadata ( show = true ) {
2018-03-09 20:59:12 +00:00
this . widget ( 'noteMetadata' ) . show ( show ) ;
2017-10-22 13:45:56 +01:00
}
hideNoteMetadata ( ) {
this . showNoteMetadata ( false ) ;
}
toggleNoteMetadata ( ) {
2018-03-09 20:59:12 +00:00
this . showNoteMetadata ( ! this . widget ( 'noteMetadata' ) . shown ) ;
2017-10-22 13:45:56 +01:00
}
2017-10-05 18:17:56 +01:00
widget ( name ) {
2018-03-09 20:59:12 +00:00
if ( name === 'root' ) return this . rootWidget _ ;
2017-10-05 18:17:56 +01:00
return this . rootWidget _ . childByName ( name ) ;
}
app ( ) {
return this . app _ ;
}
setLogger ( l ) {
this . logger _ = l ;
}
logger ( ) {
return this . logger _ ;
}
2018-02-16 22:53:53 +00:00
keymap ( ) {
return this . keymap _ ;
}
keymapItemByKey ( key ) {
for ( let i = 0 ; i < this . keymap _ . length ; i ++ ) {
const item = this . keymap _ [ i ] ;
if ( item . keys . indexOf ( key ) >= 0 ) return item ;
}
return null ;
2017-10-08 23:34:01 +01:00
}
2017-10-05 18:17:56 +01:00
term ( ) {
return this . term _ ;
}
2017-10-07 17:30:27 +01:00
activeListItem ( ) {
2018-03-09 20:59:12 +00:00
const widget = this . widget ( 'mainWindow' ) . focusedWidget ;
2017-10-07 17:30:27 +01:00
if ( ! widget ) return null ;
2019-07-30 09:35:42 +02:00
2018-03-09 20:59:12 +00:00
if ( widget . name == 'noteList' || widget . name == 'folderList' ) {
2017-10-07 21:04:53 +01:00
return widget . currentItem ;
2017-10-07 17:30:27 +01:00
}
return null ;
}
2017-10-06 18:38:17 +01:00
async handleModelAction ( action ) {
2018-03-09 20:59:12 +00:00
this . logger ( ) . info ( 'Action:' , action ) ;
2017-10-07 19:05:35 +01:00
2020-03-13 23:46:14 +00:00
const state = Object . assign ( { } , defaultState ) ;
2018-03-09 20:59:12 +00:00
state . notes = this . widget ( 'noteList' ) . items ;
2017-10-06 18:38:17 +01:00
2020-03-13 23:46:14 +00:00
const newState = reducer ( state , action ) ;
2017-10-06 18:38:17 +01:00
2017-10-07 17:30:27 +01:00
if ( newState !== state ) {
2018-03-09 20:59:12 +00:00
this . widget ( 'noteList' ) . items = newState . notes ;
2017-10-07 17:30:27 +01:00
}
}
2017-10-06 18:38:17 +01:00
2018-02-16 22:53:53 +00:00
async processFunctionCommand ( cmd ) {
2018-03-09 20:59:12 +00:00
if ( cmd === 'activate' ) {
const w = this . widget ( 'mainWindow' ) . focusedWidget ;
if ( w . name === 'folderList' ) {
this . widget ( 'noteList' ) . focus ( ) ;
} else if ( w . name === 'noteList' || w . name === 'noteText' ) {
this . processPromptCommand ( 'edit $n' ) ;
2018-02-16 22:53:53 +00:00
}
2018-03-09 20:59:12 +00:00
} else if ( cmd === 'delete' ) {
if ( this . widget ( 'folderList' ) . hasFocus ) {
const item = this . widget ( 'folderList' ) . selectedJoplinItem ;
2018-02-16 22:53:53 +00:00
if ( ! item ) return ;
if ( item . type _ === BaseModel . TYPE _FOLDER ) {
2019-09-19 22:51:18 +01:00
await this . processPromptCommand ( ` rmbook ${ item . id } ` ) ;
2018-02-16 22:53:53 +00:00
} else if ( item . type _ === BaseModel . TYPE _TAG ) {
2018-03-09 20:59:12 +00:00
this . stdout ( _ ( 'To delete a tag, untag the associated notes.' ) ) ;
2018-02-16 22:53:53 +00:00
} else if ( item . type _ === BaseModel . TYPE _SEARCH ) {
this . store ( ) . dispatch ( {
2018-03-09 20:59:12 +00:00
type : 'SEARCH_DELETE' ,
2018-02-16 22:53:53 +00:00
id : item . id ,
} ) ;
}
2018-03-09 20:59:12 +00:00
} else if ( this . widget ( 'noteList' ) . hasFocus ) {
await this . processPromptCommand ( 'rmnote $n' ) ;
2018-02-16 22:53:53 +00:00
} else {
2018-03-09 20:59:12 +00:00
this . stdout ( _ ( 'Please select the note or notebook to be deleted first.' ) ) ;
2018-02-16 22:53:53 +00:00
}
2020-05-22 05:48:27 -06:00
// NOTE: MY SHORTCUTS
} else if ( cmd === 'next_link' || cmd === 'previous_link' || cmd === 'open_link' ) {
const noteText = this . widget ( 'noteText' ) ;
const mainWindow = this . widget ( 'mainWindow' ) ;
2020-05-22 06:39:08 -06:00
// const lines = noteText.renderedText_.split('\n');
// console.log(lines)
2020-05-22 05:48:27 -06:00
if ( noteText . hasFocus ) {
noteText . render ( ) ;
2020-05-22 06:39:08 -06:00
2020-05-22 05:48:27 -06:00
const lines = noteText . renderedText _ . split ( '\n' ) ;
2020-05-22 06:39:08 -06:00
// const link = /\\x1B\[[0-9]{2}m\\x1B\[[0-9]mhttp:\/\/[0-9.]+:[0-9]+\/[0-9]+\\x1B\[[0-9]{2}m\\x1B\[[0-9]{2}m/g;
2020-05-22 05:48:27 -06:00
const link = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g ;
2020-05-22 15:26:42 -06:00
// this.term_.moveTo(mainWindow.width - noteText.innerWidth + 1, 1);
this . term _ . showCursor ( ) ;
2020-05-22 05:48:27 -06:00
this . term _ . term ( ) . getCursorLocation ( ( error , x , y ) => {
if ( error ) throw new Error ( 'Could not get cursor index' ) ;
2020-05-22 15:26:42 -06:00
const cursorOffset = mainWindow . width - noteText . innerWidth + 1 ;
const innerX = x - cursorOffset ;
2020-05-22 05:48:27 -06:00
const innerY = y ;
2020-05-22 06:39:08 -06:00
const scrollHeight = noteText . scrollableHeight _ - 1 ;
2020-05-22 05:48:27 -06:00
const beginStr = lines [ innerY ] . substr ( 0 , innerX ) ;
const endStr = lines [ innerY ] . substr ( innerX , lines [ innerY ] . length - 1 ) ;
if ( cmd !== 'previous_link' ) {
const matchesNext = [ ... beginStr . matchAll ( link ) ] ;
if ( cmd === 'open_link' && matchesNext . length ) {
2020-05-22 15:26:42 -06:00
if ( matchesNext [ 0 ] . index === innerX ) {
console . log ( matchesNext [ 0 ] [ 0 ] ) ;
2020-05-22 05:48:27 -06:00
open ( matchesNext [ 0 ] [ 0 ] ) ;
return ;
}
} else if ( cmd === 'next_link' && matchesNext . length > 1 ) {
2020-05-22 15:26:42 -06:00
this . term _ . term ( ) . moveTo ( cursorOffset + matchesNext [ 1 ] . index - 9 , innerY ) ;
this . term _ . term ( ) . inverse ( matchesNext [ 1 ] [ 0 ] ) ;
this . term _ . term ( ) . move ( - matchesNext [ 1 ] [ 0 ] . length , 0 ) ;
2020-05-22 05:48:27 -06:00
return ;
}
if ( cmd === 'open_link' ) return ;
} else {
const matchesPrev = [ ... endStr . matchAll ( link ) ] ;
if ( matchesPrev . length ) {
2020-05-22 15:26:42 -06:00
this . term _ . moveTo ( cursorOffset + matchesPrev [ matchesPrev . length - 1 ] . index - 9 , innerY ) ;
this . term _ . term ( ) . inverse ( matchesPrev [ matchesPrev . length - 1 ] [ 0 ] ) ;
this . term _ . move ( - matchesPrev [ matchesPrev . length - 1 ] [ 0 ] . length , 0 ) ;
2020-05-22 05:48:27 -06:00
return ;
}
}
let i ;
if ( cmd === 'next_link' ) i === scrollHeight ? i = 0 : i = innerY + 1 ;
else i === 0 ? i = scrollHeight : i = innerY - 1 ;
for ( ; i !== innerY ; ( cmd === 'next_link' ? i ++ : i -- ) ) {
const matches = [ ... lines [ i ] . matchAll ( link ) ] ;
if ( cmd === 'next_link' ) {
if ( i === scrollHeight ) i = 0 ;
if ( matches . length ) {
2020-05-22 15:26:42 -06:00
this . term _ . term ( ) . moveTo ( cursorOffset + matches [ 0 ] . index - 9 , i + 1 ) ;
2020-05-22 05:48:27 -06:00
this . term _ . term ( ) . inverse ( matches [ 0 ] [ 0 ] ) ;
2020-05-22 15:26:42 -06:00
this . term _ . term ( ) . move ( - matches [ 0 ] [ 0 ] . length , 0 ) ;
2020-05-22 05:48:27 -06:00
return ;
}
} else {
if ( i === 0 ) i = scrollHeight ;
if ( matches . length ) {
2020-05-22 15:26:42 -06:00
this . term _ . term ( ) . moveTo ( cursorOffset + matches [ matches . length - 1 ] . index - 9 , i + 1 ) ;
2020-05-22 05:48:27 -06:00
this . term _ . term ( ) . inverse ( matches [ matches . length - 1 ] [ 0 ] ) ;
2020-05-22 15:26:42 -06:00
this . term _ . term ( ) . move ( - matches [ matches . length - 1 ] [ 0 ] . length , 0 ) ;
2020-05-22 05:48:27 -06:00
return ;
}
}
}
} ) ;
}
2018-03-09 20:59:12 +00:00
} else if ( cmd === 'toggle_console' ) {
2018-02-16 22:53:53 +00:00
if ( ! this . consoleIsShown ( ) ) {
this . showConsole ( ) ;
this . minimizeConsole ( ) ;
} else {
if ( this . consoleIsMaximized ( ) ) {
this . hideConsole ( ) ;
} else {
this . maximizeConsole ( ) ;
}
}
2018-03-09 20:59:12 +00:00
} else if ( cmd === 'toggle_metadata' ) {
2018-02-16 22:53:53 +00:00
this . toggleNoteMetadata ( ) ;
2018-03-09 20:59:12 +00:00
} else if ( cmd === 'enter_command_line_mode' ) {
const cmd = await this . widget ( 'statusBar' ) . prompt ( ) ;
2018-02-16 22:53:53 +00:00
if ( ! cmd ) return ;
this . addCommandToConsole ( cmd ) ;
2019-07-30 09:35:42 +02:00
await this . processPromptCommand ( cmd ) ;
2018-02-16 22:53:53 +00:00
} else {
2019-09-19 22:51:18 +01:00
throw new Error ( ` Unknown command: ${ cmd } ` ) ;
2018-02-16 22:53:53 +00:00
}
}
async processPromptCommand ( cmd ) {
2017-10-07 17:30:27 +01:00
if ( ! cmd ) return ;
cmd = cmd . trim ( ) ;
if ( ! cmd . length ) return ;
2018-03-14 17:28:41 +00:00
// this.logger().debug('Got command: ' + cmd);
2017-10-19 23:02:13 +01:00
2019-07-30 09:35:42 +02:00
try {
2020-03-13 23:46:14 +00:00
const note = this . widget ( 'noteList' ) . currentItem ;
const folder = this . widget ( 'folderList' ) . currentItem ;
const args = splitCommandString ( cmd ) ;
2017-12-01 17:51:27 +00:00
for ( let i = 0 ; i < args . length ; i ++ ) {
2018-03-09 20:59:12 +00:00
if ( args [ i ] == '$n' ) {
args [ i ] = note ? note . id : '' ;
} else if ( args [ i ] == '$b' ) {
args [ i ] = folder ? folder . id : '' ;
2019-07-30 09:35:42 +02:00
} else if ( args [ i ] == '$c' ) {
2017-12-01 17:51:27 +00:00
const item = this . activeListItem ( ) ;
2018-03-09 20:59:12 +00:00
args [ i ] = item ? item . id : '' ;
2017-12-01 17:51:27 +00:00
}
2017-10-07 17:30:27 +01:00
}
2017-10-06 18:38:17 +01:00
2017-10-07 17:30:27 +01:00
await this . app ( ) . execCommand ( args ) ;
} catch ( error ) {
2017-10-15 17:57:09 +01:00
this . stdout ( error . message ) ;
2017-10-07 17:30:27 +01:00
}
2017-10-24 22:09:22 +01:00
2018-03-09 20:59:12 +00:00
this . widget ( 'console' ) . scrollBottom ( ) ;
2019-07-30 09:35:42 +02:00
2017-12-29 10:39:50 +01:00
// Invalidate so that the screen is redrawn in case inputting a command has moved
// the GUI up (in particular due to autocompletion), it's moved back to the right position.
2018-03-09 20:59:12 +00:00
this . widget ( 'root' ) . invalidate ( ) ;
2017-10-06 18:38:17 +01:00
}
2017-10-05 18:17:56 +01:00
async updateFolderList ( ) {
const folders = await Folder . all ( ) ;
2018-03-09 20:59:12 +00:00
this . widget ( 'folderList' ) . items = folders ;
2017-10-05 18:17:56 +01:00
}
async updateNoteList ( folderId ) {
2017-10-06 18:38:17 +01:00
const fields = Note . previewFields ( ) ;
2018-03-09 20:59:12 +00:00
fields . splice ( fields . indexOf ( 'body' ) , 1 ) ;
2017-10-06 18:38:17 +01:00
const notes = folderId ? await Note . previews ( folderId , { fields : fields } ) : [ ] ;
2018-03-09 20:59:12 +00:00
this . widget ( 'noteList' ) . items = notes ;
2017-10-05 18:17:56 +01:00
}
async updateNoteText ( note ) {
2018-03-09 20:59:12 +00:00
const text = note ? note . body : '' ;
this . widget ( 'noteText' ) . text = text ;
2017-10-05 18:17:56 +01:00
}
2017-10-09 20:57:00 +01:00
// Any key after which a shortcut is not possible.
2017-10-08 23:34:01 +01:00
isSpecialKey ( name ) {
2018-03-09 20:59:12 +00:00
return [ ':' , 'ENTER' , 'DOWN' , 'UP' , 'LEFT' , 'RIGHT' , 'DELETE' , 'BACKSPACE' , 'ESCAPE' , 'TAB' , 'SHIFT_TAB' , 'PAGE_UP' , 'PAGE_DOWN' ] . indexOf ( name ) >= 0 ;
2017-10-08 23:34:01 +01:00
}
2017-10-14 19:03:23 +01:00
fullScreen ( enable = true ) {
if ( enable ) {
this . term ( ) . fullscreen ( ) ;
this . term ( ) . hideCursor ( ) ;
2018-03-09 20:59:12 +00:00
this . widget ( 'root' ) . invalidate ( ) ;
2017-10-14 19:03:23 +01:00
} else {
this . term ( ) . fullscreen ( false ) ;
this . term ( ) . showCursor ( ) ;
}
}
2017-10-17 18:18:31 +01:00
stdout ( text ) {
if ( text === null || text === undefined ) return ;
2020-03-13 23:46:14 +00:00
const lines = text . split ( '\n' ) ;
2017-10-17 18:18:31 +01:00
for ( let i = 0 ; i < lines . length ; i ++ ) {
2018-03-09 20:59:12 +00:00
const v = typeof lines [ i ] === 'object' ? JSON . stringify ( lines [ i ] ) : lines [ i ] ;
this . widget ( 'console' ) . addLine ( v ) ;
2017-10-15 17:57:09 +01:00
}
2017-10-15 18:20:54 +01:00
this . updateStatusBarMessage ( ) ;
}
2017-10-24 20:40:15 +01:00
exit ( ) {
this . fullScreen ( false ) ;
this . resourceServer _ . stop ( ) ;
}
2017-10-15 18:20:54 +01:00
updateStatusBarMessage ( ) {
2018-03-09 20:59:12 +00:00
const consoleWidget = this . widget ( 'console' ) ;
2017-10-15 18:20:54 +01:00
2018-03-09 20:59:12 +00:00
let msg = '' ;
2017-10-15 18:20:54 +01:00
2017-10-17 18:18:31 +01:00
const text = consoleWidget . lastLine ;
2017-10-15 18:20:54 +01:00
const cmd = this . app ( ) . currentCommand ( ) ;
if ( cmd ) {
msg += cmd . name ( ) ;
2018-03-09 20:59:12 +00:00
if ( cmd . cancellable ( ) ) msg += ' [Press Ctrl+C to cancel]' ;
msg += ': ' ;
2017-10-15 18:20:54 +01:00
}
if ( text && text . length ) {
msg += text ;
}
2018-03-09 20:59:12 +00:00
if ( msg !== '' ) this . widget ( 'statusBar' ) . setItemAt ( 0 , msg ) ;
2017-10-15 17:57:09 +01:00
}
2017-10-26 20:58:27 +01:00
async setupResourceServer ( ) {
2017-10-24 20:40:15 +01:00
const linkStyle = chalk . blue . underline ;
2018-03-09 20:59:12 +00:00
const noteTextWidget = this . widget ( 'noteText' ) ;
2019-07-30 09:35:42 +02:00
const resourceIdRegex = /^:\/[a-f0-9]+$/i ;
2017-10-26 20:58:27 +01:00
const noteLinks = { } ;
2017-10-24 20:40:15 +01:00
2017-10-28 00:20:03 +01:00
const hasProtocol = function ( s , protocols ) {
if ( ! s ) return false ;
s = s . trim ( ) . toLowerCase ( ) ;
for ( let i = 0 ; i < protocols . length ; i ++ ) {
2019-09-19 22:51:18 +01:00
if ( s . indexOf ( ` ${ protocols [ i ] } :// ` ) === 0 ) return true ;
2017-10-28 00:20:03 +01:00
}
return false ;
2019-07-30 09:35:42 +02:00
} ;
2017-10-28 00:20:03 +01:00
2017-10-24 20:40:15 +01:00
// By default, before the server is started, only the regular
// URLs appear in blue.
noteTextWidget . markdownRendererOptions = {
2017-10-26 20:58:27 +01:00
linkUrlRenderer : ( index , url ) => {
2017-10-26 22:57:49 +01:00
if ( ! url ) return url ;
2017-10-24 20:40:15 +01:00
if ( resourceIdRegex . test ( url ) ) {
return url ;
2018-03-09 20:59:12 +00:00
} else if ( hasProtocol ( url , [ 'http' , 'https' ] ) ) {
2017-10-26 20:58:27 +01:00
return linkStyle ( url ) ;
2017-10-24 20:40:15 +01:00
} else {
2017-10-26 20:58:27 +01:00
return url ;
2017-10-24 20:40:15 +01:00
}
} ,
} ;
this . resourceServer _ = new ResourceServer ( ) ;
this . resourceServer _ . setLogger ( this . app ( ) . logger ( ) ) ;
2017-10-26 20:58:27 +01:00
this . resourceServer _ . setLinkHandler ( async ( path , response ) => {
const link = noteLinks [ path ] ;
2018-03-09 20:59:12 +00:00
if ( link . type === 'url' ) {
2019-07-30 09:35:42 +02:00
response . writeHead ( 302 , { Location : link . url } ) ;
2017-10-26 20:58:27 +01:00
return true ;
2017-10-24 20:40:15 +01:00
}
2017-10-26 20:58:27 +01:00
2018-05-09 12:50:50 +01:00
if ( link . type === 'item' ) {
const itemId = link . id ;
2020-03-13 23:46:14 +00:00
const item = await BaseItem . loadItemById ( itemId ) ;
2019-09-19 22:51:18 +01:00
if ( ! item ) throw new Error ( ` No item with ID ${ itemId } ` ) ; // Should be nearly impossible
2018-05-09 12:50:50 +01:00
if ( item . type _ === BaseModel . TYPE _RESOURCE ) {
if ( item . mime ) response . setHeader ( 'Content-Type' , item . mime ) ;
response . write ( await Resource . content ( item ) ) ;
} else if ( item . type _ === BaseModel . TYPE _NOTE ) {
2019-07-30 09:35:42 +02:00
const html = [
`
2018-05-09 12:50:50 +01:00
< ! DOCTYPE html >
< html class = "client-nojs" lang = "en" dir = "ltr" >
< head > < meta charset = "UTF-8" / > < / h e a d > < b o d y >
2019-07-30 09:35:42 +02:00
` ,
] ;
2019-09-19 22:51:18 +01:00
html . push ( ` <pre> ${ htmlentities ( item . title ) } \n \n ${ htmlentities ( item . body ) } </pre> ` ) ;
2018-05-09 12:50:50 +01:00
html . push ( '</body></html>' ) ;
response . write ( html . join ( '' ) ) ;
} else {
2019-09-19 22:51:18 +01:00
throw new Error ( ` Unsupported item type: ${ item . type _ } ` ) ;
2018-05-09 12:50:50 +01:00
}
2017-10-26 20:58:27 +01:00
return true ;
}
return false ;
2017-10-24 20:40:15 +01:00
} ) ;
2017-10-26 20:58:27 +01:00
await this . resourceServer _ . start ( ) ;
if ( ! this . resourceServer _ . started ( ) ) return ;
noteTextWidget . markdownRendererOptions = {
linkUrlRenderer : ( index , url ) => {
if ( ! url ) return url ;
if ( resourceIdRegex . test ( url ) ) {
noteLinks [ index ] = {
2018-05-09 12:50:50 +01:00
type : 'item' ,
2017-10-26 20:58:27 +01:00
id : url . substr ( 2 ) ,
2019-07-30 09:35:42 +02:00
} ;
2018-03-09 20:59:12 +00:00
} else if ( hasProtocol ( url , [ 'http' , 'https' , 'file' , 'ftp' ] ) ) {
2017-10-26 20:58:27 +01:00
noteLinks [ index ] = {
2018-03-09 20:59:12 +00:00
type : 'url' ,
2017-10-26 20:58:27 +01:00
url : url ,
} ;
2018-03-09 20:59:12 +00:00
} else if ( url . indexOf ( '#' ) === 0 ) {
return '' ; // Anchors aren't supported for now
2017-10-28 00:20:03 +01:00
} else {
return url ;
2017-10-26 20:58:27 +01:00
}
2019-09-19 22:51:18 +01:00
return linkStyle ( ` ${ this . resourceServer _ . baseUrl ( ) } / ${ index } ` ) ;
2017-10-26 20:58:27 +01:00
} ,
} ;
2017-10-24 20:40:15 +01:00
}
2017-10-05 18:17:56 +01:00
async start ( ) {
const term = this . term ( ) ;
2017-10-14 19:03:23 +01:00
this . fullScreen ( ) ;
2017-10-05 18:17:56 +01:00
try {
2017-10-24 20:40:15 +01:00
this . setupResourceServer ( ) ;
2017-10-05 18:17:56 +01:00
this . renderer _ . start ( ) ;
2018-03-09 20:59:12 +00:00
const statusBar = this . widget ( 'statusBar' ) ;
2017-10-07 17:30:27 +01:00
2017-10-05 18:17:56 +01:00
term . grabInput ( ) ;
2019-09-12 22:16:42 +00:00
term . on ( 'key' , async ( name ) => {
2017-10-17 18:18:31 +01:00
// -------------------------------------------------------------------------
// Handle special shortcuts
// -------------------------------------------------------------------------
2018-03-09 20:59:12 +00:00
if ( name === 'CTRL_D' ) {
2017-10-14 19:03:23 +01:00
const cmd = this . app ( ) . currentCommand ( ) ;
if ( cmd && cmd . cancellable ( ) && ! this . commandCancelCalled _ ) {
this . commandCancelCalled _ = true ;
await cmd . cancel ( ) ;
this . commandCancelCalled _ = false ;
}
2017-10-09 21:29:49 +01:00
await this . app ( ) . exit ( ) ;
2017-10-07 17:30:27 +01:00
return ;
2017-10-05 18:17:56 +01:00
}
2017-10-14 19:03:23 +01:00
2019-07-30 09:35:42 +02:00
if ( name === 'CTRL_C' ) {
2017-10-14 19:03:23 +01:00
const cmd = this . app ( ) . currentCommand ( ) ;
if ( ! cmd || ! cmd . cancellable ( ) || this . commandCancelCalled _ ) {
2017-10-15 17:57:09 +01:00
this . stdout ( _ ( 'Press Ctrl+D or type "exit" to exit the application' ) ) ;
2017-10-14 19:03:23 +01:00
} else {
this . commandCancelCalled _ = true ;
2019-07-30 09:35:42 +02:00
await cmd . cancel ( ) ;
2017-10-14 19:03:23 +01:00
this . commandCancelCalled _ = false ;
}
return ;
}
2017-10-17 18:18:31 +01:00
// -------------------------------------------------------------------------
// Build up current shortcut
// -------------------------------------------------------------------------
2019-07-30 09:35:42 +02:00
const now = new Date ( ) . getTime ( ) ;
2017-10-08 23:34:01 +01:00
2017-10-09 20:57:00 +01:00
if ( now - this . lastShortcutKeyTime _ > 800 || this . isSpecialKey ( name ) ) {
this . currentShortcutKeys _ = [ name ] ;
2017-10-08 23:34:01 +01:00
} else {
2017-10-09 20:57:00 +01:00
// If the previous key was a special key (eg. up, down arrow), this new key
// starts a new shortcut.
if ( this . currentShortcutKeys _ . length && this . isSpecialKey ( this . currentShortcutKeys _ [ 0 ] ) ) {
this . currentShortcutKeys _ = [ name ] ;
} else {
this . currentShortcutKeys _ . push ( name ) ;
}
2017-10-08 23:34:01 +01:00
}
this . lastShortcutKeyTime _ = now ;
2017-10-17 18:18:31 +01:00
// -------------------------------------------------------------------------
// Process shortcut and execute associated command
// -------------------------------------------------------------------------
2018-03-09 20:59:12 +00:00
const shortcutKey = this . currentShortcutKeys _ . join ( '' ) ;
2020-03-13 23:46:14 +00:00
const keymapItem = this . keymapItemByKey ( shortcutKey ) ;
2017-12-01 18:56:35 +00:00
// If this command is an alias to another command, resolve to the actual command
2017-10-17 18:18:31 +01:00
2018-02-16 22:53:53 +00:00
let processShortcutKeys = ! this . app ( ) . currentCommand ( ) && keymapItem ;
if ( keymapItem && keymapItem . canRunAlongOtherCommands ) processShortcutKeys = true ;
2017-10-17 22:56:22 +01:00
if ( statusBar . promptActive ) processShortcutKeys = false ;
2017-10-17 18:18:31 +01:00
if ( processShortcutKeys ) {
2018-03-14 17:28:41 +00:00
this . logger ( ) . debug ( 'Shortcut:' , shortcutKey , keymapItem ) ;
2017-10-17 22:56:22 +01:00
2017-10-17 18:18:31 +01:00
this . currentShortcutKeys _ = [ ] ;
2018-03-09 20:59:12 +00:00
if ( keymapItem . type === 'function' ) {
2018-02-16 22:53:53 +00:00
this . processFunctionCommand ( keymapItem . command ) ;
2018-03-09 20:59:12 +00:00
} else if ( keymapItem . type === 'prompt' ) {
2020-03-13 23:46:14 +00:00
const promptOptions = { } ;
2018-03-09 20:59:12 +00:00
if ( 'cursorPosition' in keymapItem ) promptOptions . cursorPosition = keymapItem . cursorPosition ;
const commandString = await statusBar . prompt ( keymapItem . command ? keymapItem . command : '' , null , promptOptions ) ;
2018-02-16 22:53:53 +00:00
this . addCommandToConsole ( commandString ) ;
await this . processPromptCommand ( commandString ) ;
2018-03-09 20:59:12 +00:00
} else if ( keymapItem . type === 'exec' ) {
2018-02-16 22:53:53 +00:00
this . stdout ( keymapItem . command ) ;
await this . processPromptCommand ( keymapItem . command ) ;
2018-03-09 20:59:12 +00:00
} else if ( keymapItem . type === 'tkwidgets' ) {
this . widget ( 'root' ) . handleKey ( this . tkWidgetKeys _ [ keymapItem . command ] ) ;
2018-02-16 22:53:53 +00:00
} else {
2019-09-19 22:51:18 +01:00
throw new Error ( ` Unknown command type: ${ JSON . stringify ( keymapItem ) } ` ) ;
2017-10-07 17:30:27 +01:00
}
2017-10-05 18:17:56 +01:00
}
2017-10-15 18:20:54 +01:00
// Optimisation: Update the status bar only
// if the user is not already typing a command:
if ( ! statusBar . promptActive ) this . updateStatusBarMessage ( ) ;
2017-10-05 18:17:56 +01:00
} ) ;
} catch ( error ) {
2017-10-14 19:03:23 +01:00
this . fullScreen ( false ) ;
2017-10-05 18:17:56 +01:00
this . logger ( ) . error ( error ) ;
console . error ( error ) ;
}
2017-10-07 23:17:10 +01:00
2018-03-09 20:59:12 +00:00
process . on ( 'unhandledRejection' , ( reason , p ) => {
2017-10-14 19:03:23 +01:00
this . fullScreen ( false ) ;
2018-03-09 20:59:12 +00:00
console . error ( 'Unhandled promise rejection' , p , 'reason:' , reason ) ;
2017-10-07 23:17:10 +01:00
process . exit ( 1 ) ;
} ) ;
2017-10-05 18:17:56 +01:00
}
}
2017-10-08 23:34:01 +01:00
AppGui . INPUT _MODE _NORMAL = 1 ;
AppGui . INPUT _MODE _META = 2 ;
2017-12-09 23:08:28 -06:00
module . exports = AppGui ;