diff --git a/About/build/debug/main.js b/About/build/debug/main.js index 091e4e8..08ab2a9 100644 --- a/About/build/debug/main.js +++ b/About/build/debug/main.js @@ -1,35 +1 @@ - -(function() { - var About; - - About = class About extends this.OS.application.BaseApplication { - constructor(args) { - super("About", args); - } - - main() { - var me, path; - me = this; - this.container = this.find("container"); - path = "https://raw.githubusercontent.com/lxsang/antos/master/README.md"; - path.asFileHandle().read().then(function(txt) { - var converter; - converter = new showdown.Converter(); - return ($(me.container)).html(converter.makeHtml(txt)); - }).catch(() => { - return this.notify(__("Unable to read: {0}", path)); - }); - return this.find("btnclose").onbtclick = () => { - return this.quit(); - }; - } - - }; - - About.singleton = true; - - About.dependencies = ["os://scripts/showdown.min.js"]; - - this.OS.register("About", About); - -}).call(this); +(function(){var t;(t=class extends this.OS.application.BaseApplication{constructor(t){super("About",t)}main(){var t,n;return t=this,this.container=this.find("container"),(n="https://raw.githubusercontent.com/lxsang/antos/master/README.md").asFileHandle().read().then((function(n){var i;return i=new showdown.Converter,$(t.container).html(i.makeHtml(n))})).catch(()=>this.notify(__("Unable to read: {0}",n))),this.find("btnclose").onbtclick=()=>this.quit()}}).singleton=!0,t.dependencies=["os://scripts/showdown.min.js"],this.OS.register("About",t)}).call(this); \ No newline at end of file diff --git a/About/build/release/About.zip b/About/build/release/About.zip index 15ff1e9..73f77be 100644 Binary files a/About/build/release/About.zip and b/About/build/release/About.zip differ diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/main.js b/Antedit/build/debug/extensions/AntOSDKExtension/main.js new file mode 100644 index 0000000..32d29c0 --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/main.js @@ -0,0 +1,173 @@ + +(function() { + // import the CodePad application module + const App = this.OS.application.Antedit; + // define the extension + App.extensions.AntOSDKExtension = class AntOSDKExtension extends App.EditorBaseExtension { + constructor(app) { + super("AntOSDKExtension",app); + this.sdk = undefined; + this.last_target = undefined; + } + init(){ + if(! OS.API.AntOSDKBuilder) + { + throw new Error(__("{0} is not installed, please install it", "libantosdk").__()); + return; + } + if(!this.sdk) + { + this.sdk = new OS.API.AntOSDKBuilder(this.logger(),""); + } + this.logger().clear(); + } + create() { + this.init(); + this.app + .openDialog("FileDialog", { + title: "__(New AntOS package at)", + file: { basename: "PackageName" }, + mimes: ["dir"], + }) + .then((d) => { + return this.mktpl(d.file.path, d.name); + }); + } + + build() { + this.init(); + this.metadata("build.json") + .then(async (options) => { + try{ + const targets = Object.keys(options.targets).map(e =>{ + return {text: e}; + } ); + console.log(targets); + const target = await this.app.openDialog("SelectionDialog",{ + title: __("Select a build target"), + data: targets + }); + this.last_target = target.text; + await this.app.load(this.sdk.batch([target.text], options)); + } + catch(error) + { + this.logger().error(__("Unable to read build options:{0}", error.stack)); + } + }) + .catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack))); + } + load_lib(){ + this.init(); + OS.API.VFS.read_files([ + "sdk://core/ts/jquery.d.ts", + "sdk://core/ts/antos.d.ts" + ]).then((results) => { + for(const content of results) + { + monaco.languages.typescript.typescriptDefaults.addExtraLib(content, ""); + } + this.logger().info(__("Dev packages loaded")); + }) + .catch((e) => this.logger().error(__("Unable to load AntOS dev packages:{0}", e.stack))); + } + build_last() + { + this.init(); + if(!this.last_target) + { + return this.build(); + } + this.metadata("build.json") + .then(async (options) => { + try{ + await this.app.load(this.sdk.batch([this.last_target], options)); + } + catch(error) + { + this.logger().error(__("Unable to read build options:{0}", error.stack)); + } + }) + .catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack))); + } + + run(){ + this.metadata("/build/debug/package.json") + .then((v) => { + v.text = v.name; + v.path = `${v.root}/build/debug/`; + v.filename = v.pkgname; + v.type = "app"; + v.mime = "antos/app"; + if (v.icon) { + v.icon = `${v.path}/${v.icon}`; + } + if (!v.iconclass && !v.icon) { + v.iconclass = "fa fa-adn"; + } + this.logger().info(__("Installing...")); + OS.setting.system.packages[v.pkgname] = v; + if(v.app) + { + this.logger().info(__("Running {0}...", v.app)); + return OS.GUI.forceLaunch(v.app, []); + } + this.logger().error(__("{0} is not an application", v.pkgname)); + }) + .catch((e) => this.logger().error(__("Unable to read package meta-data:{0}", e.stack))); + } + + cleanup() { + if(this.sdk) + { + this.sdk = undefined; + } + } + + /*basedir() { + return "home://workspace/antos-codepad-extensions/AntOSDKExtension" + }*/ + + mktpl(path,name){ + const rpath = `${path}/${name}`; + const dirs = [ + rpath, + `${rpath}/build`, + `${rpath}/build/release`, + `${rpath}/build/debug`, + ]; + const files = [ + [`tpl/main.tpl`, `${rpath}/main.ts`], + [`tpl/build.tpl`, `${rpath}/build.json`], + [`tpl/package.tpl`, `${rpath}/package.json`], + [`tpl/README.tpl`, `${rpath}/README.md`], + [`tpl/scheme.tpl`, `${rpath}/scheme.html`], + ]; + OS.API.VFS.mkdirAll(dirs, true) + .then(async () => { + try { + await OS.API.VFS.mktpl(files, this.basedir(), (data)=>{ + return data.format(name, `${path}/${name}`); + }); + this.app.currdir = rpath.asFileHandle(); + this.app.toggleSideBar(); + return this.app.eum.active.openFile( + `${rpath}/main.ts`.asFileHandle() + ); + } catch (e) { + return this.logger().error( + __("Unable to create package from template: {0}", + e.stack) + ); + } + }) + .catch((e) => + this.logger().error(__("Unable to create extension directories: {0}", e.stack)) + ); + } + + }; + App.extensions.AntOSDKExtension.dependencies = [ + "pkg://libantosdk/main.js" + ]; +}).call(this); \ No newline at end of file diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/tpl/README.tpl b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/README.tpl new file mode 100644 index 0000000..b5ad29d --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/README.tpl @@ -0,0 +1,15 @@ +# {0} +This is an example project, generated by AntOS Development Kit + +## Howto +Use the Antedit command palette to access to the SDK functionalities: + +1. Create new project +2. Init the project from the current folder located in side bar +3. Build and run the project +4. Release the project in zip package + +## Set up build target + +Open the `build.json` file from the current project tree and add/remove +build target entries and jobs. Save the file \ No newline at end of file diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/tpl/build.tpl b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/build.tpl new file mode 100644 index 0000000..270d480 --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/build.tpl @@ -0,0 +1,70 @@ +{ + "name": "{0}", + "targets":{ + "clean": { + "jobs": [ + { + "name": "vfs-rm", + "data": ["build/debug","build/release"] + } + ] + }, + "build": { + "require": ["ts"], + "jobs":[ + { + "name": "vfs-mkdir", + "data": ["build","build/debug","build/release"] + }, + { + "name": "ts-import", + "data": ["sdk://core/ts/core.d.ts", "sdk://core/ts/jquery.d.ts","sdk://core/ts/antos.d.ts"] + }, + { + "name": "ts-compile", + "data": { + "src": ["main.ts"], + "dest": "build/debug/main.js" + } + } + ] + }, + "uglify": { + "require": ["terser"], + "jobs": [ + { + "name":"terser-uglify", + "data": ["build/debug/main.js"] + } + ] + }, + "copy": { + "jobs": [ + { + "name": "vfs-cp", + "data": { + "src": [ + "scheme.html", + "package.json", + "README.md" + ], + "dest":"build/debug" + } + } + ] + }, + "release": { + "depend": ["clean","build","uglify", "copy"], + "require": ["zip"], + "jobs": [ + { + "name": "zip-mk", + "data": { + "src":"build/debug", + "dest":"build/release/{0}.zip" + } + } + ] + } + } +} \ No newline at end of file diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/tpl/main.tpl b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/main.tpl new file mode 100644 index 0000000..dadd79d --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/main.tpl @@ -0,0 +1,18 @@ +namespace OS { + + export namespace application { + /** + * + * @class {0} + * @extends {BaseApplication} + */ + export class {0} extends BaseApplication { + constructor(args: AppArgumentsType[]) { + super("{0}", args); + } + main(): void { + // YOUR CODE HERE + } + } + } +} \ No newline at end of file diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/tpl/package.tpl b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/package.tpl new file mode 100644 index 0000000..a0a90b9 --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/package.tpl @@ -0,0 +1,16 @@ +{ + "pkgname": "{0}", + "app":"{0}", + "name":"{0}", + "description":"{0}", + "info":{ + "author": "", + "email": "" + }, + "version":"0.0.1-a", + "category":"Other", + "iconclass":"fa fa-adn", + "mimes":["none"], + "dependencies":[], + "locale": {} +} \ No newline at end of file diff --git a/Antedit/build/debug/extensions/AntOSDKExtension/tpl/scheme.tpl b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/scheme.tpl new file mode 100644 index 0000000..fbe59c9 --- /dev/null +++ b/Antedit/build/debug/extensions/AntOSDKExtension/tpl/scheme.tpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Antedit/build/debug/main.js b/Antedit/build/debug/main.js index 4924f3c..f9bf842 100644 --- a/Antedit/build/debug/main.js +++ b/Antedit/build/debug/main.js @@ -1 +1,1779 @@ -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(t){class e extends t.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.revealLine(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?{model:i}:{model:monaco.editor.createModel(t.cache,void 0,e)}}getModes(){return monaco.languages.getLanguages()}setTheme(t){}setMode(t){}editorSetup(t){this.editor=monaco.editor.create(t,{value:"",language:"textplain"}),e.modes||(e.modes={},monaco.languages.getLanguages().forEach(t=>{e.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(),i=e.modes[this.editor.getModel().getModeId()];return{row:t.lineNumber,column:t.column,line:this.editor.getModel().getLineCount(),langmode:{text:i.aliases[0],mode:i},file:this.currfile.path}}getValue(){return this.editor.getValue()}setValue(t){this.editor.setValue(t)}getEditor(){return this.editor}}t.MonacoEditorModel=e}(e=t.application||(t.application={}))}(OS||(OS={})),__monaco_public_path__="VFS/get/"+"pkg://MonacoCore/bundle/".asFileHandle().path+"/",function(t){let e;!function(t){class e extends t.BaseApplication{constructor(t){super("Antedit",t),this.currdir=void 0}main(){this.extensions={},this.eum=new i,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 a(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 s=this.find("wrapper");$(s).css("visibility","hidden"),monaco.editor.setTheme("vs-dark"),this.eum.add(new t.MonacoEditorModel(this,this.find("left-tabbar"),this.find("left-editorarea"))).add(new t.MonacoEditorModel(this,this.find("right-tabbar"),this.find("right-editorarea"))),this.eum.onstatuschange=t=>this.updateStatus(t),$(s).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),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,i,a){if(e.extensions[t])this.runExtensionAction(t,i);else{let e=`${this.meta().path}/extensions/${t}/main.js`;a&&(e=a+"/main.js"),this._api.requires(e,!0).then(()=>this.runExtensionAction(t,i)).catch(e=>this.error(__("unable to load extension: {0}",t),e))}}runExtensionAction(t,i){if(!this.extensions[t]){if(!e.extensions[t])return this.error(__("Unable to find extension: {0}",t));this.extensions[t]=new e.extensions[t](this)}if(!this.extensions[t][i])return this.error(__("Unable to find action: {0}",i));this.extensions[t].preload().then(()=>this.extensions[t][i]()).catch(t=>this.error(__("Unable to preload extension"),t))}}t.Antedit=e;class i{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.__()}`,precondition:null,keybindingContext:null,contextMenuGroupId:t.name,run:()=>i(t.name,e.name)};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 a{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()}}e.Logger=a,e.dependencies=["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 CodePad 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 (OS) {
+    let application;
+    (function (application) {
+        class BaseEditorModel {
+            /**
+             * Creates an instance of BaseEditorModel.
+             *
+             * @param {Antedit} app parent app
+             * @param {GUI.tag.TabBarTag} tabbar tabbar DOM element
+             * @param {HTMLElement} editorarea editor container DOM element
+             * @memberof BaseEditorModel
+             */
+            constructor(app, tabbar, editorarea) {
+                this.container = editorarea;
+                this.currfile = "Untitled".asFileHandle();
+                this.tabbar = tabbar;
+                this.editorSetup(editorarea);
+                this.app = app;
+                this.editormux = false;
+                this.onstatuschange = undefined;
+                this.on("focus", () => {
+                    if (this.onstatuschange)
+                        this.onstatuschange(this.getEditorStatus());
+                });
+                this.on("input", () => {
+                    if (this.editormux) {
+                        this.editormux = false;
+                        return false;
+                    }
+                    if (!this.currfile.dirty) {
+                        this.currfile.dirty = true;
+                        this.currfile.text += "*";
+                        return this.tabbar.update(undefined);
+                    }
+                });
+                this.on("changeCursor", () => {
+                    if (this.onstatuschange)
+                        this.onstatuschange(this.getEditorStatus());
+                });
+                this.tabbar.ontabselect = (e) => {
+                    return this.selecteTab($(e.data.item).index());
+                };
+                this.tabbar.ontabclose = (e) => {
+                    const it = e.data.item;
+                    if (!it) {
+                        return false;
+                    }
+                    if (!it.data.dirty) {
+                        return this.closeTab(it);
+                    }
+                    this.app.openDialog("YesNoDialog", {
+                        title: __("Close tab"),
+                        text: __("Close without saving ?"),
+                    }).then((d) => {
+                        if (d) {
+                            return this.closeTab(it);
+                        }
+                        return this.focus();
+                    });
+                    return false;
+                };
+            }
+            /**
+             * Find a tab on the tabbar corresponding to a file handle
+             *
+             * @private
+             * @param {EditorFileHandle} file then file handle to search
+             * @returns {number}
+             * @memberof BaseEditorModel
+             */
+            findTabByFile(file) {
+                const lst = this.tabbar.items;
+                const its = (() => {
+                    const result = [];
+                    for (let i = 0; i < lst.length; i++) {
+                        const d = lst[i];
+                        if (d.hash() === file.hash()) {
+                            result.push(i);
+                        }
+                    }
+                    return result;
+                })();
+                if (its.length === 0) {
+                    return -1;
+                }
+                return its[0];
+            }
+            /**
+             * Create new tab when opening a file
+             *
+             * @private
+             * @param {EditorFileHandle} file
+             * @memberof BaseEditorModel
+             */
+            newTab(file) {
+                file.text = file.basename ? file.basename : file.path;
+                if (!file.cache) {
+                    file.cache = "";
+                }
+                file.textModel = this.newTextModelFrom(file);
+                this.currfile.selected = false;
+                file.selected = true;
+                //console.log cnt
+                this.tabbar.push(file);
+            }
+            /**
+             * Close a tab when a file is closed
+             *
+             * @private
+             * @param {GUI.tag.ListViewItemTag} it reference to the tab to close
+             * @returns {boolean}
+             * @memberof BaseEditorModel
+             */
+            closeTab(it) {
+                this.tabbar.delete(it);
+                const cnt = this.tabbar.items.length;
+                if (cnt === 0) {
+                    this.openFile("Untitled".asFileHandle());
+                    return false;
+                }
+                this.tabbar.selected = cnt - 1;
+                return false;
+            }
+            /**
+             * Select a tab by its index
+             *
+             * @private
+             * @param {number} i tab index
+             * @returns {void}
+             * @memberof BaseEditorModel
+             */
+            selecteTab(i) {
+                //return if i is @tabbar.get "selidx"
+                const file = this.tabbar.items[i];
+                if (!file) {
+                    return;
+                }
+                //return if file is @currfile
+                if (this.currfile !== file) {
+                    this.currfile.textModel = this.getTexModel();
+                    this.currfile.selected = false;
+                    this.currfile = file;
+                }
+                this.editormux = true;
+                this.setTextModel(file.textModel);
+                if (this.onstatuschange)
+                    this.onstatuschange(this.getEditorStatus());
+                this.focus();
+            }
+            /**
+             * Select an opened file, this will select the corresponding tab
+             *
+             * @param {(EditorFileHandle | string)} file
+             * @memberof BaseEditorModel
+             */
+            selectFile(file) {
+                const i = this.findTabByFile(file.asFileHandle());
+                if (i !== -1) {
+                    this.tabbar.selected = i;
+                }
+            }
+            /**
+             * Open a file in new tab. If the file is already opened,
+             * the just select the tab
+             *
+             *
+             * @param {EditorFileHandle} file file to open
+             * @returns {void}
+             * @memberof BaseEditorModel
+             */
+            openFile(file) {
+                //find tab
+                const i = this.findTabByFile(file);
+                if (i !== -1) {
+                    this.tabbar.selected = i;
+                    return;
+                }
+                if (file.path.toString() === "Untitled") {
+                    this.newTab(file);
+                    return;
+                }
+                file.read()
+                    .then((d) => {
+                    file.cache = d || "";
+                    return this.newTab(file);
+                })
+                    .catch((e) => {
+                    return this.app.error(__("Unable to open: {0}", file.path), e);
+                });
+            }
+            /**
+             * write a file
+             *
+             * @private
+             * @param {EditorFileHandle} file
+             * @memberof BaseEditorModel
+             */
+            write(file) {
+                this.currfile.cache = this.getValue();
+                file.write("text/plain")
+                    .then((d) => {
+                    file.dirty = false;
+                    file.text = file.basename;
+                    this.tabbar.update(undefined);
+                })
+                    .catch((e) => this.app.error(__("Unable to save file: {0}", file.path), e));
+            }
+            /**
+             * Save the current opened file
+             *
+             * @return {*}  {void}
+             * @memberof BaseEditorModel
+             */
+            save() {
+                this.currfile.cache = this.getValue();
+                if (this.currfile.basename) {
+                    return this.write(this.currfile);
+                }
+                return this.saveAs();
+            }
+            /**
+             * Save the current file as another file
+             *
+             * @public
+             * @memberof BaseEditorModel
+             */
+            saveAs() {
+                this.app.openDialog("FileDialog", {
+                    title: __("Save as"),
+                    file: this.currfile,
+                }).then((f) => {
+                    let d = f.file.path.asFileHandle();
+                    if (f.file.type === "file") {
+                        d = d.parent();
+                    }
+                    this.currfile.setPath(`${d.path}/${f.name}`);
+                    this.write(this.currfile);
+                });
+            }
+            /**
+             * Get all dirty file handles in the editor
+             *
+             * @return {*}  {EditorFileHandle[]}
+             * @memberof BaseEditorModel
+             */
+            dirties() {
+                const result = [];
+                for (let v of Array.from(this.tabbar.items)) {
+                    if (v.dirty) {
+                        result.push(v);
+                    }
+                }
+                return result;
+            }
+            /**
+             * Context menu handle for the editor
+             *
+             * @memberof BaseEditorModel
+             */
+            set contextmenuHandle(cb) {
+                this.container.contextmenuHandle = cb;
+            }
+            /**
+             * Close all opened files
+             *
+             * @memberof BaseEditorModel
+             */
+            closeAll() {
+                this.tabbar.items = [];
+                this.resetEditor();
+            }
+            /**
+             * Check whether the editor is dirty
+             *
+             * @return {*}  {boolean}
+             * @memberof BaseEditorModel
+             */
+            isDirty() {
+                return this.dirties().length > 0;
+            }
+        }
+        application.BaseEditorModel = BaseEditorModel;
+    })(application = OS.application || (OS.application = {}));
+})(OS || (OS = {}));
+
+var OS;
+(function (OS) {
+    let application;
+    (function (application) {
+        /**
+         * Wrapper model for the ACE text editor
+         *
+         * @export
+         * @class MonacoEditorModel
+         * @extends {BaseEditorModel}
+         */
+        class MonacoEditorModel extends OS.application.BaseEditorModel {
+            /**
+             * Creates an instance of MonacoEditorModel.
+             * @param {MonacoEditorModel} app MonacoEditorModel instance
+             * @param {GUI.tag.TabBarTag} tabbar tabbar element
+             * @param {HTMLElement} editorarea main editor container element
+             * @memberof MonacoEditorModel
+             */
+            constructor(app, tabbar, editorarea) {
+                super(app, tabbar, editorarea);
+            }
+            /**
+             * Reset the editor
+             *
+             * @protected
+             * @memberof MonacoEditorModel
+             */
+            resetEditor() {
+                this.setValue("");
+                // TODO create new textmodel
+            }
+            /**
+             * Get a text model from the current editor session
+             *
+             * @protected
+             * @return {*}
+             * @memberof MonacoEditorModel
+             */
+            getTexModel() {
+                return {
+                    model: this.editor.getModel(),
+                    position: this.editor.getPosition()
+                };
+            }
+            /**
+             * Set text model to current editor session
+             *
+             * @protected
+             * @param {*} model
+             * @memberof MonacoEditorModel
+             */
+            setTextModel(model) {
+                this.editor.setModel(model.model);
+                if (model.position) {
+                    this.editor.setPosition(model.position);
+                    this.editor.revealLineInCenter(model.position.lineNumber);
+                }
+            }
+            /**
+             * Create new editor model from file
+             *
+             * @protected
+             * @param {EditorFileHandle} file
+             * @return {*}  {*}
+             * @memberof MonacoEditorModel
+             */
+            newTextModelFrom(file) {
+                if (file.path.toString() === "Untitled") {
+                    return {
+                        model: monaco.editor.createModel(file.cache, "textplain")
+                    };
+                }
+                const uri = monaco.Uri.parse(file.path);
+                const model = monaco.editor.getModel(uri);
+                if (model) {
+                    return { model: model };
+                }
+                return {
+                    model: monaco.editor.createModel(file.cache, undefined, uri)
+                };
+            }
+            /**
+             * Get language modes
+             *
+             * @return {*}  {GenericObject[]}
+             * @memberof MonacoEditorModel
+             */
+            getModes() {
+                //const list = [];
+                //return list;
+                return monaco.languages.getLanguages();
+            }
+            /**
+             * Set the editor theme
+             *
+             * @param {string} theme theme name
+             * @memberof MonacoEditorModel
+             */
+            setTheme(theme) {
+            }
+            /**
+             * Set editor language mode
+             *
+             * The mode object should be in the following format:
+             * ```ts
+             * {
+             *  text: string,
+             *  mode: string
+             * }
+             * ```
+             *
+             * @param {GenericObject} m language mode object
+             * @memberof MonacoEditorModel
+             */
+            setMode(m) {
+            }
+            /**
+             * Setup the editor
+             *
+             * @protected
+             * @param {HTMLElement} el editor container DOM
+             * @memberof MonacoEditorModel
+             */
+            editorSetup(el) {
+                this.editor = monaco.editor.create(el, {
+                    value: "",
+                    language: 'textplain'
+                });
+                if (!MonacoEditorModel.modes) {
+                    MonacoEditorModel.modes = {};
+                    monaco.languages.getLanguages().forEach((el) => {
+                        MonacoEditorModel.modes[el.id] = el;
+                    });
+                }
+            }
+            /**
+             * Register to editor event
+             *
+             * @param {string} evt_str event name
+             * @param {() => void} callback callback function
+             * @memberof MonacoEditorModel
+             */
+            on(evt_str, callback) {
+                switch (evt_str) {
+                    case "input":
+                        this.editor.onDidChangeModelContent(callback);
+                        break;
+                    case "focus":
+                        this.editor.onDidFocusEditorText(callback);
+                        break;
+                    case "changeCursor":
+                        this.editor.onDidChangeCursorPosition(callback);
+                        break;
+                    default:
+                        break;
+                }
+            }
+            /**
+             * Resize the editor
+             *
+             * @memberof MonacoEditorModel
+             */
+            resize() {
+                if (this.editor)
+                    this.editor.layout();
+            }
+            /**
+             * Focus on the editor
+             *
+             * @memberof MonacoEditorModel
+             */
+            focus() {
+                if (this.editor)
+                    this.editor.focus();
+            }
+            /**
+             * Get language mode from path
+             *
+             * @protected
+             * @param {string} path
+             * @return {*}  {GenericObject}
+             * @memberof MonacoEditorModel
+             */
+            getModeForPath(path) {
+                return {};
+            }
+            /**
+             * Get the editor status
+             *
+             * @return {*}  {GenericObject}
+             * @memberof MonacoEditorModel
+             */
+            getEditorStatus() {
+                const pos = this.editor.getPosition();
+                const mode = MonacoEditorModel.modes[this.editor.getModel().getModeId()];
+                return {
+                    row: pos.lineNumber,
+                    column: pos.column,
+                    line: this.editor.getModel().getLineCount(),
+                    langmode: {
+                        text: mode.aliases[0],
+                        mode: mode
+                    },
+                    file: this.currfile.path
+                };
+            }
+            /**
+             * Get editor value
+             *
+             * @return {*}  {string}
+             * @memberof MonacoEditorModel
+             */
+            getValue() {
+                return this.editor.getValue();
+            }
+            /**
+             * Set editor value
+             *
+             * @param {string} value
+             * @memberof MonacoEditorModel
+             */
+            setValue(value) {
+                this.editor.setValue(value);
+            }
+            getEditor() {
+                return this.editor;
+            }
+        }
+        application.MonacoEditorModel = MonacoEditorModel;
+    })(application = OS.application || (OS.application = {}));
+})(OS || (OS = {}));
+
+__monaco_public_path__ = "VFS/get/" + "pkg://MonacoCore/bundle/".asFileHandle().path + "/";
+var OS;
+(function (OS) {
+    let application;
+    (function (application) {
+        /**
+         * A simple yet powerful code/text editor.
+         *
+         * Antedit is the default text editor shipped with
+         * AntOS base system. It is based on the Monaco editor
+         * which power the VS Code IDE.
+         *
+         * @export
+         * @class Antedit
+         * @extends {BaseApplication}
+         */
+        class Antedit extends application.BaseApplication {
+            /**
+             *Creates an instance of Antedit.
+             * @param {AppArgumentsType[]} args application arguments
+             * @memberof Antedit
+             */
+            constructor(args) {
+                super("Antedit", args);
+                this.currdir = undefined;
+            }
+            /**
+             * Main application entry point
+             *
+             * @returns {void}
+             * @memberof Antedit
+             */
+            main() {
+                this.extensions = {};
+                this.eum = new EditorModelManager();
+                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 Logger(this.find("output-tab"));
+                this.split_mode = true;
+                this.fileview.fetch = (path) => new Promise(async function (resolve, reject) {
+                    let dir;
+                    if (typeof path === "string") {
+                        dir = path.asFileHandle();
+                    }
+                    else {
+                        dir = path;
+                    }
+                    try {
+                        const d = await dir
+                            .read();
+                        if (d.error) {
+                            return reject(d.error);
+                        }
+                        return resolve(d.result);
+                    }
+                    catch (e) {
+                        return reject(__e(e));
+                    }
+                });
+                let file = "Untitled".asFileHandle();
+                if (this.args && this.args.length > 0) {
+                    this.addRecent(this.args[0].path);
+                    if (this.args[0].type === "dir") {
+                        this.currdir = this.args[0].path.asFileHandle();
+                    }
+                    else {
+                        file = this.args[0].path.asFileHandle();
+                        this.currdir = file.parent();
+                    }
+                }
+                if (!this.setting.recent)
+                    this.setting.recent = [];
+                const wrapper = this.find("wrapper");
+                $(wrapper).css('visibility', 'hidden');
+                monaco.editor.setTheme("vs-dark");
+                // add editor instance
+                this.eum
+                    .add(new OS.application.MonacoEditorModel(this, this.find("left-tabbar"), this.find("left-editorarea")))
+                    .add(new OS.application.MonacoEditorModel(this, this.find("right-tabbar"), this.find("right-editorarea")));
+                this.eum.onstatuschange = (st) => this.updateStatus(st);
+                $(wrapper).css('visibility', 'visible');
+                this.setup();
+                this.eum.active.openFile(file);
+                /*this.load(new Promise((resolve, reject) => {
+                    require.config({ paths: { 'vs': "pkg://MonacoCore/vs".asFileHandle().getlink() }});
+                    require(['vs/editor/editor.main'], () => {
+                        
+                        resolve(undefined);
+                    });
+                }))*/
+            }
+            /**
+             * Set up the text editor
+             *
+             * @private
+             * @returns {void}
+             * @memberof Antedit
+             */
+            setup() {
+                this.fileview.onfileopen = (e) => {
+                    if (!e.data || !e.data.path) {
+                        return;
+                    }
+                    if (e.data.type === "dir") {
+                        return;
+                    }
+                    this.addRecent(e.data.path);
+                    return this.eum.active.openFile(e.data.path.asFileHandle());
+                };
+                this.fileview.onfileselect = (e) => {
+                    if (!e.data || !e.data.path) {
+                        return;
+                    }
+                    if (e.data.type === "dir") {
+                        return;
+                    }
+                    this.eum.active.selectFile(e.data.path);
+                };
+                this.on("resize", () => this.eum.resize());
+                this.on("focus", () => this.eum.active.focus());
+                this.fileview.contextmenuHandle = (e, m) => {
+                    m.items = [
+                        { text: "__(New file)", id: "new" },
+                        { text: "__(New folder)", id: "newdir" },
+                        { text: "__(Rename)", id: "rename" },
+                        { text: "__(Delete)", id: "delete" },
+                    ];
+                    m.onmenuselect = (e) => {
+                        return this.ctxFileMenuHandle(e);
+                    };
+                    return m.show(e);
+                };
+                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 = (e) => {
+                    const src = e.data.from.data.path.asFileHandle();
+                    const des = e.data.to.data.path;
+                    return src
+                        .move(`${des}/${src.basename}`)
+                        .then(function (d) {
+                        const p1 = des;
+                        const p2 = src.parent().path;
+                        if (p1.length < p2.length) {
+                            e.data.to.update(p1);
+                            e.data
+                                .from.parent.update(p2);
+                        }
+                        else {
+                            e.data
+                                .from.parent.update(p2);
+                            e.data.to.update(p1);
+                        }
+                    })
+                        .catch((e) => this.error(__("Unable to move file/folder"), e));
+                };
+                this.on("filechange", (data) => {
+                    let { path } = data.file;
+                    if (data.type === "file") {
+                        ({ path } = data.file.parent());
+                    }
+                    return this.fileview.update(path);
+                });
+                this.find("logger-clear").onbtclick = () => {
+                    this.logger.clear();
+                };
+                if (this.setting.showBottomBar === undefined) {
+                    this.setting.showBottomBar = false;
+                }
+                this.loadExtensionMetaData();
+                this.toggleSideBar();
+                this.toggleSplitMode();
+                this.applyAllSetting();
+            }
+            /**
+             * Update the editor status bar
+             *
+             * @private
+             * @memberof Antedit
+             */
+            updateStatus(stat = undefined) {
+                if (!stat)
+                    stat = this.eum.active.getEditorStatus();
+                this.editorstat.text = __("Row {0}, col {1}, lines: {2}", stat.row, stat.column, stat.line);
+                if (stat.langmode)
+                    this.langstat.text = stat.langmode.text;
+                this.filestat.text = stat.file;
+                let win = this.scheme;
+                if (win.apptitle != stat.file)
+                    win.apptitle = stat.file;
+            }
+            /**
+             * Show or hide the SideBar
+             *
+             * @memberof Antedit
+             */
+            toggleSideBar() {
+                if (this.currdir) {
+                    $(this.sidebar).show();
+                    this.fileview.path = this.currdir.path;
+                }
+                else {
+                    $(this.sidebar).hide();
+                }
+                this.trigger("resize");
+            }
+            showOutput(toggle = false) {
+                if (toggle)
+                    this.showBottomBar(true);
+                this.bottombar.selectedIndex = 0;
+            }
+            /**
+             * Apply [[showBottomBar]] from user setting value
+             *
+             * @protected
+             * @param {string} k
+             * @memberof Antedit
+             */
+            applySetting(k) {
+                if (k == "showBottomBar") {
+                    this.showBottomBar(this.setting.showBottomBar);
+                }
+            }
+            /**
+             * Show or hide the bottom bar and
+             * save the value to user setting
+             *
+             * @param {boolean} v
+             * @memberof Antedit
+             */
+            showBottomBar(v) {
+                this.setting.showBottomBar = v;
+                if (v) {
+                    $(this.bottombar).show();
+                }
+                else {
+                    $(this.bottombar).hide();
+                }
+                this.trigger("resize");
+            }
+            /**
+             * toggle the bottom bar
+             *
+             * @memberof Antedit
+             */
+            toggleBottomBar() {
+                this.showBottomBar(!this.setting.showBottomBar);
+            }
+            toggleSplitMode() {
+                const right_pannel = this.find("right-panel");
+                const right_editor = this.eum.editors[1];
+                const left_editor = this.eum.editors[0];
+                if (this.split_mode) {
+                    // before hide check if there is dirty files
+                    if (right_editor.isDirty()) {
+                        this.notify(__("Unable to disable split view: Please save changes of modified files on the right panel"));
+                        return;
+                    }
+                    right_editor.closeAll();
+                    $(right_pannel).hide();
+                    this.split_mode = false;
+                    left_editor.focus();
+                }
+                else {
+                    $(right_pannel).show();
+                    this.split_mode = true;
+                    right_editor.openFile("Untitled".asFileHandle());
+                    right_editor.focus();
+                }
+                this.trigger("resize");
+            }
+            /**
+             * File menu definition
+             *
+             * @private
+             * @returns {GUI.BasicItemType}
+             * @memberof Antedit
+             */
+            fileMenu() {
+                const recent = this.setting.recent.map((i) => {
+                    return { text: i };
+                });
+                return {
+                    text: __("File"),
+                    nodes: [
+                        { text: __("New"), dataid: "new", shortcut: "A-N" },
+                        {
+                            text: __("Open Recent"),
+                            dataid: "recent",
+                            nodes: recent,
+                            onchildselect: (e, r) => {
+                                const handle = e.data.item.data.text.asFileHandle();
+                                handle.onready().then((meta) => {
+                                    if (!meta) {
+                                        return;
+                                    }
+                                    if (meta.type == "dir") {
+                                        this.currdir = handle;
+                                        this.toggleSideBar();
+                                    }
+                                    else {
+                                        this.eum.active.openFile(handle);
+                                    }
+                                });
+                            }
+                        },
+                        { 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: (e, r) => {
+                        return this.menuAction(e.data.item.data.dataid, r);
+                    },
+                };
+            }
+            /**
+             * Context menu definition
+             *
+             * @private
+             * @param {GUI.TagEventType} e
+             * @returns {void}
+             * @memberof Antedit
+             */
+            ctxFileMenuHandle(e) {
+                const el = e.data.item;
+                if (!el) {
+                    return;
+                }
+                const data = el.data;
+                if (!data) {
+                    return;
+                }
+                let file = this
+                    .fileview.selectedFile;
+                let dir = this.currdir;
+                if (file && file.type === "dir") {
+                    dir = file.path.asFileHandle();
+                }
+                if (file && file.type === "file") {
+                    dir = file.path.asFileHandle().parent();
+                }
+                switch (data.id) {
+                    case "new":
+                        if (!dir) {
+                            return;
+                        }
+                        this.openDialog("PromptDialog", {
+                            title: "__(New file)",
+                            label: "__(File name)",
+                        }).then(async (d) => {
+                            const fp = `${dir.path}/${d}`.asFileHandle();
+                            try {
+                                const r = await fp.write("text/plain");
+                                return this.fileview.update(dir.path);
+                            }
+                            catch (e) {
+                                return this.error(__("Fail to create: {0}", e.stack), e);
+                            }
+                        });
+                        break;
+                    case "newdir":
+                        if (!dir) {
+                            return;
+                        }
+                        this.openDialog("PromptDialog", {
+                            title: "__(New folder)",
+                            label: "__(Folder name)",
+                        }).then(async (d) => {
+                            try {
+                                const r = await dir.mk(d);
+                                return this.fileview.update(dir.path);
+                            }
+                            catch (e) {
+                                return this.error(__("Fail to create: {0}", dir.path), e);
+                            }
+                        });
+                        break;
+                    case "rename":
+                        if (!file) {
+                            return;
+                        }
+                        this.openDialog("PromptDialog", {
+                            title: "__(Rename)",
+                            label: "__(File name)",
+                            value: file.filename,
+                        }).then(async (d) => {
+                            if (d === file.filename) {
+                                return;
+                            }
+                            file = file.path.asFileHandle();
+                            dir = file.parent();
+                            try {
+                                const r = await file.move(`${dir.path}/${d}`);
+                                return this.fileview.update(dir.path);
+                            }
+                            catch (e) {
+                                return this.error(__("Fail to rename: {0}", file.path), e);
+                            }
+                        });
+                        break;
+                    case "delete":
+                        if (!file) {
+                            return;
+                        }
+                        this.openDialog("YesNoDialog", {
+                            title: "__(Delete)",
+                            iconclass: "fa fa-question-circle",
+                            text: __("Do you really want to delete: {0}?", file.filename),
+                        }).then(async (d) => {
+                            if (!d) {
+                                return;
+                            }
+                            file = file.path.asFileHandle();
+                            dir = file.parent();
+                            try {
+                                const r = await file.remove();
+                                return this.fileview.update(dir.path);
+                            }
+                            catch (e) {
+                                return this.error(__("Fail to delete: {0}", file.path), e);
+                            }
+                        });
+                        break;
+                    default:
+                }
+            }
+            /**
+             * Add a file to recent files setting
+             *
+             * @private
+             * @param {string} file
+             * @memberof Antedit
+             */
+            addRecent(file) {
+                if (!this.setting.recent)
+                    this.setting.recent = [];
+                if (this.setting.recent.includes(file)) {
+                    return;
+                }
+                this.setting.recent.push(file);
+                if (this.setting.recent.length > 10)
+                    this.setting.recent = this.setting.recent.slice(0, 10);
+            }
+            /**
+             * Menu action definition
+             *
+             * @private
+             * @param {string} dataid
+             * @param {Antedit} [r]
+             * @returns {void}
+             * @memberof Antedit
+             */
+            menuAction(dataid, r) {
+                let me = this;
+                if (r) {
+                    me = r;
+                }
+                switch (dataid) {
+                    case "new":
+                        return me.eum.active.openFile("Untitled".asFileHandle());
+                    case "open":
+                        return me
+                            .openDialog("FileDialog", {
+                            title: __("Open file"),
+                            mimes: Array.from(me.meta().mimes).filter((v) => v !== "dir"),
+                        })
+                            .then((f) => {
+                            this.addRecent(f.file.path);
+                            me.eum.active.openFile(f.file.path.asFileHandle());
+                        });
+                    case "opendir":
+                        return me
+                            .openDialog("FileDialog", {
+                            title: __("Open folder"),
+                            mimes: ["dir"],
+                        })
+                            .then(function (f) {
+                            me.addRecent(f.file.path);
+                            me.currdir = f.file.path.asFileHandle();
+                            return me.toggleSideBar();
+                        });
+                    case "save":
+                        return me.eum.active.save();
+                    case "saveas":
+                        return me.eum.active.saveAs();
+                    default:
+                        return console.log(dataid);
+                }
+            }
+            /**
+             * Cleanup the editor before exiting.
+             *
+             * @param {BaseEvent} evt
+             * @returns {void}
+             * @memberof Antedit
+             */
+            cleanup(evt) {
+                let v;
+                const dirties = this.eum.dirties();
+                if (dirties.length === 0) {
+                    // cleanup all extension
+                    for (let k in this.extensions) {
+                        if (this.extensions[k] && this.extensions[k].cleanup) {
+                            this.extensions[k].cleanup();
+                        }
+                    }
+                    return;
+                }
+                evt.preventDefault();
+                this.openDialog("YesNoDialog", {
+                    title: "__(Quit)",
+                    text: __("Ignore all unsaved files: {0} ?", (() => {
+                        const result1 = [];
+                        for (v of Array.from(dirties)) {
+                            result1.push(v.filename);
+                        }
+                        return result1;
+                    })().join(", ")),
+                }).then((d) => {
+                    if (d) {
+                        for (v of Array.from(dirties)) {
+                            v.dirty = false;
+                        }
+                        return this.quit(false);
+                    }
+                });
+            }
+            /**
+             * Application menu definition
+             *
+             * @returns {GUI.BasicItemType[]}
+             * @memberof Antedit
+             */
+            menu() {
+                return [
+                    this.fileMenu(),
+                    {
+                        text: "__(View)",
+                        nodes: [
+                            {
+                                text: "__(Toggle bottom bar)",
+                                dataid: "bottombar"
+                            },
+                            {
+                                text: "__(Toggle split view)",
+                                dataid: "splitview"
+                            }
+                        ],
+                        onchildselect: (e, r) => {
+                            switch (e.data.item.data.dataid) {
+                                case "bottombar":
+                                    return this.toggleBottomBar();
+                                case "splitview":
+                                    return this.toggleSplitMode();
+                                    break;
+                                default:
+                                    break;
+                            }
+                            r;
+                        },
+                    },
+                ];
+            }
+            /**
+             * Load the extension meta data from `extension.json` file
+             *
+             * @memberof AntEdit
+             */
+            loadExtensionMetaData() {
+                this.loadExtensionMetaFromFile(`${this.meta().path}/extensions/extensions.json`)
+                    .catch((e) => {
+                    return this.error(__("Cannot load extension meta data"), e);
+                });
+            }
+            /**
+             * Load extension meta-data from specific file
+             *
+             * @private
+             * @param {string} path
+             * @return {*}  {Promise}
+             * @memberof AntEdit
+             */
+            loadExtensionMetaFromFile(path) {
+                return new Promise((resolve, reject) => {
+                    path
+                        .asFileHandle()
+                        .read("json")
+                        .then((d) => {
+                        for (let extension of d) {
+                            for (let act of extension.actions) {
+                                this.eum.addAction(extension, act, (e_name, a_name) => {
+                                    this.loadAndRunExtensionAction(e_name, a_name, extension.root);
+                                });
+                            }
+                        }
+                        resolve();
+                    })
+                        .catch((e) => {
+                        reject(__e(e));
+                    });
+                });
+            }
+            /**
+             * Load extension then run an action
+             *
+             * @param name extension name
+             * @param action action name
+             * @memberof AntEdit
+             */
+            loadAndRunExtensionAction(name, action, root) {
+                //verify if the extension is load
+                if (!Antedit.extensions[name]) {
+                    //load the extension
+                    let path = `${this.meta().path}/extensions/${name}/main.js`;
+                    if (root)
+                        path = `${root}/main.js`;
+                    this._api
+                        .requires(path, true)
+                        .then(() => this.runExtensionAction(name, action))
+                        .catch((e) => {
+                        return this.error(__("unable to load extension: {0}", name), e);
+                    });
+                }
+                else {
+                    this.runExtensionAction(name, action);
+                }
+            }
+            /**
+             * Run an extension action from the command palette
+             *
+             * @private
+             * @param {string} name extension name
+             * @param {string} action action name
+             * @returns {void}
+             * @memberof AntEdit
+             */
+            runExtensionAction(name, action) {
+                if (!this.extensions[name]) {
+                    if (!Antedit.extensions[name]) {
+                        return this.error(__("Unable to find extension: {0}", name));
+                    }
+                    this.extensions[name] = new Antedit.extensions[name](this);
+                }
+                if (!this.extensions[name][action]) {
+                    return this.error(__("Unable to find action: {0}", action));
+                }
+                this.extensions[name].preload()
+                    .then(() => this.extensions[name][action]())
+                    .catch((e) => {
+                    return this.error(__("Unable to preload extension"), e);
+                });
+            }
+        }
+        application.Antedit = Antedit;
+        /**
+         * Helper class to manager several instances
+         * of editor models
+         *
+         * @class EditorModelManager
+         */
+        class EditorModelManager {
+            /**
+             * Creates an instance of EditorModelManager.
+             * @memberof EditorModelManager
+             */
+            constructor() {
+                this.active_editor = undefined;
+                this.models = [];
+            }
+            get editors() {
+                return this.models;
+            }
+            set contextmenuHandle(cb) {
+                for (let ed of this.models) {
+                    ed.contextmenuHandle = cb;
+                }
+            }
+            /**
+             * Get the active editor model
+             *
+             * @readonly
+             * @type {BaseEditorModel}
+             * @memberof EditorModelManager
+             */
+            get active() {
+                return this.active_editor;
+            }
+            /**
+             * Add a model to the manager
+             *
+             * @param {BaseEditorModel} model
+             * @memberof EditorModelManager
+             */
+            add(model) {
+                this.models.push(model);
+                if (!this.active_editor)
+                    this.active_editor = model;
+                model.on("focus", () => {
+                    this.active_editor = model;
+                });
+                return this;
+            }
+            addAction(extension, action, callback) {
+                const ed_action = {
+                    id: `${extension.name}:${action.name}`,
+                    label: `${extension.text.__()}: ${action.text.__()}`,
+                    /*
+                    keybindings: [
+                        monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10,
+                        // chord
+                        monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_K, monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_M)
+                    ]*/
+                    precondition: null,
+                    keybindingContext: null,
+                    contextMenuGroupId: extension.name,
+                    //contextMenuOrder: 1.5,
+                    run: () => callback(extension.name, action.name)
+                };
+                for (let ed of this.models) {
+                    const editor = ed.getEditor();
+                    if (!editor.getAction(ed_action.id))
+                        editor.addAction(ed_action);
+                }
+            }
+            set onstatuschange(cb) {
+                for (let ed of this.models) {
+                    ed.onstatuschange = cb;
+                }
+            }
+            dirties() {
+                let list = [];
+                for (let ed of this.models) {
+                    list = list.concat(ed.dirties());
+                }
+                return list;
+            }
+            /**
+             * Resize all editor
+             *
+             * @memberof EditorModelManager
+             */
+            resize() {
+                for (let ed of this.models) {
+                    ed.resize();
+                }
+            }
+        }
+        /**
+         * This class handles log output to the Editor output container
+         *
+         * @class Logger
+         */
+        class Logger {
+            /**
+             * Creates an instance of Logger.
+             * @param {HTMLElement} el target container
+             * @memberof Logger
+             */
+            constructor(el) {
+                this.target = el;
+            }
+            /**
+             * Log level info
+             *
+             * @param {string|FormattedString} s
+             * @memberof Logger
+             */
+            info(s) {
+                this.log("info", s, true);
+            }
+            /**
+             * Log level warning
+             *
+             * @param {string|FormattedString} s
+             * @memberof Logger
+             */
+            warn(s) {
+                this.log("warn", s, true);
+            }
+            /**
+             * Log level error
+             *
+             * @param {string|FormattedString} s
+             * @memberof Logger
+             */
+            error(s) {
+                this.log("error", s, true);
+            }
+            /**
+             * Log a string to target container
+             *
+             * @private
+             * @param {string} c class name of the appended log element
+             * @param {string|FormattedString} s log string
+             * @param {boolean} showtime define whether the logger should insert datetime prefix
+             * in the log string
+             * @memberof Logger
+             */
+            log(c, s, showtime) {
+                let el = $("
")
+                    .attr("class", `code-pad-log-${c}`);
+                if (showtime) {
+                    let date = new Date();
+                    let prefix = date.getDate() + "/"
+                        + (date.getMonth() + 1) + "/"
+                        + date.getFullYear() + " "
+                        + date.getHours() + ":"
+                        + date.getMinutes() + ":"
+                        + date.getSeconds();
+                    el.text(`[${prefix}]: ${s.__()}`);
+                }
+                else {
+                    el.text(s.__());
+                }
+                $(this.target).append(el);
+                $(this.target).scrollTop($(this.target)[0].scrollHeight);
+            }
+            /**
+             * Print a log message without prefix
+             *
+             * @param {string|FormattedString} s text to print
+             * @memberof Logger
+             */
+            print(s) {
+                this.log("info", s, false);
+            }
+            /**
+             * Empty the log container
+             *
+             * @memberof Logger
+             */
+            clear() {
+                $(this.target).empty();
+            }
+        }
+        Antedit.Logger = Logger;
+        Antedit.dependencies = [
+            "pkg://MonacoCore/bundle/app.bundle.js"
+        ];
+    })(application = OS.application || (OS.application = {}));
+})(OS || (OS = {}));
+
+var OS;
+(function (OS) {
+    /**
+     *
+     *
+     * @class EditorBaseExtension
+     */
+    class EditorBaseExtension {
+        constructor(name, app) {
+            this.app = app;
+            this.name = name;
+        }
+        /**
+         *
+         *
+         * @returns {Promise}
+         * @memberof EditorBaseExtension
+         */
+        preload() {
+            return OS.API.require(OS.application.Antedit.extensions[this.name].dependencies);
+        }
+        /**
+         *
+         *
+         * @protected
+         * @returns {string}
+         * @memberof EditorBaseExtension
+         */
+        basedir() {
+            return `${this.app.meta().path}/extensions/${this.name}`;
+        }
+        /**
+         *
+         *
+         * @protected
+         * @param {(string | FormattedString)} m
+         * @returns {void}
+         * @memberof EditorBaseExtension
+         */
+        notify(m) {
+            return this.app.notify(m);
+        }
+        /**
+         *
+         *
+         * @protected
+         * @param {(string | FormattedString)} m
+         * @param {Error} e
+         * @returns {void}
+         * @memberof EditorBaseExtension
+         */
+        error(m, e) {
+            return this.app.error(m, e);
+        }
+        /**
+         *
+         *
+         * @protected
+         * @return {AnteditLogger} editor logger
+         * @memberof EditorBaseExtension
+         */
+        logger() {
+            if (!this.app.setting.showBottomBar) {
+                this.app.showOutput(true);
+            }
+            else {
+                this.app.showOutput(false);
+            }
+            return this.app.logger;
+        }
+        /**
+         *
+         *
+         * @protected
+         * @param {string} file
+         * @returns {Promise>}
+         * @memberof EditorBaseExtension
+         */
+        metadata(file) {
+            return new Promise((resolve, reject) => {
+                if (!this.app.currdir) {
+                    return reject(OS.API.throwe(__("Current folder is not found")));
+                }
+                `${this.app.currdir.path}/${file}`
+                    .asFileHandle()
+                    .read("json")
+                    .then((data) => {
+                    if (!data.root && this.app.currdir) {
+                        data.root = this.app.currdir.path;
+                    }
+                    resolve(data);
+                })
+                    .catch((e) => {
+                    // try to ask user to select a folder
+                    this.app.openDialog("FileDialog", {
+                        title: __("Select build directory"),
+                        root: this.app.currdir.path,
+                        mimes: ["dir"]
+                    })
+                        .then((d) => {
+                        `${d.file.path}/${file}`
+                            .asFileHandle()
+                            .read("json")
+                            .then((data) => {
+                            if (!data.root) {
+                                data.root = d.file.path;
+                            }
+                            resolve(data);
+                        })
+                            .catch((e1) => reject(e1));
+                    })
+                        .catch((e1) => reject(OS.API.throwe(__("Unable to read meta-data"))));
+                });
+            });
+        }
+    }
+    EditorBaseExtension.dependencies = [];
+    OS.application.Antedit.extensions = {};
+    OS.application.Antedit.EditorBaseExtension = EditorBaseExtension;
+    class EditorExtensionMaker extends EditorBaseExtension {
+        constructor(app) {
+            super("EditorExtensionMaker", app);
+        }
+        create() {
+            this.logger().clear();
+            this.app
+                .openDialog("FileDialog", {
+                title: "__(New CodePad extension at)",
+                file: { basename: __("ExtensionName") },
+                mimes: ["dir"],
+            })
+                .then((d) => {
+                return this.mktpl(d.file.path, d.name);
+            });
+        }
+        build(callback) {
+            this.logger().clear();
+            this.metadata("extension.json")
+                .then(async (meta) => {
+                try {
+                    const jsrc = await OS.API.VFS.cat(meta.javascripts.map(v => `${meta.root}/${v}`), "");
+                    await `${meta.root}/build/debug/main.js`
+                        .asFileHandle()
+                        .setCache(jsrc)
+                        .write("text/plain");
+                    await `${meta.root}/build/debug/extension.json`
+                        .asFileHandle()
+                        .setCache(meta.meta)
+                        .write("object");
+                    await OS.API.VFS.copy(meta.copies.map(v => `${meta.root}/${v}`), `${meta.root}/build/debug`);
+                    this.logger().info(__("Files generated in {0}", `${meta.root}/build/debug`));
+                    if (callback)
+                        callback();
+                }
+                catch (e) {
+                    return this.logger().error(__("Unable to build extension:{0}", e.stack));
+                }
+            })
+                .catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack)));
+        }
+        run() {
+            this.logger().clear();
+            this.metadata("extension.json")
+                .then(async (meta) => {
+                if (!meta || !meta.meta || !meta.meta.name)
+                    return this.logger().error(__("Invalid extension meta-data"));
+                try {
+                    const path = `${meta.root}/build/debug/main.js`;
+                    if (OS.API.shared[path]) {
+                        delete OS.API.shared[path];
+                    }
+                    await OS.API.requires(path);
+                    if (this.app.extensions[meta.meta.name] && this.app.extensions[meta.meta.name].cleanup) {
+                        this.app.extensions[meta.meta.name].cleanup();
+                    }
+                    this.app.extensions[meta.meta.name] = new OS.application.Antedit.extensions[meta.meta.name](this.app);
+                    for (let v of meta.meta.actions) {
+                        this.app.eum.addAction(meta.meta, v, (e_name, a_name) => {
+                            this.app.loadAndRunExtensionAction(e_name, a_name, `${meta.root}/build`);
+                        });
+                    }
+                    this.app.eum.active.getEditor().trigger(meta.meta.name, 'editor.action.quickCommand');
+                }
+                catch (e) {
+                    return this.logger().error(__("Unable to run extension:{0}", e.stack));
+                }
+            })
+                .catch((e) => this.logger().error(__("Unable to read meta-data:{0}", e.stack)));
+        }
+        release() {
+            this.logger().clear();
+            this.metadata("extension.json")
+                .then(async (meta) => {
+                this.build(async () => {
+                    try {
+                        await OS.API.VFS.mkar(`${meta.root}/build/debug`, `${meta.root}/build/release/${meta.meta.name}.zip`);
+                        this.logger().info(__("Archive created at {0}", `${meta.root}/build/release/${meta.meta.name}.zip`));
+                    }
+                    catch (e) {
+                        return this.logger().error(__("Unable to create archive: {0}", e.stack));
+                    }
+                });
+            })
+                .catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack)));
+        }
+        install() {
+            this.logger().clear();
+            this.app
+                .openDialog("FileDialog", {
+                title: "__(Select extension archive)",
+                mimes: [".*/zip"],
+            })
+                .then(async (d) => {
+                try {
+                    await this.installZip(d.file.path);
+                    this.logger().info(__("Extension installed"));
+                    return this.app.loadExtensionMetaData();
+                }
+                catch (e) {
+                    return this.logger().error(__("Unable to install extension: {0}", e.stack));
+                }
+            });
+        }
+        installFromURL() {
+            this.logger().clear();
+            this.app
+                .openDialog("PromptDialog", {
+                title: __("Enter URI"),
+                label: __("Please enter extension URI:")
+            })
+                .then(async (v) => {
+                if (!v)
+                    return;
+                try {
+                    await this.installZip(v);
+                    this.logger().info(__("Extension installed"));
+                    return this.app.loadExtensionMetaData();
+                }
+                catch (e) {
+                    return this.app.error(__("Unable to install extension: {0}", v));
+                }
+            });
+        }
+        /**
+         *
+         *
+         * @private
+         * @param {string} path
+         * @param {string} name
+         * @memberof EditorExtensionMaker
+         */
+        mktpl(path, name) {
+            const rpath = `${path}/${name}`;
+            const dirs = [
+                rpath,
+                `${rpath}/build`,
+                `${rpath}/build/release`,
+                `${rpath}/build/debug`,
+            ];
+            const files = [
+                ["main.tpl", `${rpath}/${name}.js`],
+                ["meta.tpl", `${rpath}/extension.json`],
+            ];
+            OS.API.VFS.mkdirAll(dirs, true)
+                .then(async () => {
+                try {
+                    await OS.API.VFS.mktpl(files, this.basedir(), (data) => {
+                        return data.format(name, `${path}/${name}`);
+                    });
+                    this.app.currdir = rpath.asFileHandle();
+                    this.app.toggleSideBar();
+                    return this.app.eum.active.openFile(`${rpath}/${name}.js`.asFileHandle());
+                }
+                catch (e) {
+                    return this.logger().error(__("Unable to create extension template: {0}", e.stack));
+                }
+            })
+                .catch((e) => this.logger().error(__("Unable to create extension directories: {0}", e.stack)));
+        }
+        /**
+         *
+         *
+         * @private
+         * @param {string} path
+         * @returns {Promise}
+         * @memberof EditorExtensionMaker
+         */
+        installZip(path) {
+            return new Promise(async (resolve, reject) => {
+                try {
+                    await OS.API.requires("os://scripts/jszip.min.js");
+                    const data = await path.asFileHandle().read("binary");
+                    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));
+                    const pth = this.ext_dir(meta.name);
+                    const dir = [pth];
+                    const files = [];
+                    for (let name in zip.files) {
+                        const file = zip.files[name];
+                        if (file.dir) {
+                            dir.push(pth + "/" + name);
+                        }
+                        else if (name != "extension.json") {
+                            files.push(name);
+                        }
+                    }
+                    if (dir.length > 0) {
+                        await OS.API.VFS.mkdirAll(dir, true);
+                        await this.installFiles(files, zip, meta);
+                    }
+                    else {
+                        await this.installFiles(files, zip, meta);
+                    }
+                    resolve();
+                }
+                catch (e) {
+                    reject(__e(e));
+                }
+            });
+        }
+        ext_dir(en) {
+            return `${this.app.meta().path}/extensions/${en}`;
+        }
+        /**
+         *
+         *
+         * @private
+         * @param {string[]} files
+         * @param {*} zip
+         * @param {GenericObject} meta
+         * @returns {Promise}
+         * @memberof EditorExtensionMaker
+         */
+        installFiles(files, zip, meta) {
+            if (files.length === 0) {
+                return this.installMeta(meta);
+            }
+            return new Promise(async (resolve, reject) => {
+                try {
+                    const file = files.splice(0, 1)[0];
+                    const path = `${this.ext_dir(meta.name)}/${file}`;
+                    const d = await zip.file(file).async("uint8array");
+                    const r = await path.asFileHandle()
+                        .setCache(new Blob([d], { type: "octet/stream" }))
+                        .write("text/plain");
+                    if (r.error) {
+                        return reject(r.error);
+                    }
+                    await this.installFiles(files, zip, meta);
+                    resolve();
+                }
+                catch (e) {
+                    reject(__e(e));
+                }
+            });
+        }
+        /**
+         *
+         *
+         * @private
+         * @param {GenericObject} meta
+         * @returns {Promise}
+         * @memberof EditorExtensionMaker
+         */
+        installMeta(meta) {
+            return new Promise(async (resolve, reject) => {
+                const file = `${this.ext_dir("")}/extensions.json`.asFileHandle();
+                try {
+                    const data = await file.read("json");
+                    const names = [];
+                    for (let v of data) {
+                        names.push(v.name);
+                    }
+                    const idx = names.indexOf(meta.name);
+                    if (idx >= 0) {
+                        data.splice(idx, 1);
+                    }
+                    data.push(meta);
+                    try {
+                        await file.setCache(data).write("object");
+                        return resolve();
+                    }
+                    catch (e) {
+                        return reject(__e(e));
+                    }
+                }
+                catch (e_1) {
+                    // try to create new file
+                    try {
+                        await file.setCache([meta]).write("object");
+                        return resolve();
+                    }
+                    catch (e_2) {
+                        return reject(__e(e_2));
+                    }
+                }
+            });
+        }
+    }
+    OS.application.Antedit.extensions.EditorExtensionMaker = EditorExtensionMaker;
+})(OS || (OS = {}));
diff --git a/Antedit/build/release/Antedit.zip b/Antedit/build/release/Antedit.zip
index aba34e6..a0c928e 100644
Binary files a/Antedit/build/release/Antedit.zip and b/Antedit/build/release/Antedit.zip differ
diff --git a/Antedit/ts/BaseEditorModel.ts b/Antedit/ts/BaseEditorModel.ts
index 8e459ef..b164a27 100644
--- a/Antedit/ts/BaseEditorModel.ts
+++ b/Antedit/ts/BaseEditorModel.ts
@@ -27,7 +27,6 @@ namespace OS {
              * @type {boolean}
              */
             selected: boolean;
-
         };
 
         export abstract class BaseEditorModel {
diff --git a/Antedit/ts/EditorExtensionMaker.ts b/Antedit/ts/EditorExtensionMaker.ts
index 8b1ec6f..3c37ab0 100644
--- a/Antedit/ts/EditorExtensionMaker.ts
+++ b/Antedit/ts/EditorExtensionMaker.ts
@@ -307,7 +307,7 @@ namespace OS {
                         this.app.currdir = rpath.asFileHandle();
                         this.app.toggleSideBar();
                         return this.app.eum.active.openFile(
-                            `${rpath}/${name}.js`.asFileHandle() as application.EditorFileHandle
+                            `${rpath}/${name}.js`.asFileHandle() as OS.application.EditorFileHandle
                         );
                     } catch (e) {
                         return this.logger().error(
diff --git a/Antedit/ts/MonacoEditorModel.ts b/Antedit/ts/MonacoEditorModel.ts
index b7e11ba..dc97a43 100644
--- a/Antedit/ts/MonacoEditorModel.ts
+++ b/Antedit/ts/MonacoEditorModel.ts
@@ -1,4 +1,5 @@
 namespace OS {
+    declare var monaco;
     export namespace application {
         /**
          * Wrapper model for the ACE text editor
@@ -7,7 +8,7 @@ namespace OS {
          * @class MonacoEditorModel
          * @extends {BaseEditorModel}
          */
-        export class MonacoEditorModel extends BaseEditorModel {
+        export class MonacoEditorModel extends OS.application.BaseEditorModel {
             
             static modes: GenericObject;
 
@@ -61,7 +62,7 @@ namespace OS {
                 if(model.position)
                 {
                     this.editor.setPosition(model.position);
-                    this.editor.revealLine(model.position.lineNumber);
+                    this.editor.revealLineInCenter(model.position.lineNumber);
                 }
             }
 
diff --git a/Antedit/ts/main.ts b/Antedit/ts/main.ts
index 988a043..0a93e02 100644
--- a/Antedit/ts/main.ts
+++ b/Antedit/ts/main.ts
@@ -204,11 +204,11 @@ namespace OS {
                 monaco.editor.setTheme("vs-dark");
                 // add editor instance
                 this.eum
-                    .add(new MonacoEditorModel(
+                    .add(new OS.application.MonacoEditorModel(
                         this,
                         this.find("left-tabbar") as GUI.tag.TabBarTag,
                         this.find("left-editorarea")) as BaseEditorModel)
-                    .add(new MonacoEditorModel(
+                    .add(new OS.application.MonacoEditorModel(
                         this,
                         this.find("right-tabbar") as GUI.tag.TabBarTag,
                         this.find("right-editorarea")) as BaseEditorModel);
@@ -919,7 +919,7 @@ namespace OS {
                 this.models = [];
             }
 
-            get editors(): BaseEditorModel[] {
+            get editors(): OS.application.BaseEditorModel[] {
                 return this.models;
             }
             set contextmenuHandle(cb: (e: any, m: any) => void) {