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) {