From e86139e202941198f74dbab6d78253856245996d Mon Sep 17 00:00:00 2001 From: lxsang Date: Sun, 13 Jun 2021 11:06:49 +0200 Subject: [PATCH] Update Antos editor: Antedit now have it own extension manager --- Antedit/README.md | 1 + Antedit/assets/scheme.html | 24 +- Antedit/build.json | 43 ++- Antedit/build/debug/README.md | 1 + .../build/debug/extensions/extensions.json | 4 +- Antedit/build/debug/main.css | 91 +++--- Antedit/build/debug/main.js | 2 +- Antedit/build/debug/package.json | 2 +- Antedit/build/debug/scheme.html | 24 +- Antedit/build/release/Antedit.zip | Bin 10866 -> 12146 bytes Antedit/css/main.css | 91 +++--- Antedit/extensions/extensions.json | 4 +- Antedit/package.json | 2 +- Antedit/ts/EditorExtensionMaker.ts | 49 +++- Antedit/ts/main.ts | 265 +++++++++++++++++- libantosdk/ts/app.ts | 1 - packages.json | 2 +- 17 files changed, 480 insertions(+), 126 deletions(-) diff --git a/Antedit/README.md b/Antedit/README.md index 1722404..ce6b952 100644 --- a/Antedit/README.md +++ b/Antedit/README.md @@ -6,6 +6,7 @@ The editor functionality can be extended by its extension mechanism. Extension can be developed/released/isntalled by the editor itself. ### Change logs +- 0.1.10-b: Antedit now has it own extension manager - 0.1.9-a: Allow output text selection - 0.1.8-a: Allow to change language mode - 0.1.7-a: Add keyboard shortcut support to extension actions \ No newline at end of file diff --git a/Antedit/assets/scheme.html b/Antedit/assets/scheme.html index 4ce75ad..e410cc4 100644 --- a/Antedit/assets/scheme.html +++ b/Antedit/assets/scheme.html @@ -1,10 +1,26 @@ - -
- - + + + + + +
+ + +
+
+ + + + + + + + + +
diff --git a/Antedit/build.json b/Antedit/build.json index 32bd2f7..6801085 100644 --- a/Antedit/build.json +++ b/Antedit/build.json @@ -1,13 +1,31 @@ { "name": "Antedit", "targets": { - "build": { - "require":["ts"], + "copy": { "jobs": [ { "name": "vfs-mkdir", "data": ["build","build/debug","build/release"] }, + { + "name": "vfs-cp", + "data": { + "src": [ + "extensions", + "assets/scheme.html", + "package.json", + "README.md", + "css/main.css" + ], + "dest":"build/debug" + } + } + ] + }, + "build": { + "depend": ["copy"], + "require":["ts"], + "jobs": [ { "name": "ts-import", "data": ["sdk://core/ts/core.d.ts", "sdk://core/ts/jquery.d.ts","sdk://core/ts/antos.d.ts"] @@ -24,19 +42,6 @@ ], "dest": "build/debug/main.js" } - }, - { - "name": "vfs-cp", - "data": { - "src": [ - "extensions", - "assets/scheme.html", - "package.json", - "README.md", - "css/main.css" - ], - "dest":"build/debug" - } } ] }, @@ -75,6 +80,14 @@ } } ] + }, + "run": { + "jobs": [ + { + "name": "sdk-run-app", + "data": "build/debug" + } + ] } } } \ No newline at end of file diff --git a/Antedit/build/debug/README.md b/Antedit/build/debug/README.md index 1722404..ce6b952 100644 --- a/Antedit/build/debug/README.md +++ b/Antedit/build/debug/README.md @@ -6,6 +6,7 @@ The editor functionality can be extended by its extension mechanism. Extension can be developed/released/isntalled by the editor itself. ### Change logs +- 0.1.10-b: Antedit now has it own extension manager - 0.1.9-a: Allow output text selection - 0.1.8-a: Allow to change language mode - 0.1.7-a: Add keyboard shortcut support to extension actions \ No newline at end of file diff --git a/Antedit/build/debug/extensions/extensions.json b/Antedit/build/debug/extensions/extensions.json index a18686f..ced33a1 100644 --- a/Antedit/build/debug/extensions/extensions.json +++ b/Antedit/build/debug/extensions/extensions.json @@ -21,11 +21,11 @@ "name": "release" }, { - "text": "__(Install extension from file)", + "text": "__(Install from file)", "name": "install" }, { - "text": "__(Install extension from URL)", + "text": "__(Install from URL)", "name": "installFromURL" } ] diff --git a/Antedit/build/debug/main.css b/Antedit/build/debug/main.css index 460e3b3..49448d0 100644 --- a/Antedit/build/debug/main.css +++ b/Antedit/build/debug/main.css @@ -35,7 +35,7 @@ afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-contai border-radius: 0; } afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view afx-list-view i.closable:before { - color:afafaf; + color:#afafaf; } afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li, afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul li{ @@ -48,6 +48,15 @@ afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-conta padding-right: 20px; border-right: 1px solid #272822; } + +afx-app-window[data-id = "antedit"] afx-tab-container[data-id="sidebar-tab-container"] afx-tab-bar> afx-list-view > div.list-container { + background-color: #333333; +} +afx-app-window[data-id = "antedit"] afx-tab-container[data-id="sidebar-tab-container"] afx-tab-bar> afx-list-view > div.list-container > ul li{ + float: none; + font-size: 20px; +} + afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-vbox[data-id = "sidebar"]{ background-color:#272822; } @@ -91,49 +100,6 @@ afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"] a padding-left: 10px; } -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper{ - border-radius: 0px; - border: 0; - /*border: 1px solid #37373d;*/ - background-color: transparent; - box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.65); -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li -{ - background-color: transparent; -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view afx-list-item li{ - background-color: transparent; - color:#afafaf; -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper div.list-container > ul li:hover{ - background-color: #37373d; -} - -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li.selected, -afx-app-window[data-id = "cmd-win"] .afx-window-wrappe dafx-list-viewafx-list-view ul li.selected -{ - background-color: #116cd6; - color:white; -} -afx-app-window[data-id = "cmd-win"] .afx-window-top{ - height: 0; - border:0; -} -afx-app-window[data-id = "cmd-win"] input{ - border: 1px solid #007acc; - border-radius: 0; - font-size: 12px; - color:#afafaf; - background-color:#272822; - padding-left: 5px; - margin: 3px; -} - -afx-app-window[data-id = "cmd-win"] .afx-window-content{ - background-color:#272822; -} - afx-app-window[data-id = "antedit"] div[data-id="output-tab"] { overflow-y: auto; overflow-x: hidden; @@ -165,4 +131,41 @@ afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-i afx-app-window[data-id = "antedit"] afx-button[ data-id="logger-clear" ] button{ border: 0; background: transparent; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item { + color: white !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item afx-label i.label-text{ + font-weight: bold !important; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item p { + margin: 0; + padding: 0; + padding-left:15px; + font-size: 11px; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item p[data-id="ext-list-item-b-p"] { + text-align: right; + font-size: 11px; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item > li { + background-color: transparent !important; + padding-right: 5px !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item > li.selected { + background-color: #116cd6 !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item button { + height: 22px; + width: 24px; + padding: 0 !important; +} +afx-app-window[data-id = "antedit"] input[data-id="txt_ext_search"] { + background-color: transparent; + border-radius: 0; + border-color: #333; + color: white; } \ No newline at end of file diff --git a/Antedit/build/debug/main.js b/Antedit/build/debug/main.js index 73fa7d6..f62d6a7 100644 --- a/Antedit/build/debug/main.js +++ b/Antedit/build/debug/main.js @@ -1 +1 @@ -var OS;!function(t){let e;!function(t){t.BaseEditorModel=class{constructor(t,e,i){this.container=i,this.currfile="Untitled".asFileHandle(),this.tabbar=e,this.editorSetup(i),this.app=t,this.editormux=!1,this.onstatuschange=void 0,this.on("focus",()=>{this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}),this.on("input",()=>this.editormux?(this.editormux=!1,!1):this.currfile.dirty?void 0:(this.currfile.dirty=!0,this.currfile.text+="*",this.tabbar.update(void 0))),this.on("changeCursor",()=>{this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}),this.tabbar.ontabselect=t=>this.selecteTab($(t.data.item).index()),this.tabbar.ontabclose=t=>{const e=t.data.item;return!!e&&(e.data.dirty?(this.app.openDialog("YesNoDialog",{title:__("Close tab"),text:__("Close without saving ?")}).then(t=>t?this.closeTab(e):this.focus()),!1):this.closeTab(e))}}findTabByFile(t){const e=this.tabbar.items,i=(()=>{const i=[];for(let a=0;a(t.cache=e||"",this.newTab(t))).catch(e=>this.app.error(__("Unable to open: {0}",t.path),e)):this.newTab(t):this.tabbar.selected=e}write(t){this.currfile.cache=this.getValue(),t.write("text/plain").then(e=>{t.dirty=!1,t.text=t.basename,this.tabbar.update(void 0)}).catch(e=>this.app.error(__("Unable to save file: {0}",t.path),e))}save(){return this.currfile.cache=this.getValue(),this.currfile.basename?this.write(this.currfile):this.saveAs()}saveAs(){this.app.openDialog("FileDialog",{title:__("Save as"),file:this.currfile}).then(t=>{let e=t.file.path.asFileHandle();"file"===t.file.type&&(e=e.parent()),this.currfile.setPath(`${e.path}/${t.name}`),this.write(this.currfile)})}dirties(){const t=[];for(let e of Array.from(this.tabbar.items))e.dirty&&t.push(e);return t}set contextmenuHandle(t){this.container.contextmenuHandle=t}closeAll(){this.tabbar.items=[],this.resetEditor()}isDirty(){return this.dirties().length>0}}}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){class i extends t.application.BaseEditorModel{constructor(t,e,i){super(t,e,i)}resetEditor(){this.setValue("")}getTexModel(){return{model:this.editor.getModel(),position:this.editor.getPosition()}}setTextModel(t){this.editor.setModel(t.model),t.position&&(this.editor.setPosition(t.position),this.editor.revealLineInCenter(t.position.lineNumber))}newTextModelFrom(t){if("Untitled"===t.path.toString())return{model:monaco.editor.createModel(t.cache,"textplain")};const e=monaco.Uri.parse(t.path),i=monaco.editor.getModel(e);return i?(i.setValue(t.cache),{model:i}):{model:monaco.editor.createModel(t.cache,void 0,e)}}getModes(){return monaco.languages.getLanguages().map(t=>(t.text=t.aliases[0],t))}setTheme(t){}setMode(t){monaco.editor.setModelLanguage(this.editor.getModel(),t.id),this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}editorSetup(t){this.editor=monaco.editor.create(t,{value:"",language:"textplain"}),i.modes||(i.modes={},monaco.languages.getLanguages().forEach(t=>{i.modes[t.id]=t}))}on(t,e){switch(t){case"input":this.editor.onDidChangeModelContent(e);break;case"focus":this.editor.onDidFocusEditorText(e);break;case"changeCursor":this.editor.onDidChangeCursorPosition(e)}}resize(){this.editor&&this.editor.layout()}focus(){this.editor&&this.editor.focus()}getModeForPath(t){return{}}getEditorStatus(){const t=this.editor.getPosition(),e=i.modes[this.editor.getModel().getModeId()];return{row:t.lineNumber,column:t.column,line:this.editor.getModel().getLineCount(),langmode:{text:e.aliases[0],mode:e},file:this.currfile.path}}getValue(){return this.editor.getValue()}setValue(t){this.editor.setValue(t)}getEditor(){return this.editor}}e.MonacoEditorModel=i}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){class i extends e.BaseApplication{constructor(t){super("Antedit",t),this.currdir=void 0}main(){this.extensions={},this.eum=new a,this.fileview=this.find("fileview"),this.sidebar=this.find("sidebar"),this.bottombar=this.find("bottombar"),this.langstat=this.find("langstat"),this.editorstat=this.find("editorstat"),this.filestat=this.find("current-file-lbl"),this.logger=new s(this.find("output-tab")),this.split_mode=!0,this.fileview.fetch=t=>new Promise((async function(e,i){let a;a="string"==typeof t?t.asFileHandle():t;try{const t=await a.read();return t.error?i(t.error):e(t.result)}catch(t){return i(__e(t))}}));let e="Untitled".asFileHandle();this.args&&this.args.length>0&&(this.addRecent(this.args[0].path),"dir"===this.args[0].type?this.currdir=this.args[0].path.asFileHandle():(e=this.args[0].path.asFileHandle(),this.currdir=e.parent())),this.setting.recent||(this.setting.recent=[]);const i=this.find("wrapper");$(i).css("visibility","hidden"),monaco.editor.setTheme("vs-dark"),this.eum.add(new t.application.MonacoEditorModel(this,this.find("left-tabbar"),this.find("left-editorarea"))).add(new t.application.MonacoEditorModel(this,this.find("right-tabbar"),this.find("right-editorarea"))),this.eum.onstatuschange=t=>this.updateStatus(t),$(i).css("visibility","visible"),this.setup(),this.eum.active.openFile(e)}setup(){this.fileview.onfileopen=t=>{if(t.data&&t.data.path&&"dir"!==t.data.type)return this.addRecent(t.data.path),this.eum.active.openFile(t.data.path.asFileHandle())},this.fileview.onfileselect=t=>{t.data&&t.data.path&&"dir"!==t.data.type&&this.eum.active.selectFile(t.data.path)},this.on("resize",()=>this.eum.resize()),this.on("focus",()=>this.eum.active.focus()),this.fileview.contextmenuHandle=(t,e)=>(e.items=[{text:"__(New file)",id:"new"},{text:"__(New folder)",id:"newdir"},{text:"__(Rename)",id:"rename"},{text:"__(Delete)",id:"delete"}],e.onmenuselect=t=>this.ctxFileMenuHandle(t),e.show(t)),this.bindKey("ALT-N",()=>this.menuAction("new")),this.bindKey("ALT-O",()=>this.menuAction("open")),this.bindKey("ALT-F",()=>this.menuAction("opendir")),this.bindKey("CTRL-S",()=>this.menuAction("save")),this.bindKey("ALT-W",()=>this.menuAction("saveas")),this.fileview.ondragndrop=t=>{const e=t.data.from.data.path.asFileHandle(),i=t.data.to.data.path;return e.move(`${i}/${e.basename}`).then((function(a){const s=i,n=e.parent().path;s.lengththis.error(__("Unable to move file/folder"),t))},this.on("filechange",t=>{let{path:e}=t.file;return"file"===t.type&&({path:e}=t.file.parent()),this.fileview.update(e)}),this.find("logger-clear").onbtclick=()=>{this.logger.clear()},void 0===this.setting.showBottomBar&&(this.setting.showBottomBar=!1);const t={name:"Editor",text:__("Editor")},e={name:"langmode",text:__("Change language mode"),shortcut:"CTRL-K"};this.eum.addAction(t,e,async t=>{try{const t=await this.openDialog("SelectionDialog",{title:__("Select language"),data:this.eum.active.getModes()});this.eum.active.setMode(t)}catch(t){}}),this.loadExtensionMetaData(),this.toggleSideBar(),this.toggleSplitMode(),this.applyAllSetting()}updateStatus(t){t||(t=this.eum.active.getEditorStatus()),this.editorstat.text=__("Row {0}, col {1}, lines: {2}",t.row,t.column,t.line),t.langmode&&(this.langstat.text=t.langmode.text),this.filestat.text=t.file;let e=this.scheme;e.apptitle!=t.file&&(e.apptitle=t.file)}toggleSideBar(){this.currdir?($(this.sidebar).show(),this.fileview.path=this.currdir.path):$(this.sidebar).hide(),this.trigger("resize")}showOutput(t=!1){t&&this.showBottomBar(!0),this.bottombar.selectedIndex=0}applySetting(t){"showBottomBar"==t&&this.showBottomBar(this.setting.showBottomBar)}showBottomBar(t){this.setting.showBottomBar=t,t?$(this.bottombar).show():$(this.bottombar).hide(),this.trigger("resize")}toggleBottomBar(){this.showBottomBar(!this.setting.showBottomBar)}toggleSplitMode(){const t=this.find("right-panel"),e=this.eum.editors[1],i=this.eum.editors[0];if(this.split_mode){if(e.isDirty())return void this.notify(__("Unable to disable split view: Please save changes of modified files on the right panel"));e.closeAll(),$(t).hide(),this.split_mode=!1,i.focus()}else $(t).show(),this.split_mode=!0,e.openFile("Untitled".asFileHandle()),e.focus();this.trigger("resize")}fileMenu(){const t=this.setting.recent.map(t=>({text:t}));return{text:__("File"),nodes:[{text:__("New"),dataid:"new",shortcut:"A-N"},{text:__("Open Recent"),dataid:"recent",nodes:t,onchildselect:(t,e)=>{const i=t.data.item.data.text.asFileHandle();i.onready().then(t=>{t&&("dir"==t.type?(this.currdir=i,this.toggleSideBar()):this.eum.active.openFile(i))})}},{text:__("Open"),dataid:"open",shortcut:"A-O"},{text:__("Open Folder"),dataid:"opendir",shortcut:"A-F"},{text:__("Save"),dataid:"save",shortcut:"C-S"},{text:__("Save as"),dataid:"saveas",shortcut:"A-W"}],onchildselect:(t,e)=>this.menuAction(t.data.item.data.dataid,e)}}ctxFileMenuHandle(t){const e=t.data.item;if(!e)return;const i=e.data;if(!i)return;let a=this.fileview.selectedFile,s=this.currdir;switch(a&&"dir"===a.type&&(s=a.path.asFileHandle()),a&&"file"===a.type&&(s=a.path.asFileHandle().parent()),i.id){case"new":if(!s)return;this.openDialog("PromptDialog",{title:"__(New file)",label:"__(File name)"}).then(async t=>{const e=`${s.path}/${t}`.asFileHandle();try{return await e.write("text/plain"),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to create: {0}",t.stack),t)}});break;case"newdir":if(!s)return;this.openDialog("PromptDialog",{title:"__(New folder)",label:"__(Folder name)"}).then(async t=>{try{return await s.mk(t),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to create: {0}",s.path),t)}});break;case"rename":if(!a)return;this.openDialog("PromptDialog",{title:"__(Rename)",label:"__(File name)",value:a.filename}).then(async t=>{if(t!==a.filename){a=a.path.asFileHandle(),s=a.parent();try{return await a.move(`${s.path}/${t}`),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to rename: {0}",a.path),t)}}});break;case"delete":if(!a)return;this.openDialog("YesNoDialog",{title:"__(Delete)",iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",a.filename)}).then(async t=>{if(t){a=a.path.asFileHandle(),s=a.parent();try{return await a.remove(),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to delete: {0}",a.path),t)}}})}}addRecent(t){this.setting.recent||(this.setting.recent=[]),this.setting.recent.includes(t)||(this.setting.recent.push(t),this.setting.recent.length>10&&(this.setting.recent=this.setting.recent.slice(0,10)))}menuAction(t,e){let i=this;switch(e&&(i=e),t){case"new":return i.eum.active.openFile("Untitled".asFileHandle());case"open":return i.openDialog("FileDialog",{title:__("Open file"),mimes:Array.from(i.meta().mimes).filter(t=>"dir"!==t)}).then(t=>{this.addRecent(t.file.path),i.eum.active.openFile(t.file.path.asFileHandle())});case"opendir":return i.openDialog("FileDialog",{title:__("Open folder"),mimes:["dir"]}).then((function(t){return i.addRecent(t.file.path),i.currdir=t.file.path.asFileHandle(),i.toggleSideBar()}));case"save":return i.eum.active.save();case"saveas":return i.eum.active.saveAs();default:return console.log(t)}}cleanup(t){let e;const i=this.eum.dirties();if(0!==i.length)t.preventDefault(),this.openDialog("YesNoDialog",{title:"__(Quit)",text:__("Ignore all unsaved files: {0} ?",(()=>{const t=[];for(e of Array.from(i))t.push(e.filename);return t})().join(", "))}).then(t=>{if(t){for(e of Array.from(i))e.dirty=!1;return this.quit(!1)}});else for(let t in this.extensions)this.extensions[t]&&this.extensions[t].cleanup&&this.extensions[t].cleanup()}menu(){return[this.fileMenu(),{text:"__(View)",nodes:[{text:"__(Toggle bottom bar)",dataid:"bottombar"},{text:"__(Toggle split view)",dataid:"splitview"}],onchildselect:(t,e)=>{switch(t.data.item.data.dataid){case"bottombar":return this.toggleBottomBar();case"splitview":return this.toggleSplitMode()}}}]}loadExtensionMetaData(){this.loadExtensionMetaFromFile(this.meta().path+"/extensions/extensions.json").catch(t=>this.error(__("Cannot load extension meta data"),t))}loadExtensionMetaFromFile(t){return new Promise((e,i)=>{t.asFileHandle().read("json").then(t=>{for(let e of t)for(let t of e.actions)this.eum.addAction(e,t,(t,i)=>{this.loadAndRunExtensionAction(t,i,e.root)});e()}).catch(t=>{i(__e(t))})})}loadAndRunExtensionAction(t,e,a){if(i.extensions[t])this.runExtensionAction(t,e);else{let i=`${this.meta().path}/extensions/${t}/main.js`;a&&(i=a+"/main.js"),this._api.requires(i,!0).then(()=>this.runExtensionAction(t,e)).catch(e=>this.error(__("unable to load extension: {0}",t),e))}}runExtensionAction(t,e){if(!this.extensions[t]){if(!i.extensions[t])return this.error(__("Unable to find extension: {0}",t));this.extensions[t]=new i.extensions[t](this)}if(!this.extensions[t][e])return this.error(__("Unable to find action: {0}",e));this.extensions[t].preload().then(()=>this.extensions[t][e]()).catch(t=>this.error(__("Unable to preload extension"),t))}}e.Antedit=i;class a{constructor(){this.active_editor=void 0,this.models=[]}get editors(){return this.models}set contextmenuHandle(t){for(let e of this.models)e.contextmenuHandle=t}get active(){return this.active_editor}add(t){return this.models.push(t),this.active_editor||(this.active_editor=t),t.on("focus",()=>{this.active_editor=t}),this}addAction(t,e,i){const a={id:`${t.name}:${e.name}`,label:`${t.text.__()}: ${e.text.__()}`,keybindings:[],precondition:null,keybindingContext:null,contextMenuGroupId:t.name,run:()=>i(t.name,e.name)};if(e.shortcut){const t=e.shortcut.split("-");let i=0;for(const e of t)switch(e){case"CTRL":i|=monaco.KeyMod.CtrlCmd;break;case"ALT":i|=monaco.KeyMod.Alt;break;case"SHIFT":i|=monaco.KeyMod.Shift;break;case"SUPPER":i|=monaco.KeyMod.WinCtrl;break;default:const t="KEY_"+e;monaco.KeyCode[t]?i|=monaco.KeyCode[t]:i=0}0!=i&&a.keybindings.push(i)}for(let t of this.models){const e=t.getEditor();e.getAction(a.id)||e.addAction(a)}}set onstatuschange(t){for(let e of this.models)e.onstatuschange=t}dirties(){let t=[];for(let e of this.models)t=t.concat(e.dirties());return t}resize(){for(let t of this.models)t.resize()}}class s{constructor(t){this.target=t}info(t){this.log("info",t,!0)}warn(t){this.log("warn",t,!0)}error(t){this.log("error",t,!0)}log(t,e,i){let a=$("
").attr("class","code-pad-log-"+t);if(i){let t=new Date,i=t.getDate()+"/"+(t.getMonth()+1)+"/"+t.getFullYear()+" "+t.getHours()+":"+t.getMinutes()+":"+t.getSeconds();a.text(`[${i}]: ${e.__()}`)}else a.text(e.__());$(this.target).append(a),$(this.target).scrollTop($(this.target)[0].scrollHeight)}print(t){this.log("info",t,!1)}clear(){$(this.target).empty()}}i.Logger=s,i.dependencies=["pkg://MonacoCore/path.js","pkg://MonacoCore/bundle/app.bundle.js"]}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){class e{constructor(t,e){this.app=e,this.name=t}preload(){return t.API.require(t.application.Antedit.extensions[this.name].dependencies)}basedir(){return`${this.app.meta().path}/extensions/${this.name}`}notify(t){return this.app.notify(t)}error(t,e){return this.app.error(t,e)}logger(){return this.app.setting.showBottomBar?this.app.showOutput(!1):this.app.showOutput(!0),this.app.logger}metadata(e){return new Promise((i,a)=>{if(!this.app.currdir)return a(t.API.throwe(__("Current folder is not found")));`${this.app.currdir.path}/${e}`.asFileHandle().read("json").then(t=>{!t.root&&this.app.currdir&&(t.root=this.app.currdir.path),i(t)}).catch(s=>{this.app.openDialog("FileDialog",{title:__("Select build directory"),root:this.app.currdir.path,mimes:["dir"]}).then(t=>{`${t.file.path}/${e}`.asFileHandle().read("json").then(e=>{e.root||(e.root=t.file.path),i(e)}).catch(t=>a(t))}).catch(e=>a(t.API.throwe(__("Unable to read meta-data"))))})})}}e.dependencies=[],t.application.Antedit.extensions={},t.application.Antedit.EditorBaseExtension=e,t.application.Antedit.extensions.EditorExtensionMaker=class extends e{constructor(t){super("EditorExtensionMaker",t)}create(){this.logger().clear(),this.app.openDialog("FileDialog",{title:"__(New extension at)",file:{basename:__("ExtensionName")},mimes:["dir"]}).then(t=>this.mktpl(t.file.path,t.name))}build(e){this.logger().clear(),this.metadata("extension.json").then(async i=>{try{const a=await t.API.VFS.cat(i.javascripts.map(t=>`${i.root}/${t}`),"");await(i.root+"/build/debug/main.js").asFileHandle().setCache(a).write("text/plain"),await(i.root+"/build/debug/extension.json").asFileHandle().setCache(i.meta).write("object"),await t.API.VFS.copy(i.copies.map(t=>`${i.root}/${t}`),i.root+"/build/debug"),this.logger().info(__("Files generated in {0}",i.root+"/build/debug")),e&&e()}catch(t){return this.logger().error(__("Unable to build extension:{0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to read meta-data:{0}",t.stack)))}run(){this.logger().clear(),this.metadata("extension.json").then(async e=>{if(!e||!e.meta||!e.meta.name)return this.logger().error(__("Invalid extension meta-data"));try{const i=e.root+"/build/debug/main.js";t.API.shared[i]&&delete t.API.shared[i],await t.API.requires(i),this.app.extensions[e.meta.name]&&this.app.extensions[e.meta.name].cleanup&&this.app.extensions[e.meta.name].cleanup(),this.app.extensions[e.meta.name]=new t.application.Antedit.extensions[e.meta.name](this.app);for(let t of e.meta.actions)this.app.eum.addAction(e.meta,t,(t,i)=>{this.app.loadAndRunExtensionAction(t,i,e.root+"/build")});this.app.eum.active.getEditor().trigger(e.meta.name,"editor.action.quickCommand")}catch(t){return this.logger().error(__("Unable to run extension:{0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to read meta-data:{0}",t.stack)))}release(){this.logger().clear(),this.metadata("extension.json").then(async e=>{this.build(async()=>{try{await t.API.VFS.mkar(e.root+"/build/debug",`${e.root}/build/release/${e.meta.name}.zip`),this.logger().info(__("Archive created at {0}",`${e.root}/build/release/${e.meta.name}.zip`))}catch(t){return this.logger().error(__("Unable to create archive: {0}",t.stack))}})}).catch(t=>this.logger().error(__("Unable to read meta-data: {0}",t.stack)))}install(){this.logger().clear(),this.app.openDialog("FileDialog",{title:"__(Select extension archive)",mimes:[".*/zip"]}).then(async t=>{try{return await this.installZip(t.file.path),this.logger().info(__("Extension installed")),this.app.loadExtensionMetaData()}catch(t){return this.logger().error(__("Unable to install extension: {0}",t.stack))}})}installFromURL(){this.logger().clear(),this.app.openDialog("PromptDialog",{title:__("Enter URI"),label:__("Please enter extension URI:")}).then(async t=>{if(t)try{return await this.installZip(t),this.logger().info(__("Extension installed")),this.app.loadExtensionMetaData()}catch(e){return this.app.error(__("Unable to install extension: {0}",t))}})}mktpl(e,i){const a=`${e}/${i}`,s=[a,a+"/build",a+"/build/release",a+"/build/debug"],n=[["main.tpl",`${a}/${i}.js`],["meta.tpl",a+"/extension.json"]];t.API.VFS.mkdirAll(s,!0).then(async()=>{try{return await t.API.VFS.mktpl(n,this.basedir(),t=>t.format(i,`${e}/${i}`)),this.app.currdir=a.asFileHandle(),this.app.toggleSideBar(),this.app.eum.active.openFile(`${a}/${i}.js`.asFileHandle())}catch(t){return this.logger().error(__("Unable to create extension template: {0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to create extension directories: {0}",t.stack)))}installZip(e){return new Promise(async(i,a)=>{try{await t.API.requires("os://scripts/jszip.min.js");const a=await e.asFileHandle().read("binary"),s=await JSZip.loadAsync(a),n=await s.file("extension.json").async("uint8array"),o=JSON.parse(new TextDecoder("utf-8").decode(n)),r=this.ext_dir(o.name),l=[r],h=[];for(let t in s.files)s.files[t].dir?l.push(r+"/"+t):"extension.json"!=t&&h.push(t);l.length>0?(await t.API.VFS.mkdirAll(l,!0),await this.installFiles(h,s,o)):await this.installFiles(h,s,o),i()}catch(t){a(__e(t))}})}ext_dir(t){return`${this.app.meta().path}/extensions/${t}`}installFiles(t,e,i){return 0===t.length?this.installMeta(i):new Promise(async(a,s)=>{try{const n=t.splice(0,1)[0],o=`${this.ext_dir(i.name)}/${n}`,r=await e.file(n).async("uint8array"),l=await o.asFileHandle().setCache(new Blob([r],{type:"octet/stream"})).write("text/plain");if(l.error)return s(l.error);await this.installFiles(t,e,i),a()}catch(t){s(__e(t))}})}installMeta(t){return new Promise(async(e,i)=>{const a=(this.ext_dir("")+"/extensions.json").asFileHandle();try{const s=await a.read("json"),n=[];for(let t of s)n.push(t.name);const o=n.indexOf(t.name);o>=0&&s.splice(o,1),s.push(t);try{return await a.setCache(s).write("object"),e()}catch(t){return i(__e(t))}}catch(s){try{return await a.setCache([t]).write("object"),e()}catch(t){return i(__e(t))}}})}}}(OS||(OS={}));
\ No newline at end of file
+var OS;!function(t){let e;!function(t){t.BaseEditorModel=class{constructor(t,e,i){this.container=i,this.currfile="Untitled".asFileHandle(),this.tabbar=e,this.editorSetup(i),this.app=t,this.editormux=!1,this.onstatuschange=void 0,this.on("focus",()=>{this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}),this.on("input",()=>this.editormux?(this.editormux=!1,!1):this.currfile.dirty?void 0:(this.currfile.dirty=!0,this.currfile.text+="*",this.tabbar.update(void 0))),this.on("changeCursor",()=>{this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}),this.tabbar.ontabselect=t=>this.selecteTab($(t.data.item).index()),this.tabbar.ontabclose=t=>{const e=t.data.item;return!!e&&(e.data.dirty?(this.app.openDialog("YesNoDialog",{title:__("Close tab"),text:__("Close without saving ?")}).then(t=>t?this.closeTab(e):this.focus()),!1):this.closeTab(e))}}findTabByFile(t){const e=this.tabbar.items,i=(()=>{const i=[];for(let a=0;a(t.cache=e||"",this.newTab(t))).catch(e=>this.app.error(__("Unable to open: {0}",t.path),e)):this.newTab(t):this.tabbar.selected=e}write(t){this.currfile.cache=this.getValue(),t.write("text/plain").then(e=>{t.dirty=!1,t.text=t.basename,this.tabbar.update(void 0)}).catch(e=>this.app.error(__("Unable to save file: {0}",t.path),e))}save(){return this.currfile.cache=this.getValue(),this.currfile.basename?this.write(this.currfile):this.saveAs()}saveAs(){this.app.openDialog("FileDialog",{title:__("Save as"),file:this.currfile}).then(t=>{let e=t.file.path.asFileHandle();"file"===t.file.type&&(e=e.parent()),this.currfile.setPath(`${e.path}/${t.name}`),this.write(this.currfile)})}dirties(){const t=[];for(let e of Array.from(this.tabbar.items))e.dirty&&t.push(e);return t}set contextmenuHandle(t){this.container.contextmenuHandle=t}closeAll(){this.tabbar.items=[],this.resetEditor()}isDirty(){return this.dirties().length>0}}}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){class i extends t.application.BaseEditorModel{constructor(t,e,i){super(t,e,i)}resetEditor(){this.setValue("")}getTexModel(){return{model:this.editor.getModel(),position:this.editor.getPosition()}}setTextModel(t){this.editor.setModel(t.model),t.position&&(this.editor.setPosition(t.position),this.editor.revealLineInCenter(t.position.lineNumber))}newTextModelFrom(t){if("Untitled"===t.path.toString())return{model:monaco.editor.createModel(t.cache,"textplain")};const e=monaco.Uri.parse(t.path),i=monaco.editor.getModel(e);return i?(i.setValue(t.cache),{model:i}):{model:monaco.editor.createModel(t.cache,void 0,e)}}getModes(){return monaco.languages.getLanguages().map(t=>(t.text=t.aliases[0],t))}setTheme(t){}setMode(t){monaco.editor.setModelLanguage(this.editor.getModel(),t.id),this.onstatuschange&&this.onstatuschange(this.getEditorStatus())}editorSetup(t){this.editor=monaco.editor.create(t,{value:"",language:"textplain"}),i.modes||(i.modes={},monaco.languages.getLanguages().forEach(t=>{i.modes[t.id]=t}))}on(t,e){switch(t){case"input":this.editor.onDidChangeModelContent(e);break;case"focus":this.editor.onDidFocusEditorText(e);break;case"changeCursor":this.editor.onDidChangeCursorPosition(e)}}resize(){this.editor&&this.editor.layout()}focus(){this.editor&&this.editor.focus()}getModeForPath(t){return{}}getEditorStatus(){const t=this.editor.getPosition(),e=i.modes[this.editor.getModel().getModeId()];return{row:t.lineNumber,column:t.column,line:this.editor.getModel().getLineCount(),langmode:{text:e.aliases[0],mode:e},file:this.currfile.path}}getValue(){return this.editor.getValue()}setValue(t){this.editor.setValue(t)}getEditor(){return this.editor}}e.MonacoEditorModel=i}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e,i;!function(t){let e;!function(t){class e extends t.ListViewItemTag{itemlayout(){return{el:"div",children:[{el:"afx-label",ref:"label"},{el:"p",ref:"desc",id:"ext-list-item-d-p"},{el:"p",id:"ext-list-item-b-p",children:[{el:"i",ref:"intall_status"},{el:"afx-button",ref:"btn_remove"},{el:"afx-button",ref:"btn_install"}]}]}}ondatachange(){const t=this.data;if(!t)return;const e=this.refs.label;e.iconclass="bi bi-puzzle",e.text=`${t.text} - v${t.version}`;const i=this.refs.desc;$(i).text(t.description);const a=this.refs.btn_install,s=this.refs.btn_remove;t.installed?($(s).show(),s.iconclass="bi bi-trash-fill",a.iconclass="bi bi-arrow-repeat",$(this.refs.intall_status).text(__("Installed: v{0} ",t.installed).__())):($(s).hide(),a.iconclass="fa bi-cloud-download-fill",$(this.refs.intall_status).text(" "))}init(){this.closable=!1,this.data={};const t=this.refs.btn_install,e=this.refs.btn_remove;t.onbtclick=t=>{this.data.download&&this.data.install_action&&this.data.install_action(this.data.download,t=>{this.data.installed=t,this.update(void 0)})},e.onbtclick=t=>{this.data.installed&&this.data.uninstall_action&&this.data.uninstall_action(this.data.name,()=>{delete this.data.installed,this.update(void 0)})}}reload(t){this.data=this.data}}t.define("afx-antedit-ext-list-item",e)}(e=t.tag||(t.tag={}))}(e=t.GUI||(t.GUI={})),function(e){class i extends e.BaseApplication{constructor(t){super("Antedit",t),this.currdir=void 0}main(){this.extensions={},this.eum=new a,this.fileview=this.find("fileview"),this.sidebar=this.find("sidebar"),this.sidebar_container=this.find("sidebar-tab-container"),this.bottombar=this.find("bottombar"),this.langstat=this.find("langstat"),this.editorstat=this.find("editorstat"),this.filestat=this.find("current-file-lbl"),this.extension_list_view=this.find("extension-list"),this.logger=new s(this.find("output-tab")),this.split_mode=!0,this.fileview.fetch=t=>new Promise((async function(e,i){let a;a="string"==typeof t?t.asFileHandle():t;try{const t=await a.read();return t.error?i(t.error):e(t.result)}catch(t){return i(__e(t))}}));let e="Untitled".asFileHandle();this.args&&this.args.length>0&&(this.addRecent(this.args[0].path),"dir"===this.args[0].type?this.currdir=this.args[0].path.asFileHandle():(e=this.args[0].path.asFileHandle(),this.currdir=e.parent())),this.setting.recent||(this.setting.recent=[]),this.setting.extension_repos||(this.setting.extension_repos=["https://raw.githubusercontent.com/lxsang/antos-antedit-extensions/master/extensions.json"]);const i=this.find("wrapper");$(i).css("visibility","hidden"),monaco.editor.setTheme("vs-dark"),this.eum.add(new t.application.MonacoEditorModel(this,this.find("left-tabbar"),this.find("left-editorarea"))).add(new t.application.MonacoEditorModel(this,this.find("right-tabbar"),this.find("right-editorarea"))),this.eum.onstatuschange=t=>this.updateStatus(t),$(i).css("visibility","visible"),this.setup(),this.eum.active.openFile(e)}setup(){this.sidebar_container.selectedIndex=0,this.extension_list_view.itemtag="afx-antedit-ext-list-item",this.fileview.onfileopen=t=>{if(t.data&&t.data.path&&"dir"!==t.data.type)return this.addRecent(t.data.path),this.eum.active.openFile(t.data.path.asFileHandle())},this.fileview.onfileselect=t=>{t.data&&t.data.path&&"dir"!==t.data.type&&this.eum.active.selectFile(t.data.path)},this.on("resize",()=>this.eum.resize()),this.on("focus",()=>this.eum.active.focus()),this.fileview.contextmenuHandle=(t,e)=>(e.items=[{text:"__(New file)",id:"new"},{text:"__(New folder)",id:"newdir"},{text:"__(Rename)",id:"rename"},{text:"__(Delete)",id:"delete"}],e.onmenuselect=t=>this.ctxFileMenuHandle(t),e.show(t)),this.bindKey("ALT-N",()=>this.menuAction("new")),this.bindKey("ALT-O",()=>this.menuAction("open")),this.bindKey("ALT-F",()=>this.menuAction("opendir")),this.bindKey("CTRL-S",()=>this.menuAction("save")),this.bindKey("ALT-W",()=>this.menuAction("saveas")),this.fileview.ondragndrop=t=>{const e=t.data.from.data.path.asFileHandle(),i=t.data.to.data.path;return e.move(`${i}/${e.basename}`).then((function(a){const s=i,n=e.parent().path;s.lengththis.error(__("Unable to move file/folder"),t))},this.on("filechange",t=>{let{path:e}=t.file;return"file"===t.type&&({path:e}=t.file.parent()),this.fileview.update(e)}),this.find("logger-clear").onbtclick=()=>{this.logger.clear()},void 0===this.setting.showBottomBar&&(this.setting.showBottomBar=!1);const t={name:"Editor",text:__("Editor")},e={name:"langmode",text:__("Change language mode"),shortcut:"CTRL-K"};this.eum.addAction(t,e,async t=>{try{const t=await this.openDialog("SelectionDialog",{title:__("Select language"),data:this.eum.active.getModes()});this.eum.active.setMode(t)}catch(t){}}),$(this.find("txt_ext_search")).keyup(t=>this.extension_search(t)),this.loadExtensionMetaData(),this.toggleSideBar(),this.toggleSplitMode(),this.applyAllSetting()}extension_search(t){let e;const i=this.find("txt_ext_search");switch(t.which){case 37:return t.preventDefault();case 38:return this.extension_list_view.selectPrev(),t.preventDefault();case 39:return t.preventDefault();case 40:return this.extension_list_view.selectNext(),t.preventDefault();case 13:return t.preventDefault();default:var a=i.value,s=[];if(2===a.length)return void(this.extension_list_view.data=this.extension_meta_data);if(a.length<3)return;var n=new RegExp(a,"i");for(e in this.extension_meta_data)this.extension_meta_data[e].text.match(n)&&s.push(this.extension_meta_data[e]);this.extension_list_view.data=s}}refreshExtensionRepositories(){const t=[],e=this.meta().path+"/extensions/extensions.json";for(let i of[e].concat(this.setting.extension_repos))t.push(i.asFileHandle().read("json"));Promise.all(t).then(t=>{const e={};for(let i of t.shift())e[i.name]=i;this.extension_meta_data=[];for(let a of t)for(let t of a)e[t.name]&&(t.installed=e[t.name].version),t.install_action=(e,a)=>{new i.extensions.EditorExtensionMaker(this).installZip(e).then(()=>{this.loadExtensionMetaData(),a&&a(t.version),this.notify(__("Extension '{0}' installed",t.text))}).catch(e=>{this.error(__("Unable to install '{0}': {1}",t.text,e.toString()),e)})},t.uninstall_action=(t,e)=>{new i.extensions.EditorExtensionMaker(this).uninstall(t).then(()=>{this.loadExtensionMetaData(),e&&e(),this.notify(__("Extension '{0}' uninstalled",t))}).catch(e=>{this.error(__("Unable to uninstall '{0}': {1}",t,e.toString()),e)})},this.extension_meta_data.push(t);this.extension_list_view.data=this.extension_meta_data}).catch(t=>{this.error(__("Unable to read extension from repositories: {0}",t.toString()),t)})}updateStatus(t){t||(t=this.eum.active.getEditorStatus()),this.editorstat.text=__("Row {0}, col {1}, lines: {2}",t.row,t.column,t.line),t.langmode&&(this.langstat.text=t.langmode.text),this.filestat.text=t.file;let e=this.scheme;e.apptitle!=t.file&&(e.apptitle=t.file)}toggleSideBar(){this.currdir?($(this.sidebar).show(),this.fileview.path=this.currdir.path,this.refreshExtensionRepositories()):$(this.sidebar).hide(),this.trigger("resize")}showOutput(t=!1){t&&this.showBottomBar(!0),this.bottombar.selectedIndex=0}applySetting(t){"showBottomBar"==t&&this.showBottomBar(this.setting.showBottomBar)}showBottomBar(t){this.setting.showBottomBar=t,t?$(this.bottombar).show():$(this.bottombar).hide(),this.trigger("resize")}toggleBottomBar(){this.showBottomBar(!this.setting.showBottomBar)}toggleSplitMode(){const t=this.find("right-panel"),e=this.eum.editors[1],i=this.eum.editors[0];if(this.split_mode){if(e.isDirty())return void this.notify(__("Unable to disable split view: Please save changes of modified files on the right panel"));e.closeAll(),$(t).hide(),this.split_mode=!1,i.focus()}else $(t).show(),this.split_mode=!0,e.openFile("Untitled".asFileHandle()),e.focus();this.trigger("resize")}fileMenu(){const t=this.setting.recent.map(t=>({text:t}));return{text:__("File"),nodes:[{text:__("New"),dataid:"new",shortcut:"A-N"},{text:__("Open Recent"),dataid:"recent",nodes:t,onchildselect:(t,e)=>{const i=t.data.item.data.text.asFileHandle();i.onready().then(t=>{t&&("dir"==t.type?(this.currdir=i,this.toggleSideBar()):this.eum.active.openFile(i))})}},{text:__("Open"),dataid:"open",shortcut:"A-O"},{text:__("Open Folder"),dataid:"opendir",shortcut:"A-F"},{text:__("Save"),dataid:"save",shortcut:"C-S"},{text:__("Save as"),dataid:"saveas",shortcut:"A-W"}],onchildselect:(t,e)=>this.menuAction(t.data.item.data.dataid,e)}}ctxFileMenuHandle(t){const e=t.data.item;if(!e)return;const i=e.data;if(!i)return;let a=this.fileview.selectedFile,s=this.currdir;switch(a&&"dir"===a.type&&(s=a.path.asFileHandle()),a&&"file"===a.type&&(s=a.path.asFileHandle().parent()),i.id){case"new":if(!s)return;this.openDialog("PromptDialog",{title:"__(New file)",label:"__(File name)"}).then(async t=>{const e=`${s.path}/${t}`.asFileHandle();try{return await e.write("text/plain"),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to create: {0}",t.stack),t)}});break;case"newdir":if(!s)return;this.openDialog("PromptDialog",{title:"__(New folder)",label:"__(Folder name)"}).then(async t=>{try{return await s.mk(t),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to create: {0}",s.path),t)}});break;case"rename":if(!a)return;this.openDialog("PromptDialog",{title:"__(Rename)",label:"__(File name)",value:a.filename}).then(async t=>{if(t!==a.filename){a=a.path.asFileHandle(),s=a.parent();try{return await a.move(`${s.path}/${t}`),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to rename: {0}",a.path),t)}}});break;case"delete":if(!a)return;this.openDialog("YesNoDialog",{title:"__(Delete)",iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",a.filename)}).then(async t=>{if(t){a=a.path.asFileHandle(),s=a.parent();try{return await a.remove(),this.fileview.update(s.path)}catch(t){return this.error(__("Fail to delete: {0}",a.path),t)}}})}}addRecent(t){this.setting.recent||(this.setting.recent=[]),this.setting.recent.includes(t)||(this.setting.recent.push(t),this.setting.recent.length>10&&(this.setting.recent=this.setting.recent.slice(0,10)))}menuAction(t,e){let i=this;switch(e&&(i=e),t){case"new":return i.eum.active.openFile("Untitled".asFileHandle());case"open":return i.openDialog("FileDialog",{title:__("Open file"),mimes:Array.from(i.meta().mimes).filter(t=>"dir"!==t)}).then(t=>{this.addRecent(t.file.path),i.eum.active.openFile(t.file.path.asFileHandle())});case"opendir":return i.openDialog("FileDialog",{title:__("Open folder"),mimes:["dir"]}).then((function(t){return i.addRecent(t.file.path),i.currdir=t.file.path.asFileHandle(),i.toggleSideBar()}));case"save":return i.eum.active.save();case"saveas":return i.eum.active.saveAs();default:return console.log(t)}}cleanup(t){let e;const i=this.eum.dirties();if(0!==i.length)t.preventDefault(),this.openDialog("YesNoDialog",{title:"__(Quit)",text:__("Ignore all unsaved files: {0} ?",(()=>{const t=[];for(e of Array.from(i))t.push(e.filename);return t})().join(", "))}).then(t=>{if(t){for(e of Array.from(i))e.dirty=!1;return this.quit(!1)}});else for(let t in this.extensions)this.extensions[t]&&this.extensions[t].cleanup&&this.extensions[t].cleanup()}menu(){return[this.fileMenu(),{text:"__(View)",nodes:[{text:"__(Toggle bottom bar)",dataid:"bottombar"},{text:"__(Toggle split view)",dataid:"splitview"}],onchildselect:(t,e)=>{switch(t.data.item.data.dataid){case"bottombar":return this.toggleBottomBar();case"splitview":return this.toggleSplitMode()}}}]}loadExtensionMetaData(){this.loadExtensionMetaFromFile(this.meta().path+"/extensions/extensions.json").catch(t=>this.error(__("Cannot load extension meta data"),t))}loadExtensionMetaFromFile(t){return new Promise((e,i)=>{t.asFileHandle().read("json").then(t=>{for(let e of t)for(let t of e.actions)this.eum.addAction(e,t,(t,i)=>{this.loadAndRunExtensionAction(t,i,e.root)});e()}).catch(t=>{i(__e(t))})})}loadAndRunExtensionAction(t,e,a){if(i.extensions[t])this.runExtensionAction(t,e);else{let i=`${this.meta().path}/extensions/${t}/main.js`;a&&(i=a+"/main.js"),this._api.requires(i,!0).then(()=>this.runExtensionAction(t,e)).catch(e=>this.error(__("unable to load extension: {0}",t),e))}}runExtensionAction(t,e){if(!this.extensions[t]){if(!i.extensions[t])return this.error(__("Unable to find extension: {0}",t));this.extensions[t]=new i.extensions[t](this)}if(!this.extensions[t][e])return this.error(__("Unable to find action: {0}",e));this.extensions[t].preload().then(()=>this.extensions[t][e]()).catch(t=>this.error(__("Unable to preload extension"),t))}}e.Antedit=i;class a{constructor(){this.active_editor=void 0,this.models=[]}get editors(){return this.models}set contextmenuHandle(t){for(let e of this.models)e.contextmenuHandle=t}get active(){return this.active_editor}add(t){return this.models.push(t),this.active_editor||(this.active_editor=t),t.on("focus",()=>{this.active_editor=t}),this}addAction(t,e,i){const a={id:`${t.name}:${e.name}`,label:`${t.text.__()}: ${e.text.__()}`,keybindings:[],precondition:null,keybindingContext:null,contextMenuGroupId:t.name,run:()=>i(t.name,e.name)};if(e.shortcut){const t=e.shortcut.split("-");let i=0;for(const e of t)switch(e){case"CTRL":i|=monaco.KeyMod.CtrlCmd;break;case"ALT":i|=monaco.KeyMod.Alt;break;case"SHIFT":i|=monaco.KeyMod.Shift;break;case"SUPPER":i|=monaco.KeyMod.WinCtrl;break;default:const t="KEY_"+e;monaco.KeyCode[t]?i|=monaco.KeyCode[t]:i=0}0!=i&&a.keybindings.push(i)}for(let t of this.models){const e=t.getEditor();e.getAction(a.id)||e.addAction(a)}}set onstatuschange(t){for(let e of this.models)e.onstatuschange=t}dirties(){let t=[];for(let e of this.models)t=t.concat(e.dirties());return t}resize(){for(let t of this.models)t.resize()}}class s{constructor(t){this.target=t}info(t){this.log("info",t,!0)}warn(t){this.log("warn",t,!0)}error(t){this.log("error",t,!0)}log(t,e,i){let a=$("
").attr("class","code-pad-log-"+t);if(i){let t=new Date,i=t.getDate()+"/"+(t.getMonth()+1)+"/"+t.getFullYear()+" "+t.getHours()+":"+t.getMinutes()+":"+t.getSeconds();a.text(`[${i}]: ${e.__()}`)}else a.text(e.__());$(this.target).append(a),$(this.target).scrollTop($(this.target)[0].scrollHeight)}print(t){this.log("info",t,!1)}clear(){$(this.target).empty()}}i.Logger=s,i.dependencies=["pkg://MonacoCore/path.js","pkg://MonacoCore/bundle/app.bundle.js"]}(i=t.application||(t.application={}))}(OS||(OS={})),function(t){class e{constructor(t,e){this.app=e,this.name=t}preload(){return t.API.require(t.application.Antedit.extensions[this.name].dependencies)}basedir(){return`${this.app.meta().path}/extensions/${this.name}`}notify(t){return this.app.notify(t)}error(t,e){return this.app.error(t,e)}logger(){return this.app.setting.showBottomBar?this.app.showOutput(!1):this.app.showOutput(!0),this.app.logger}metadata(e){return new Promise((i,a)=>{if(!this.app.currdir)return a(t.API.throwe(__("Current folder is not found")));`${this.app.currdir.path}/${e}`.asFileHandle().read("json").then(t=>{!t.root&&this.app.currdir&&(t.root=this.app.currdir.path),i(t)}).catch(s=>{this.app.openDialog("FileDialog",{title:__("Select build directory"),root:this.app.currdir.path,mimes:["dir"]}).then(t=>{`${t.file.path}/${e}`.asFileHandle().read("json").then(e=>{e.root||(e.root=t.file.path),i(e)}).catch(t=>a(t))}).catch(e=>a(t.API.throwe(__("Unable to read meta-data"))))})})}}e.dependencies=[],t.application.Antedit.extensions={},t.application.Antedit.EditorBaseExtension=e,t.application.Antedit.extensions.EditorExtensionMaker=class extends e{constructor(t){super("EditorExtensionMaker",t)}create(){this.logger().clear(),this.app.openDialog("FileDialog",{title:"__(New extension at)",file:{basename:__("ExtensionName")},mimes:["dir"]}).then(t=>this.mktpl(t.file.path,t.name))}build(e){this.logger().clear(),this.metadata("extension.json").then(async i=>{try{const a=await t.API.VFS.cat(i.javascripts.map(t=>`${i.root}/${t}`),"");await(i.root+"/build/debug/main.js").asFileHandle().setCache(a).write("text/plain"),await(i.root+"/build/debug/extension.json").asFileHandle().setCache(i.meta).write("object"),await t.API.VFS.copy(i.copies.map(t=>`${i.root}/${t}`),i.root+"/build/debug"),this.logger().info(__("Files generated in {0}",i.root+"/build/debug")),e&&e()}catch(t){return this.logger().error(__("Unable to build extension:{0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to read meta-data:{0}",t.stack)))}run(){this.logger().clear(),this.metadata("extension.json").then(async e=>{if(!e||!e.meta||!e.meta.name)return this.logger().error(__("Invalid extension meta-data"));try{const i=e.root+"/build/debug/main.js";t.API.shared[i]&&delete t.API.shared[i],await t.API.requires(i),this.app.extensions[e.meta.name]&&this.app.extensions[e.meta.name].cleanup&&this.app.extensions[e.meta.name].cleanup(),this.app.extensions[e.meta.name]=new t.application.Antedit.extensions[e.meta.name](this.app);for(let t of e.meta.actions)this.app.eum.addAction(e.meta,t,(t,i)=>{this.app.loadAndRunExtensionAction(t,i,e.root+"/build")});this.app.eum.active.getEditor().trigger(e.meta.name,"editor.action.quickCommand")}catch(t){return this.logger().error(__("Unable to run extension:{0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to read meta-data:{0}",t.stack)))}release(){this.logger().clear(),this.metadata("extension.json").then(async e=>{this.build(async()=>{try{await t.API.VFS.mkar(e.root+"/build/debug",`${e.root}/build/release/${e.meta.name}.zip`),this.logger().info(__("Archive created at {0}",`${e.root}/build/release/${e.meta.name}.zip`))}catch(t){return this.logger().error(__("Unable to create archive: {0}",t.stack))}})}).catch(t=>this.logger().error(__("Unable to read meta-data: {0}",t.stack)))}install(){this.logger().clear(),this.app.openDialog("FileDialog",{title:"__(Select extension archive)",mimes:[".*/zip"]}).then(async t=>{try{return await this.installZip(t.file.path),this.logger().info(__("Extension installed")),this.app.loadExtensionMetaData()}catch(t){return this.logger().error(__("Unable to install extension: {0}",t.stack))}})}installFromURL(){this.logger().clear(),this.app.openDialog("PromptDialog",{title:__("Enter URI"),label:__("Please enter extension URI:")}).then(async t=>{if(t)try{return await this.installZip(t),this.logger().info(__("Extension installed")),this.app.loadExtensionMetaData()}catch(e){return this.app.error(__("Unable to install extension: {0}",t))}})}mktpl(e,i){const a=`${e}/${i}`,s=[a,a+"/build",a+"/build/release",a+"/build/debug"],n=[["main.tpl",`${a}/${i}.js`],["meta.tpl",a+"/extension.json"]];t.API.VFS.mkdirAll(s,!0).then(async()=>{try{return await t.API.VFS.mktpl(n,this.basedir(),t=>t.format(i,`${e}/${i}`)),this.app.currdir=a.asFileHandle(),this.app.toggleSideBar(),this.app.eum.active.openFile(`${a}/${i}.js`.asFileHandle())}catch(t){return this.logger().error(__("Unable to create extension template: {0}",t.stack))}}).catch(t=>this.logger().error(__("Unable to create extension directories: {0}",t.stack)))}uninstall(t){return new Promise(async(e,i)=>{try{const i=this.app.meta().path+"/extensions",a=(i+"/extensions.json").asFileHandle(),s=await a.read("json");let n=void 0,o=void 0;for(let e in s)if(s[e].name===t){n=s[e],o=e;break}if(void 0===n)return e();await`${i}/${t}`.asFileHandle().remove(),s.splice(o,1),a.cache=s,await a.write("object"),e()}catch(t){i(t)}})}installZip(e){return new Promise(async(i,a)=>{try{await t.API.requires("os://scripts/jszip.min.js");const a=await e.asFileHandle().read("binary"),s=await JSZip.loadAsync(a),n=await s.file("extension.json").async("uint8array"),o=JSON.parse(new TextDecoder("utf-8").decode(n));await this.uninstall(o.name);const r=this.ext_dir(o.name),l=[r],h=[];for(let t in s.files)s.files[t].dir?l.push(r+"/"+t):"extension.json"!=t&&h.push(t);l.length>0?(await t.API.VFS.mkdirAll(l,!0),await this.installFiles(h,s,o)):await this.installFiles(h,s,o),i()}catch(t){a(__e(t))}})}ext_dir(t){return`${this.app.meta().path}/extensions/${t}`}installFiles(t,e,i){return 0===t.length?this.installMeta(i):new Promise(async(a,s)=>{try{const n=t.splice(0,1)[0],o=`${this.ext_dir(i.name)}/${n}`,r=await e.file(n).async("uint8array"),l=await o.asFileHandle().setCache(new Blob([r],{type:"octet/stream"})).write("text/plain");if(l.error)return s(l.error);await this.installFiles(t,e,i),a()}catch(t){s(__e(t))}})}installMeta(t){return new Promise(async(e,i)=>{const a=(this.ext_dir("")+"/extensions.json").asFileHandle();try{const s=await a.read("json"),n=[];for(let t of s)n.push(t.name);const o=n.indexOf(t.name);o>=0&&s.splice(o,1),s.push(t);try{return await a.setCache(s).write("object"),e()}catch(t){return i(__e(t))}}catch(s){try{return await a.setCache([t]).write("object"),e()}catch(t){return i(__e(t))}}})}}}(OS||(OS={}));
\ No newline at end of file
diff --git a/Antedit/build/debug/package.json b/Antedit/build/debug/package.json
index 82f3e3c..a18b50e 100644
--- a/Antedit/build/debug/package.json
+++ b/Antedit/build/debug/package.json
@@ -7,7 +7,7 @@
         "author": "Xuan Sang LE",
         "email": "mrsang@iohub.dev"
     },
-    "version": "0.1.9-a",
+    "version": "0.1.10-b",
     "category": "Development",
     "iconclass": "bi bi-journal-code",
     "mimes": [
diff --git a/Antedit/build/debug/scheme.html b/Antedit/build/debug/scheme.html
index 4ce75ad..e410cc4 100644
--- a/Antedit/build/debug/scheme.html
+++ b/Antedit/build/debug/scheme.html
@@ -1,10 +1,26 @@
 
     
         
-            
-                
- - + + + + + +
+ + +
+
+ + + + + + + + + +
diff --git a/Antedit/build/release/Antedit.zip b/Antedit/build/release/Antedit.zip index 3e822f48fc22f1a34574470b4910a4688cae7ef8..97f0f544f3178a0a5a57a84a008a8f31c5ac4448 100644 GIT binary patch delta 11380 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 baKgXTQ2*D2Z afx-list-view > div.list-contai border-radius: 0; } afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view afx-list-view i.closable:before { - color:afafaf; + color:#afafaf; } afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view ul afx-list-item:nth-child(even) li, afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-container > ul li{ @@ -48,6 +48,15 @@ afx-app-window[data-id = "antedit"] afx-tab-bar> afx-list-view > div.list-conta padding-right: 20px; border-right: 1px solid #272822; } + +afx-app-window[data-id = "antedit"] afx-tab-container[data-id="sidebar-tab-container"] afx-tab-bar> afx-list-view > div.list-container { + background-color: #333333; +} +afx-app-window[data-id = "antedit"] afx-tab-container[data-id="sidebar-tab-container"] afx-tab-bar> afx-list-view > div.list-container > ul li{ + float: none; + font-size: 20px; +} + afx-app-window[data-id = "antedit"] .afx-window-wrapper afx-vbox[data-id = "sidebar"]{ background-color:#272822; } @@ -91,49 +100,6 @@ afx-app-window[data-id = "antedit"] .afx-window-wrapper div[data-id="statctn"] a padding-left: 10px; } -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper{ - border-radius: 0px; - border: 0; - /*border: 1px solid #37373d;*/ - background-color: transparent; - box-shadow: 0px 3px 6px 0px rgba(0,0,0,0.65); -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li -{ - background-color: transparent; -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view afx-list-item li{ - background-color: transparent; - color:#afafaf; -} -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper div.list-container > ul li:hover{ - background-color: #37373d; -} - -afx-app-window[data-id = "cmd-win"] .afx-window-wrapper afx-list-view ul afx-list-item:nth-child(even) li.selected, -afx-app-window[data-id = "cmd-win"] .afx-window-wrappe dafx-list-viewafx-list-view ul li.selected -{ - background-color: #116cd6; - color:white; -} -afx-app-window[data-id = "cmd-win"] .afx-window-top{ - height: 0; - border:0; -} -afx-app-window[data-id = "cmd-win"] input{ - border: 1px solid #007acc; - border-radius: 0; - font-size: 12px; - color:#afafaf; - background-color:#272822; - padding-left: 5px; - margin: 3px; -} - -afx-app-window[data-id = "cmd-win"] .afx-window-content{ - background-color:#272822; -} - afx-app-window[data-id = "antedit"] div[data-id="output-tab"] { overflow-y: auto; overflow-x: hidden; @@ -165,4 +131,41 @@ afx-app-window[data-id = "antedit"] div[data-id="output-tab"] pre.code-pad-log-i afx-app-window[data-id = "antedit"] afx-button[ data-id="logger-clear" ] button{ border: 0; background: transparent; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item { + color: white !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item afx-label i.label-text{ + font-weight: bold !important; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item p { + margin: 0; + padding: 0; + padding-left:15px; + font-size: 11px; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item p[data-id="ext-list-item-b-p"] { + text-align: right; + font-size: 11px; +} + +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item > li { + background-color: transparent !important; + padding-right: 5px !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item > li.selected { + background-color: #116cd6 !important; +} +afx-app-window[data-id = "antedit"] afx-antedit-ext-list-item button { + height: 22px; + width: 24px; + padding: 0 !important; +} +afx-app-window[data-id = "antedit"] input[data-id="txt_ext_search"] { + background-color: transparent; + border-radius: 0; + border-color: #333; + color: white; } \ No newline at end of file diff --git a/Antedit/extensions/extensions.json b/Antedit/extensions/extensions.json index a18686f..ced33a1 100644 --- a/Antedit/extensions/extensions.json +++ b/Antedit/extensions/extensions.json @@ -21,11 +21,11 @@ "name": "release" }, { - "text": "__(Install extension from file)", + "text": "__(Install from file)", "name": "install" }, { - "text": "__(Install extension from URL)", + "text": "__(Install from URL)", "name": "installFromURL" } ] diff --git a/Antedit/package.json b/Antedit/package.json index 82f3e3c..a18b50e 100644 --- a/Antedit/package.json +++ b/Antedit/package.json @@ -7,7 +7,7 @@ "author": "Xuan Sang LE", "email": "mrsang@iohub.dev" }, - "version": "0.1.9-a", + "version": "0.1.10-b", "category": "Development", "iconclass": "bi bi-journal-code", "mimes": [ diff --git a/Antedit/ts/EditorExtensionMaker.ts b/Antedit/ts/EditorExtensionMaker.ts index b0636c8..0faadca 100644 --- a/Antedit/ts/EditorExtensionMaker.ts +++ b/Antedit/ts/EditorExtensionMaker.ts @@ -321,16 +321,57 @@ namespace OS { ); } - /** * * - * @private + * @param {string} name extension name + * @returns {Promise} + * @memberof EditorExtensionMaker + */ + uninstall(name: string): Promise + { + return new Promise(async (resolve, reject) => { + try { + const ext_path = `${this.app.meta().path}/extensions`; + const fp = `${ext_path}/extensions.json`.asFileHandle(); + const meta = await fp.read("json"); + let ext_meta = undefined; + let ext_index = undefined; + for(let idx in meta) + { + if(meta[idx].name === name) + { + ext_meta = meta[idx]; + ext_index = idx; + break; + } + } + if(ext_meta === undefined) + { + return resolve(); + } + // remove the directory + await `${ext_path}/${name}`.asFileHandle().remove(); + // update the extension file + meta.splice(ext_index, 1); + fp.cache = meta; + await fp.write('object'); + resolve(); + } catch(e) + { + reject(e); + } + }); + } + + /** + * + * * @param {string} path * @returns {Promise} * @memberof EditorExtensionMaker */ - private installZip(path: string): Promise { + installZip(path: string): Promise { return new Promise(async (resolve, reject) => { try{ await API.requires("os://scripts/jszip.min.js"); @@ -338,6 +379,8 @@ namespace OS { const zip = await JSZip.loadAsync(data); const d = await zip.file("extension.json").async("uint8array"); const meta = JSON.parse(new TextDecoder("utf-8").decode(d)); + // uninstall if exists + await this.uninstall(meta.name); const pth = this.ext_dir(meta.name); const dir = [pth]; const files = []; diff --git a/Antedit/ts/main.ts b/Antedit/ts/main.ts index b7cb4fb..8ba8e94 100644 --- a/Antedit/ts/main.ts +++ b/Antedit/ts/main.ts @@ -1,9 +1,110 @@ namespace OS { - declare var $: any; + + export namespace GUI { + export namespace tag { + class AntEditExtensionListItem extends ListViewItemTag + { + protected itemlayout(): TagLayoutType { + return { + el: "div", + children: [ + {el:"afx-label", ref: "label"}, + {el:"p", ref:"desc", id: "ext-list-item-d-p"}, + { + el:"p", + id: "ext-list-item-b-p", + children:[ + { + el: "i", + ref:"intall_status" + }, + { + el: "afx-button", + ref:"btn_remove" + }, + { + el: "afx-button", + ref:"btn_install" + } + ] + } + ] + }; + } + protected ondatachange(): void { + const v = this.data; + if (!v) { + return; + } + const label = this.refs.label as LabelTag; + label.iconclass = "bi bi-puzzle"; + label.text = `${v.text} - v${v.version}`; + // add description + const p_desc = this.refs.desc as HTMLParagraphElement; + $(p_desc).text(v.description); + + // button install + const btn_install = this.refs.btn_install as ButtonTag; + // button remove + const btn_remove = this.refs.btn_remove as ButtonTag; + + if(v.installed) + { + $(btn_remove).show(); + btn_remove.iconclass = "bi bi-trash-fill"; + btn_install.iconclass = "bi bi-arrow-repeat"; + $(this.refs.intall_status).text(__("Installed: v{0} ", v.installed).__()); + } + else + { + $(btn_remove).hide(); + btn_install.iconclass = "fa bi-cloud-download-fill"; + $(this.refs.intall_status).text(" "); + } + } + protected init(): void { + this.closable = false; + this.data = {}; + // button install + const btn_install = this.refs.btn_install as ButtonTag; + // button remove + const btn_remove = this.refs.btn_remove as ButtonTag; + btn_install.onbtclick = (e) => { + if(!this.data.download || !this.data.install_action) + { + return; + } + this.data.install_action(this.data.download, (v: string) =>{ + this.data.installed = v; + this.update(undefined); + }); + }; + + btn_remove.onbtclick = (e) => { + if(!this.data.installed || !this.data.uninstall_action) + { + return; + } + this.data.uninstall_action(this.data.name, () => { + delete this.data.installed; + this.update(undefined); + }); + }; + } + protected reload(d?: any): void { + this.data = this.data; + } + } + + define("afx-antedit-ext-list-item", AntEditExtensionListItem) + } + } export namespace application { declare var require: any; export type AnteditLogger = typeof Logger; + const DEFAULT_REPO = "https://raw.githubusercontent.com/lxsang/antos-antedit-extensions/master/extensions.json" + /** * A simple yet powerful code/text editor. * @@ -44,6 +145,13 @@ namespace OS { */ extensions: GenericObject; + /** + * Variable stores all available extension meta-data + * @type {GenericObject[]} + * @meberof Antedit + */ + private extension_meta_data: GenericObject[]; + /** * Reference to the sidebar file view UI * @@ -61,6 +169,24 @@ namespace OS { * @memberof Antedit */ private sidebar: GUI.tag.VBoxTag; + + /** + * Reference to the sidebar tab container + * + * @private + * @type {GUI.tag.TabContainerTag} + * @memberof Antedit + */ + private sidebar_container: GUI.tag.TabContainerTag; + + /** + * Reference to the extension list UI + * + * @private + * @type {GUI.tag.ListViewTag} + * @memberof Antedit + */ + private extension_list_view: GUI.tag.ListViewTag; /** * Reference to the bottom bar @@ -157,10 +283,12 @@ namespace OS { this.eum = new EditorModelManager(); this.fileview = this.find("fileview") as GUI.tag.FileViewTag; this.sidebar = this.find("sidebar") as GUI.tag.VBoxTag; + this.sidebar_container = this.find("sidebar-tab-container") as GUI.tag.TabContainerTag; this.bottombar = this.find("bottombar") as GUI.tag.TabContainerTag; this.langstat = this.find("langstat") as GUI.tag.LabelTag; this.editorstat = this.find("editorstat") as GUI.tag.LabelTag; this.filestat = this.find("current-file-lbl") as GUI.tag.LabelTag; + this.extension_list_view = this.find("extension-list") as GUI.tag.ListViewTag; this.logger = new Logger(this.find("output-tab")); this.split_mode = true; @@ -196,6 +324,8 @@ namespace OS { } if (!this.setting.recent) this.setting.recent = []; + if(!this.setting.extension_repos) + this.setting.extension_repos = [DEFAULT_REPO]; const wrapper = this.find("wrapper"); $(wrapper).css('visibility', 'hidden'); @@ -226,6 +356,8 @@ namespace OS { * @memberof Antedit */ private setup(): void { + this.sidebar_container.selectedIndex = 0; + this.extension_list_view.itemtag = "afx-antedit-ext-list-item"; this.fileview.onfileopen = (e) => { if (!e.data || !e.data.path) { return; @@ -313,7 +445,6 @@ namespace OS { if (this.setting.showBottomBar === undefined) { this.setting.showBottomBar = false; } - //TODO: support change editor model languages const extension = { name: "Editor", text: __("Editor") @@ -336,12 +467,126 @@ namespace OS { } }); + $(this.find("txt_ext_search")).keyup((e) => this.extension_search(e)); this.loadExtensionMetaData(); this.toggleSideBar(); this.toggleSplitMode(); this.applyAllSetting(); } + /** + * Search an extension from the extension list + * + * @private + * @meberof Antedit + */ + private extension_search(e: JQuery.KeyUpEvent): void + { + let k: string; + const search_box = this.find("txt_ext_search") as HTMLInputElement; + switch (e.which) { + case 37: + return e.preventDefault(); + case 38: + this.extension_list_view.selectPrev(); + return e.preventDefault(); + case 39: + return e.preventDefault(); + case 40: + this.extension_list_view.selectNext(); + return e.preventDefault(); + case 13: + return e.preventDefault(); + default: + var text = search_box.value; + var result = []; + if (text.length === 2) { + this.extension_list_view.data = this.extension_meta_data; + return; + } + if (text.length < 3) { + return; + } + + var term = new RegExp(text, "i"); + for (k in this.extension_meta_data) { + if (this.extension_meta_data[k].text.match(term)) { + result.push(this.extension_meta_data[k]); + } + } + this.extension_list_view.data = result; + } + } + + /** + * Refresh editor extensions list on the side bar + * + * @private + * @memberof Antedit + */ + private refreshExtensionRepositories(): void { + const promises = []; + const meta_file = `${this.meta().path}/extensions/extensions.json`; + for(let url of [meta_file].concat(this.setting.extension_repos)) + { + promises.push(url.asFileHandle().read('json')); + } + Promise.all(promises) + .then((results) => { + const meta = {}; + for(let el of results.shift()) + { + meta[el.name] = el; + } + this.extension_meta_data = []; + for(let result of results) + { + for(let ext of result) + { + if(meta[ext.name]) + { + ext.installed = meta[ext.name].version; + } + ext.install_action = (url: string,callback: (arg0: string) => void) => { + (new Antedit.extensions["EditorExtensionMaker"](this)) + .installZip(url) + .then(() => { + this.loadExtensionMetaData(); + if(callback) + { + callback(ext.version); + } + this.notify(__("Extension '{0}' installed", ext.text)); + }) + .catch((error: Error) => { + this.error(__("Unable to install '{0}': {1}", ext.text , error.toString()), error); + }); + + }; + ext.uninstall_action = (name: string, callback: () => void) => { + (new Antedit.extensions["EditorExtensionMaker"](this)) + .uninstall(name) + .then(() => { + this.loadExtensionMetaData(); + if(callback) + { + callback(); + } + this.notify(__("Extension '{0}' uninstalled", name)); + }) + .catch((error: Error) => { + this.error(__("Unable to uninstall '{0}': {1}", name , error.toString()), error); + }); + }; + this.extension_meta_data.push(ext); + } + } + this.extension_list_view.data = this.extension_meta_data; + }) + .catch((error) => { + this.error(__("Unable to read extension from repositories: {0}", error.toString()), error); + }); + } /** * Update the editor status bar * @@ -374,6 +619,7 @@ namespace OS { if (this.currdir) { $(this.sidebar).show(); this.fileview.path = this.currdir.path; + this.refreshExtensionRepositories(); } else { $(this.sidebar).hide(); } @@ -425,7 +671,12 @@ namespace OS { private toggleBottomBar(): void { this.showBottomBar(!this.setting.showBottomBar); } - + + /** + * Toogle split mode + * + * #memberof Antedit + **/ private toggleSplitMode(): void { const right_pannel = this.find("right-panel"); const right_editor = this.eum.editors[1]; @@ -968,6 +1219,14 @@ namespace OS { return this; } + /** + * Add an action to the editor + * + * @param {GenericObject} extension + * @param {GenericObject} action + * @param callback + * @memberof EditorModelManager + */ addAction(extension: GenericObject, action: GenericObject, callback): void { const ed_action = { id: `${extension.name}:${action.name}`, diff --git a/libantosdk/ts/app.ts b/libantosdk/ts/app.ts index e644d40..f2cf41e 100644 --- a/libantosdk/ts/app.ts +++ b/libantosdk/ts/app.ts @@ -1,5 +1,4 @@ namespace OS { - declare var $: any; export namespace application { class Logger { diff --git a/packages.json b/packages.json index acdce6a..582dcef 100644 --- a/packages.json +++ b/packages.json @@ -45,7 +45,7 @@ "description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/README.md", "category": "Development", "author": "Xuan Sang LE", - "version": "0.1.9-a", + "version": "0.1.10-b", "dependencies": ["MonacoCore@0.23.0-r"], "download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/master/Antedit/build/release/Antedit.zip" },