diff --git a/Blogger/README.md b/Blogger/README.md
index a0f637b..d39795f 100644
--- a/Blogger/README.md
+++ b/Blogger/README.md
@@ -6,6 +6,7 @@ Blackend for my blog at https://blog.iohub.dev
## Change logs
### v0.2.x-a
+* Patch 13: fix bug on blog save
* Patch 12: support send mail via SSL
* Patch 11: Add TFIDF analyse functionality
* Patch 10: Migrate code to typescript, use SQLiteDB lib for database access
diff --git a/Blogger/build/debug/README.md b/Blogger/build/debug/README.md
index a0f637b..d39795f 100644
--- a/Blogger/build/debug/README.md
+++ b/Blogger/build/debug/README.md
@@ -6,6 +6,7 @@ Blackend for my blog at https://blog.iohub.dev
## Change logs
### v0.2.x-a
+* Patch 13: fix bug on blog save
* Patch 12: support send mail via SSL
* Patch 11: Add TFIDF analyse functionality
* Patch 10: Migrate code to typescript, use SQLiteDB lib for database access
diff --git a/Blogger/build/debug/main.js b/Blogger/build/debug/main.js
index 0d4b35d..01501bc 100644
--- a/Blogger/build/debug/main.js
+++ b/Blogger/build/debug/main.js
@@ -1 +1 @@
-var OS;!function(t){let e;!function(t){class e extends t.BaseApplication{constructor(t){super("Blogger",t),this.previewOn=!1}async init_db(){try{const e=await this.openDialog("FileDialog",{title:__("Open/create new database"),file:"Untitled.db"});var t=e.file.path.asFileHandle();"file"===e.file.type&&(t=t.parent());const i=`${t.path}/${e.name}`.asFileHandle();this.dbhandle=("sqlite://"+i.genealogy.join("/")).asFileHandle();const a=await this.dbhandle.read();if(!a.user){this.dbhandle.cache={address:"TEXT",Phone:"TEXT",shortbiblio:"TEXT",fullname:"TEXT",email:"TEXT",url:"TEXT",photo:"TEXT"};const t=await this.dbhandle.write("user");if(t.error)throw new Error(t.error)}if(!a.cv_cat){this.dbhandle.cache={publish:"NUMERIC",name:"TEXT",pid:"NUMERIC"};const t=await this.dbhandle.write("cv_cat");if(t.error)throw new Error(t.error)}if(!a.cv_sections){this.dbhandle.cache={title:"TEXT",start:"NUMERIC",location:"TEXT",end:"NUMERIC",content:"TEXT",subtitle:"TEXT",publish:"NUMERIC",cid:"NUMERIC"};const t=await this.dbhandle.write("cv_sections");if(t.error)throw new Error(t.error)}if(!a.blogs){this.dbhandle.cache={tags:"TEXT",content:"TEXT",utime:"NUMERIC",rendered:"TEXT",title:"TEXT",utimestr:"TEXT",ctime:"NUMERIC",ctimestr:"TEXT",publish:"INTEGER DEFAULT 0"};const t=await this.dbhandle.write("blogs");if(t.error)throw new Error(t.error)}if(!a.st_similarity){this.dbhandle.cache={pid:"NUMERIC",sid:"NUMERIC",score:"NUMERIC"};const t=await this.dbhandle.write("st_similarity");if(t.error)throw new Error(t.error)}if(!a.subscribers){this.dbhandle.cache={name:"TEXT",email:"TEXT"};const t=await this.dbhandle.write("subscribers");if(t.error)throw new Error(t.error)}this.userdb=(this.dbhandle.path+"@user").asFileHandle(),this.cvcatdb=(this.dbhandle.path+"@cv_cat").asFileHandle(),this.cvsecdb=(this.dbhandle.path+"@cv_sections").asFileHandle(),this.blogdb=(this.dbhandle.path+"@blogs").asFileHandle(),this.subdb=(this.dbhandle.path+"@subscribers").asFileHandle(),this.last_ctime=0,this.bloglist.data=[],this.loadBlogs()}catch(t){this.error(__("Unable to init database file: {0}",t.toString()),t),this.dbhandle=void 0}}menu(){return[{text:"__(Open/Create database)",onmenuselect:t=>{this.init_db()}}]}main(){this.user={},this.cvlist=this.find("cv-list"),this.cvlist.ontreeselect=t=>{if(!t)return;const{data:e}=t.data.item;return this.CVSectionByCID(Number(e.id))},this.inputtags=this.find("input-tags"),this.bloglist=this.find("blog-list"),this.seclist=this.find("cv-sec-list");let e=this.find("photo");return $(e).on("click",async t=>{try{const t=await this.openDialog("FileDialog",{title:__("Select image file"),mimes:["image/.*"]});return e.value=t.file.path}catch(t){return this.error(__("Unable to get file"),t)}}),this.tabcontainer=this.find("tabcontainer"),this.tabcontainer.ontabselect=t=>this.fetchData(t.data.container.aid),this.find("bt-user-save").onbtclick=t=>this.saveUser(),this.find("blog-load-more").onbtclick=t=>{this.loadBlogs()},this.find("cv-cat-add").onbtclick=async e=>{try{const e=await this.fetchCVCat(),i=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Add category"),tree:e});this.cvcatdb.cache={name:i.value,pid:i.p.id,publish:1};const a=await this.cvcatdb.write(void 0);if(a.error)throw new Error(a.error);await this.refreshCVCat()}catch(e){this.error(__("cv-cat-add: {0}",e.toString()),e)}},this.find("cv-cat-edit").onbtclick=async e=>{try{const e=this.cvlist.selectedItem;if(!e)return;const i=e.data;if(!i)return;const a=await this.fetchCVCat(),s=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Edit category"),tree:a,cat:i}),n=i.$vfs;n.cache={id:i.id,publish:i.publish,pid:s.p.id,name:s.value};const r=await n.write(void 0);if(r.error)throw new Error(r.error);await this.refreshCVCat()}catch(e){this.error(__("cv-cat-edit: {0}",e.toString()),e)}},this.find("cv-cat-del").onbtclick=async t=>{try{const t=this.cvlist.selectedItem;if(!t)return;const e=t.data;if(!e)return;if(!await this.openDialog("YesNoDialog",{title:__("Delete category"),iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",e.name)}))return;await this.deleteCVCat(e)}catch(t){this.error(__("cv-cat-del: {0}",t.toString()),t)}},this.find("cv-sec-add").onbtclick=async e=>{try{const e=this.cvlist.selectedItem;if(!e)return;const i=e.data;if(!i||"0"===i.id)return this.toast(__("Please select a category"));const a=await this.openDialog(new t.blogger.BloggerCVSectionDiaglog,{title:__("New section entry for {0}",i.name)});a.cid=Number(i.id),a.start=Number(a.start),a.end=Number(a.end),this.cvsecdb.cache=a;const s=await this.cvsecdb.write(void 0);if(s.error)throw new Error(s.error);await this.CVSectionByCID(Number(i.id))}catch(e){this.error(__("cv-sec-add: {0}",e.toString()),e)}},this.find("cv-sec-move").onbtclick=async e=>{try{const e=this.seclist.selectedItem;if(!e)return this.toast(__("Please select a section to move"));const i=e.data,a=i.$vfs;console.log(a);const s=await this.fetchCVCat(),n=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Move to"),tree:s,selonly:!0});a.cache={id:i.id,cid:n.p.id};const r=await a.write(void 0);if(r.error)throw new Error(r.error);await this.CVSectionByCID(i.cid),this.seclist.unselect()}catch(e){this.error(__("cv-sec-move: {0}",e.toString()),e)}},this.find("cv-sec-edit").onbtclick=async e=>{try{const e=this.seclist.selectedItem;if(!e)return this.toast(__("Please select a section to edit"));const i=e.data,a=await this.openDialog(new t.blogger.BloggerCVSectionDiaglog,{title:__("Modify section entry"),section:i});a.cid=Number(i.cid),a.start=Number(a.start),a.end=Number(a.end);const s=i.$vfs;s.cache=a;const n=await s.write(void 0);if(n.error)throw new Error(n.error);await this.CVSectionByCID(Number(i.cid))}catch(e){this.error(__("cv-sec-edit: {0}",e.toString()),e)}},this.seclist.onitemclose=t=>{if(!t)return;const e=t.data.item.data;return this.openDialog("YesNoDialog",{iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",e.title)}).then(async i=>{if(i)try{const i=await this.cvsecdb.remove({where:{id:e.id}});if(i.error)throw new Error(i.error);return this.seclist.delete(t.data.item)}catch(t){return this.error(__("Cannot delete the section: {0}",t.toString()),t)}}),!1},this.editor=new EasyMDE({element:this.find("markarea"),autoDownloadFontAwesome:!1,autofocus:!0,tabSize:4,indentWithTabs:!0,toolbar:[{name:__("New"),className:"fa fa-file",action:t=>(this.bloglist.unselect(),this.clearEditor())},{name:__("Save"),className:"fa fa-save",action:t=>this.saveBlog()},"|","bold","italic","heading","|","quote","code","unordered-list","ordered-list","|","link","image","table","horizontal-rule",{name:"image",className:"fa fa-file-image-o",action:t=>this.openDialog("FileDialog",{title:__("Select image file"),mimes:["image/.*"]}).then(t=>t.file.path.asFileHandle().publish().then(t=>this.editor.codemirror.getDoc().replaceSelection(`![](${this._api.handle.shared}/${t.result})`)).catch(t=>this.error(__("Cannot export file for embedding to text"),t)))},{name:"Youtube",className:"fa fa-youtube",action:t=>this.editor.codemirror.getDoc().replaceSelection("[[youtube:]]")},"|",{name:__("Preview"),className:"fa fa-eye no-disable",action:t=>{this.previewOn=!this.previewOn,EasyMDE.togglePreview(t),renderMathInElement(this.find("editor-container"))}},"|",{name:__("Send mail"),className:"fa fa-paper-plane",action:async e=>{try{const e=await this.subdb.read(),i=this.bloglist.selectedItem;if(!i)return this.error(__("No post selected"));const a=i.data;await this.openDialog(new t.blogger.BloggerSendmailDiaglog,{title:__("Send mail"),content:this.editor.value(),mails:e,id:a.id}),this.toast(__("Emails sent"))}catch(e){this.error(__("Error sending mails: {0}",e.toString()),e)}}},"|",{name:__("TFIDF analyse"),className:"fa fa-area-chart",action:async t=>{try{const t=await this.openDialog("PromptDialog",{title:__("TFIDF Analyse"),text:__("Max number of related posts to keep per post?"),value:"5"}),e={path:this.meta().path+"/api/ai/analyse.lua",parameters:{dbpath:this.dbhandle.info.file.path,top:parseInt(t)}},i=await this._api.apigateway(e,!1);if(i.error)throw new Error(i.error);this.toast(i.result)}catch(t){this.error(__("Error analysing posts: {0}",t.toString()),t)}}}]}),this.bloglist.onlistselect=async t=>{const e=this.bloglist.selectedItem;if(!e)return;const i=e.data;if(i)try{const t=await this.blogdb.read({where:{id:Number(i.id)}});if(!t||0==t.length)throw new Error(__("No record found for ID {}",i.id).__());const e=t[0];return this.editor.value(e.content),this.inputtags.value=e.tags,this.find("blog-publish").swon=!!Number(e.publish)}catch(t){return this.error(__("Cannot fetch the entry content"),t)}},this.bloglist.onitemclose=t=>{if(!t)return;const e=t.data.item,i=e.data;return this.openDialog("YesNoDialog",{title:__("Delete a post"),iconclass:"fa fa-question-circle",text:__("Do you really want to delete this post ?")}).then(async t=>{if(!t)return;const a=await this.blogdb.remove({where:{id:Number(i.id)}});if(a.error)throw new Error(a.error);return this.bloglist.delete(e),this.bloglist.unselect(),this.clearEditor()}),!1},this.bindKey("CTRL-S",()=>{const t=this.tabcontainer.selectedTab;if(t&&"blog-container"===t.container.aid)return this.saveBlog()}),this.on("resize",()=>this.resizeContent()),this.resizeContent(),this.init_db()}fetchData(t){switch(t){case"user-container":return this.userdb.read().then(t=>{if(t&&0!=t.length)return this.user=t[0],this.select("[input-class='user-input']").map((t,e)=>$(e).val(this.user[e.name]))}).catch(t=>this.error(__("Cannot fetch user data"),t));case"cv-container":return this.refreshCVCat();default:return this.last_ctime=0,this.bloglist.data=[],this.loadBlogs()}}async saveUser(){try{const t=this.select("[input-class='user-input']");for(let e of t)this.user[e.name]=$(e).val();if(!this.user.fullname||""===this.user.fullname)return this.toast(__("Full name must be entered"));let e=this.userdb;this.user&&this.user.id&&(e=`${this.userdb.path}@${this.user.id}`.asFileHandle()),e.cache=this.user;const i=await e.write(void 0);if(i.error)throw new Error(i.error);this.user.id||(this.user.id=i.result),this.toast(__("User data updated"))}catch(t){this.error(__("Cannot save user data: {0}",t.toString()),t)}}refreshCVCat(){return this.fetchCVCat().then(t=>(this.cvlist.data=t,this.cvlist.expandAll())).catch(t=>this.error(__("Unable to load categories"),t))}fetchCVCat(){return new Promise(async(t,e)=>{try{const e={text:"Porfolio",id:0,nodes:[]},i={order:["name$asc"]},a=await this.cvcatdb.read(i);this.catListToTree(a,e,0),t(e)}catch(t){e(__e(t))}})}catListToTree(t,e,i){const a=t.filter(t=>t.pid==i);if(0===a.length)return e.nodes=null;for(let i of a)i.nodes=[],i.text=i.name,this.catListToTree(t,i,i.id),e.nodes.push(i)}deleteCVCat(t){return new Promise(async(e,i)=>{try{const e=[];var a=function(t){e.push(t.id),t.nodes&&t.nodes.map(t=>a(t))};a(t);let i=await this.cvsecdb.remove({where:{$or:{cid:e}}});if(i.error)throw new Error(i.error);if(i=await this.cvcatdb.remove({where:{$or:{id:e}}}),i.error)throw new Error(i.error);await this.refreshCVCat(),this.seclist.data=[]}catch(t){i(__e(t))}})}CVSectionByCID(t){return new Promise(async(e,i)=>{try{const e=await this.cvsecdb.read({where:{cid:t},order:["start$desc"]}),i=[];this.find("cv-sec-status").text=__("Found {0} sections",e.length);for(let t of e)t.closable=!0,t.tag="afx-blogger-cvsection-item",t.start=Number(t.start),t.end=Number(t.end),t.start<1e3&&(t.start=void 0),t.end<1e3&&(t.end=void 0),i.push(t);this.seclist.data=i}catch(t){i(__e(t))}})}async saveBlog(){try{let t=void 0;const e=this.bloglist.selectedItem;e&&(t=e.data);const i=this.inputtags.value,a=this.editor.value(),s=new RegExp("^#+(.*)\n","g").exec(a);if(!s||2!==s.length)return this.toast(__("Please insert a title in the text: beginning with heading"));if(""===i)return this.toast(__("Please enter tags"));const n=new Date,r={content:a,title:s[1].trim(),tags:i,ctime:t?t.ctime:n.timestamp(),ctimestr:t?t.ctimestr:n.toString(),utime:n.timestamp(),utimestr:n.toString(),rendered:this.process(this.editor.options.previewRender(a)),publish:this.find("blog-publish").swon?1:0};let o=this.blogdb;t&&(r.id=t.id,o=t.$vfs),o.cache=r;const l=await o.write(void 0);if(l.error)throw new Error(l.error);t?e.data=r:(this.last_ctime=0,this.bloglist.data=[],await this.loadBlogs())}catch(t){this.error(__("Cannot save blog: {0}",t.toString()),t)}}process(t){let e;const i=/\[\[youtube:([^\]]*)\]\]/g,a=[];for(;null!==(e=i.exec(t));)a.push(e);if(!(a.length>0))return t;let s="",n=0;for(let e of a)s+=t.substring(n,e.index),s+=``,n=e.index+e[0].length;return s+=t.substring(n,t.length),s}clearEditor(){return this.editor.value(""),this.inputtags.value="",this.find("blog-publish").swon=!1}loadBlogs(){return new Promise(async(t,e)=>{try{const t={order:["ctime$desc"],fields:["id","title","ctimestr","ctime","utime","utimestr"],limit:10};this.last_ctime&&(t.where={ctime$lt:this.last_ctime});const e=await this.blogdb.read(t);if(0==e.length)return void this.toast(__("No more record to load"));this.last_ctime=e[e.length-1].ctime;for(let t of e)t.tag="afx-blogger-post-item",this.bloglist.push(t);return this.clearEditor(),this.bloglist.selected=-1}catch(t){e(__e(t))}})}resizeContent(){const t=this.find("editor-container"),e=$(".EasyMDEContainer",t).children(),i=$(this.scheme).find(".afx-window-top")[0],a=e[0],s=e[3],n=$(this.scheme).height()-$(i).height()-$(a).height()-$(s).height()-90;return $(e[1]).css("height",n+"px")}}t.Blogger=e,e.singleton=!0,e.dependencies=["pkg://SimpleMDE/main.js","pkg://SimpleMDE/main.css","pkg://Katex/main.js","pkg://Katex/main.css","pkg://SQLiteDB/libsqlite.js"]}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){let i;!function(e){class i extends t.GUI.BasicDialog{constructor(){super("BloggerCategoryDialog",i.scheme)}main(){if(super.main(),this.tree=this.find("tree"),this.txtinput=this.find("txtinput"),this.find("bt-ok").onbtclick=t=>{const e=this.tree.selectedItem;if(!e)return this.notify(__("Please select a parent category"));const i=e.data,a=this.txtinput.value;return""!==a||this.data.selonly?this.data.cat&&this.data.cat.id===i.id?this.notify(__("Parent can not be the category itself")):(this.handle&&this.handle({p:i,value:a}),this.quit()):this.notify(__("Please enter category name"))},this.find("bt-cancel").onbtclick=t=>this.quit(),this.data&&this.data.tree){if(this.data&&this.data.cat){let t;this.txtinput.value=this.data.cat.name,t="0"===this.data.cat.pid?this.data.tree:this.findDataByID(this.data.cat.pid,this.data.tree.nodes),t&&(t.selected=!0)}return this.tree.data=this.data.tree,this.tree.expandAll()}}findDataByID(t,e){for(let i of e){if(i.id===t)return i;i.nodes&&this.findDataByID(t,i.nodes)}}}e.BloggerCategoryDialog=i,i.scheme='\n \n \n \n \n \n \n
\n \n \n
\n \n \n ';class a extends t.GUI.BasicDialog{constructor(){super("BloggerCVSectionDiaglog")}main(){super.main(),this.editor=new EasyMDE({autoDownloadFontAwesome:!1,element:this.find("contentarea"),status:!1,toolbar:!1}),$(this.select('[class = "CodeMirror-scroll"]')[0]).css("min-height","50px"),$(this.select('[class="CodeMirror cm-s-paper CodeMirror-wrap"]')[0]).css("min-height","50px");const t=this.select("[input-class='user-input']");if(this.data&&this.data.section)for(let e of t)$(e).val(this.data.section[e.name]);return this.data&&this.data.section&&this.editor.value(this.data.section.content),this.find("section-publish").swon=!!(this.data&&this.data.section&&Number(this.data.section.publish)),this.find("bt-cv-sec-save").onbtclick=e=>{const i={};for(let e of t)i[e.name]=$(e).val();if(i.content=this.editor.value(),""===i.title&&""===i.content)return this.notify(__("Title or content must not be blank"));this.data&&this.data.section&&(i.id=this.data.section.id);const a=this.find("section-publish").swon;return i.publish=!0===a?1:0,this.handle&&this.handle(i),this.quit()},this.on("resize",()=>this.resizeContent()),this.resizeContent()}resizeContent(){const t=this.find("editor-container"),e=$(".EasyMDEContainer",t).children(),i=$(t).height()-30;return $(e[0]).css("height",i+"px")}}e.BloggerCVSectionDiaglog=a,a.scheme='\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n
\n \n';class s extends t.GUI.BasicDialog{constructor(){super("BloggerSendmailDiaglog")}main(){super.main(),this.maillinglist=this.find("email-list");const t=new RegExp("^#+(.*)\n","g").exec(this.data.content);this.find("mail-title").value=t[1];const e=this.data.content.substring(0,500)+"...";this.find("contentarea").value=s.template.format(this.data.id,e);const i=this.data.mails.map(t=>({text:t.name,email:t.email,switch:!0,checked:!0}));return console.log(i),this.maillinglist.items=i,this.find("bt-sendmail").onbtclick=t=>{const e=this.maillinglist.items,i=[];for(let t of e)!0===t.checked&&i.push(t);if(0===i.length)return this.notify(__("No email selected"));const a={path:this.meta().path+"/api/sendmail.lua",parameters:{to:i,title:this.find("mail-title").value,content:this.find("contentarea").value,user:this.find("mail-user").value,password:this.find("mail-password").value}};return this._api.apigateway(a,!1).then(t=>{if(t.error){const e=t.result.join(",");return this.notify(__("Unable to send mail to: {0}",e))}return this.quit()}).catch(t=>this.error(__("Error sending mail: {0}",t.toString()),t))}}}e.BloggerSendmailDiaglog=s,s.scheme='\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n',s.template="Hello,\n\nDany LE has just published a new post on his blog: https://blog.iohub.dev/post/id/{0}\n\n==========\n{1}\n==========\n\n\nRead the full article via:\nhttps://blog.iohub.dev/post/id/{0}\n\nYou receive this email because you have been subscribed to his blog.\n\nHave a nice day,\n\nSent from Blogger, an AntOS application"}(i=e.blogger||(e.blogger={}))}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){let i;!function(e){class i extends t.GUI.tag.ListViewItemTag{constructor(){super()}ondatachange(){if(!this.data)return;const t=this.data,e=["content","start","end"];return this.closable=t.closable,(()=>{const i=[];for(let a in this.refs){const s=this.refs[a];t[a]&&""!==t[a]?e.includes(a)?i.push($(s).text(t[a])):i.push(s.text=t[a]):i.push(void 0)}return i})()}reload(){}init(){}itemlayout(){return{el:"div",children:[{el:"afx-label",ref:"title",class:"afx-cv-sec-title"},{el:"afx-label",ref:"subtitle",class:"afx-cv-sec-subtitle"},{el:"p",ref:"content",class:"afx-cv-sec-content"},{el:"p",class:"afx-cv-sec-period",children:[{el:"i",ref:"start"},{el:"i",ref:"end",class:"period-end"}]},{el:"afx-label",ref:"location",class:"afx-cv-sec-loc"}]}}}t.GUI.tag.define("afx-blogger-cvsection-item",i);class a extends t.GUI.tag.ListViewItemTag{constructor(){super()}ondatachange(){if(!this.data)return;const t=this.data;t.closable=!0,this.closable=t.closable,this.refs.title.text=t.title,this.refs.ctimestr.text=__("Created: {0}",t.ctimestr),this.refs.utimestr.text=__("Updated: {0}",t.utimestr)}reload(){}init(){}itemlayout(){return{el:"div",children:[{el:"afx-label",ref:"title",class:"afx-blogpost-title"},{el:"afx-label",ref:"ctimestr",class:"blog-dates"},{el:"afx-label",ref:"utimestr",class:"blog-dates"}]}}}t.GUI.tag.define("afx-blogger-post-item",a)}(i=e.blogger||(e.blogger={}))}(e=t.application||(t.application={}))}(OS||(OS={}));
\ No newline at end of file
+var OS;!function(t){let e;!function(t){class e extends t.BaseApplication{constructor(t){super("Blogger",t),this.previewOn=!1}async init_db(){try{const e=await this.openDialog("FileDialog",{title:__("Open/create new database"),file:"Untitled.db"});var t=e.file.path.asFileHandle();"file"===e.file.type&&(t=t.parent());const i=`${t.path}/${e.name}`.asFileHandle();this.dbhandle=("sqlite://"+i.genealogy.join("/")).asFileHandle();const a=await this.dbhandle.read();if(!a.user){this.dbhandle.cache={address:"TEXT",Phone:"TEXT",shortbiblio:"TEXT",fullname:"TEXT",email:"TEXT",url:"TEXT",photo:"TEXT"};const t=await this.dbhandle.write("user");if(t.error)throw new Error(t.error)}if(!a.cv_cat){this.dbhandle.cache={publish:"NUMERIC",name:"TEXT",pid:"NUMERIC"};const t=await this.dbhandle.write("cv_cat");if(t.error)throw new Error(t.error)}if(!a.cv_sections){this.dbhandle.cache={title:"TEXT",start:"NUMERIC",location:"TEXT",end:"NUMERIC",content:"TEXT",subtitle:"TEXT",publish:"NUMERIC",cid:"NUMERIC"};const t=await this.dbhandle.write("cv_sections");if(t.error)throw new Error(t.error)}if(!a.blogs){this.dbhandle.cache={tags:"TEXT",content:"TEXT",utime:"NUMERIC",rendered:"TEXT",title:"TEXT",utimestr:"TEXT",ctime:"NUMERIC",ctimestr:"TEXT",publish:"INTEGER DEFAULT 0"};const t=await this.dbhandle.write("blogs");if(t.error)throw new Error(t.error)}if(!a.st_similarity){this.dbhandle.cache={pid:"NUMERIC",sid:"NUMERIC",score:"NUMERIC"};const t=await this.dbhandle.write("st_similarity");if(t.error)throw new Error(t.error)}if(!a.subscribers){this.dbhandle.cache={name:"TEXT",email:"TEXT"};const t=await this.dbhandle.write("subscribers");if(t.error)throw new Error(t.error)}this.userdb=(this.dbhandle.path+"@user").asFileHandle(),this.cvcatdb=(this.dbhandle.path+"@cv_cat").asFileHandle(),this.cvsecdb=(this.dbhandle.path+"@cv_sections").asFileHandle(),this.blogdb=(this.dbhandle.path+"@blogs").asFileHandle(),this.subdb=(this.dbhandle.path+"@subscribers").asFileHandle(),this.last_ctime=0,this.bloglist.data=[],this.loadBlogs()}catch(t){this.error(__("Unable to init database file: {0}",t.toString()),t),this.dbhandle=void 0}}menu(){return[{text:"__(Open/Create database)",onmenuselect:t=>{this.init_db()}}]}main(){this.user={},this.cvlist=this.find("cv-list"),this.cvlist.ontreeselect=t=>{if(!t)return;const{data:e}=t.data.item;return this.CVSectionByCID(Number(e.id))},this.inputtags=this.find("input-tags"),this.bloglist=this.find("blog-list"),this.seclist=this.find("cv-sec-list");let e=this.find("photo");return $(e).on("click",async t=>{try{const t=await this.openDialog("FileDialog",{title:__("Select image file"),mimes:["image/.*"]});return e.value=t.file.path}catch(t){return this.error(__("Unable to get file"),t)}}),this.tabcontainer=this.find("tabcontainer"),this.tabcontainer.ontabselect=t=>this.fetchData(t.data.container.aid),this.find("bt-user-save").onbtclick=t=>this.saveUser(),this.find("blog-load-more").onbtclick=t=>{this.loadBlogs()},this.find("cv-cat-add").onbtclick=async e=>{try{const e=await this.fetchCVCat(),i=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Add category"),tree:e});this.cvcatdb.cache={name:i.value,pid:i.p.id,publish:1};const a=await this.cvcatdb.write(void 0);if(a.error)throw new Error(a.error);await this.refreshCVCat()}catch(e){this.error(__("cv-cat-add: {0}",e.toString()),e)}},this.find("cv-cat-edit").onbtclick=async e=>{try{const e=this.cvlist.selectedItem;if(!e)return;const i=e.data;if(!i)return;const a=await this.fetchCVCat(),s=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Edit category"),tree:a,cat:i}),n=i.$vfs;n.cache={id:i.id,publish:i.publish,pid:s.p.id,name:s.value};const r=await n.write(void 0);if(r.error)throw new Error(r.error);await this.refreshCVCat()}catch(e){this.error(__("cv-cat-edit: {0}",e.toString()),e)}},this.find("cv-cat-del").onbtclick=async t=>{try{const t=this.cvlist.selectedItem;if(!t)return;const e=t.data;if(!e)return;if(!await this.openDialog("YesNoDialog",{title:__("Delete category"),iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",e.name)}))return;await this.deleteCVCat(e)}catch(t){this.error(__("cv-cat-del: {0}",t.toString()),t)}},this.find("cv-sec-add").onbtclick=async e=>{try{const e=this.cvlist.selectedItem;if(!e)return;const i=e.data;if(!i||"0"===i.id)return this.toast(__("Please select a category"));const a=await this.openDialog(new t.blogger.BloggerCVSectionDiaglog,{title:__("New section entry for {0}",i.name)});a.cid=Number(i.id),a.start=Number(a.start),a.end=Number(a.end),this.cvsecdb.cache=a;const s=await this.cvsecdb.write(void 0);if(s.error)throw new Error(s.error);await this.CVSectionByCID(Number(i.id))}catch(e){this.error(__("cv-sec-add: {0}",e.toString()),e)}},this.find("cv-sec-move").onbtclick=async e=>{try{const e=this.seclist.selectedItem;if(!e)return this.toast(__("Please select a section to move"));const i=e.data,a=i.$vfs;console.log(a);const s=await this.fetchCVCat(),n=await this.openDialog(new t.blogger.BloggerCategoryDialog,{title:__("Move to"),tree:s,selonly:!0});a.cache={id:i.id,cid:n.p.id};const r=await a.write(void 0);if(r.error)throw new Error(r.error);await this.CVSectionByCID(i.cid),this.seclist.unselect()}catch(e){this.error(__("cv-sec-move: {0}",e.toString()),e)}},this.find("cv-sec-edit").onbtclick=async e=>{try{const e=this.seclist.selectedItem;if(!e)return this.toast(__("Please select a section to edit"));const i=e.data,a=await this.openDialog(new t.blogger.BloggerCVSectionDiaglog,{title:__("Modify section entry"),section:i});a.cid=Number(i.cid),a.start=Number(a.start),a.end=Number(a.end);const s=i.$vfs;s.cache=a;const n=await s.write(void 0);if(n.error)throw new Error(n.error);await this.CVSectionByCID(Number(i.cid))}catch(e){this.error(__("cv-sec-edit: {0}",e.toString()),e)}},this.seclist.onitemclose=t=>{if(!t)return;const e=t.data.item.data;return this.openDialog("YesNoDialog",{iconclass:"fa fa-question-circle",text:__("Do you really want to delete: {0}?",e.title)}).then(async i=>{if(i)try{const i=await this.cvsecdb.remove({where:{id:e.id}});if(i.error)throw new Error(i.error);return this.seclist.delete(t.data.item)}catch(t){return this.error(__("Cannot delete the section: {0}",t.toString()),t)}}),!1},this.editor=new EasyMDE({element:this.find("markarea"),autoDownloadFontAwesome:!1,autofocus:!0,tabSize:4,indentWithTabs:!0,toolbar:[{name:__("New"),className:"fa fa-file",action:t=>(this.bloglist.unselect(),this.clearEditor())},{name:__("Save"),className:"fa fa-save",action:t=>this.saveBlog()},"|","bold","italic","heading","|","quote","code","unordered-list","ordered-list","|","link","image","table","horizontal-rule",{name:"image",className:"fa fa-file-image-o",action:t=>this.openDialog("FileDialog",{title:__("Select image file"),mimes:["image/.*"]}).then(t=>t.file.path.asFileHandle().publish().then(t=>this.editor.codemirror.getDoc().replaceSelection(`![](${this._api.handle.shared}/${t.result})`)).catch(t=>this.error(__("Cannot export file for embedding to text"),t)))},{name:"Youtube",className:"fa fa-youtube",action:t=>this.editor.codemirror.getDoc().replaceSelection("[[youtube:]]")},"|",{name:__("Preview"),className:"fa fa-eye no-disable",action:t=>{this.previewOn=!this.previewOn,EasyMDE.togglePreview(t),renderMathInElement(this.find("editor-container"))}},"|",{name:__("Send mail"),className:"fa fa-paper-plane",action:async e=>{try{const e=await this.subdb.read(),i=this.bloglist.selectedItem;if(!i)return this.error(__("No post selected"));const a=i.data;await this.openDialog(new t.blogger.BloggerSendmailDiaglog,{title:__("Send mail"),content:this.editor.value(),mails:e,id:a.id}),this.toast(__("Emails sent"))}catch(e){this.error(__("Error sending mails: {0}",e.toString()),e)}}},"|",{name:__("TFIDF analyse"),className:"fa fa-area-chart",action:async t=>{try{const t=await this.openDialog("PromptDialog",{title:__("TFIDF Analyse"),text:__("Max number of related posts to keep per post?"),value:"5"}),e={path:this.meta().path+"/api/ai/analyse.lua",parameters:{dbpath:this.dbhandle.info.file.path,top:parseInt(t)}},i=await this._api.apigateway(e,!1);if(i.error)throw new Error(i.error);this.toast(i.result)}catch(t){this.error(__("Error analysing posts: {0}",t.toString()),t)}}}]}),this.bloglist.onlistselect=async t=>{const e=this.bloglist.selectedItem;if(!e)return;const i=e.data;if(i)try{const t=await this.blogdb.read({where:{id:Number(i.id)}});if(!t||0==t.length)throw new Error(__("No record found for ID {}",i.id).__());const e=t[0];return this.editor.value(e.content),this.inputtags.value=e.tags,this.find("blog-publish").swon=!!Number(e.publish)}catch(t){return this.error(__("Cannot fetch the entry content"),t)}},this.bloglist.onitemclose=t=>{if(!t)return;const e=t.data.item,i=e.data;return this.openDialog("YesNoDialog",{title:__("Delete a post"),iconclass:"fa fa-question-circle",text:__("Do you really want to delete this post ?")}).then(async t=>{if(!t)return;const a=await this.blogdb.remove({where:{id:Number(i.id)}});if(a.error)throw new Error(a.error);return this.bloglist.delete(e),this.bloglist.unselect(),this.clearEditor()}),!1},this.bindKey("CTRL-S",()=>{const t=this.tabcontainer.selectedTab;if(t&&"blog-container"===t.container.aid)return this.saveBlog()}),this.on("resize",()=>this.resizeContent()),this.resizeContent(),this.init_db()}fetchData(t){switch(t){case"user-container":return this.userdb.read().then(t=>{if(t&&0!=t.length)return this.user=t[0],this.select("[input-class='user-input']").map((t,e)=>$(e).val(this.user[e.name]))}).catch(t=>this.error(__("Cannot fetch user data"),t));case"cv-container":return this.refreshCVCat();default:return this.last_ctime=0,this.bloglist.data=[],this.loadBlogs()}}async saveUser(){try{const t=this.select("[input-class='user-input']");for(let e of t)this.user[e.name]=$(e).val();if(!this.user.fullname||""===this.user.fullname)return this.toast(__("Full name must be entered"));let e=this.userdb;this.user&&this.user.id&&(e=`${this.userdb.path}@${this.user.id}`.asFileHandle()),e.cache=this.user;const i=await e.write(void 0);if(i.error)throw new Error(i.error);this.user.id||(this.user.id=i.result),this.toast(__("User data updated"))}catch(t){this.error(__("Cannot save user data: {0}",t.toString()),t)}}refreshCVCat(){return this.fetchCVCat().then(t=>(this.cvlist.data=t,this.cvlist.expandAll())).catch(t=>this.error(__("Unable to load categories"),t))}fetchCVCat(){return new Promise(async(t,e)=>{try{const e={text:"Porfolio",id:0,nodes:[]},i={order:["name$asc"]},a=await this.cvcatdb.read(i);this.catListToTree(a,e,0),t(e)}catch(t){e(__e(t))}})}catListToTree(t,e,i){const a=t.filter(t=>t.pid==i);if(0===a.length)return e.nodes=null;for(let i of a)i.nodes=[],i.text=i.name,this.catListToTree(t,i,i.id),e.nodes.push(i)}deleteCVCat(t){return new Promise(async(e,i)=>{try{const e=[];var a=function(t){e.push(t.id),t.nodes&&t.nodes.map(t=>a(t))};a(t);let i=await this.cvsecdb.remove({where:{$or:{cid:e}}});if(i.error)throw new Error(i.error);if(i=await this.cvcatdb.remove({where:{$or:{id:e}}}),i.error)throw new Error(i.error);await this.refreshCVCat(),this.seclist.data=[]}catch(t){i(__e(t))}})}CVSectionByCID(t){return new Promise(async(e,i)=>{try{const e=await this.cvsecdb.read({where:{cid:t},order:["start$desc"]}),i=[];this.find("cv-sec-status").text=__("Found {0} sections",e.length);for(let t of e)t.closable=!0,t.tag="afx-blogger-cvsection-item",t.start=Number(t.start),t.end=Number(t.end),t.start<1e3&&(t.start=void 0),t.end<1e3&&(t.end=void 0),i.push(t);this.seclist.data=i}catch(t){i(__e(t))}})}async saveBlog(){try{let t=void 0;const e=this.bloglist.selectedItem;e&&(t=e.data);const i=this.inputtags.value,a=this.editor.value(),s=new RegExp("^#+(.*)\n","g").exec(a);if(!s||2!==s.length)return this.toast(__("Please insert a title in the text: beginning with heading"));if(""===i)return this.toast(__("Please enter tags"));const n=new Date,r={content:a,title:s[1].trim(),tags:i,ctime:t?t.ctime:n.timestamp(),ctimestr:t?t.ctimestr:n.toString(),utime:n.timestamp(),utimestr:n.toString(),rendered:this.process(this.editor.options.previewRender(a)),publish:this.find("blog-publish").swon?1:0};let o=this.blogdb;t&&(r.id=t.id,o=t.$vfs),o.cache=r;const l=await o.write(void 0);if(l.error)throw new Error(l.error);t?(e.data.utime=r.utime,e.data.utimestr=r.utimestr,e.data=e.data):(this.last_ctime=0,this.bloglist.data=[],await this.loadBlogs())}catch(t){this.error(__("Cannot save blog: {0}",t.toString()),t)}}process(t){let e;const i=/\[\[youtube:([^\]]*)\]\]/g,a=[];for(;null!==(e=i.exec(t));)a.push(e);if(!(a.length>0))return t;let s="",n=0;for(let e of a)s+=t.substring(n,e.index),s+=``,n=e.index+e[0].length;return s+=t.substring(n,t.length),s}clearEditor(){return this.editor.value(""),this.inputtags.value="",this.find("blog-publish").swon=!1}loadBlogs(){return new Promise(async(t,e)=>{try{const t={order:["ctime$desc"],fields:["id","title","ctimestr","ctime","utime","utimestr"],limit:10};this.last_ctime&&(t.where={ctime$lt:this.last_ctime});const e=await this.blogdb.read(t);if(0==e.length)return void this.toast(__("No more record to load"));this.last_ctime=e[e.length-1].ctime;for(let t of e)t.tag="afx-blogger-post-item",this.bloglist.push(t);return this.clearEditor(),this.bloglist.selected=-1}catch(t){e(__e(t))}})}resizeContent(){const t=this.find("editor-container"),e=$(".EasyMDEContainer",t).children(),i=$(this.scheme).find(".afx-window-top")[0],a=e[0],s=e[3],n=$(this.scheme).height()-$(i).height()-$(a).height()-$(s).height()-90;return $(e[1]).css("height",n+"px")}}t.Blogger=e,e.singleton=!0,e.dependencies=["pkg://SimpleMDE/main.js","pkg://SimpleMDE/main.css","pkg://Katex/main.js","pkg://Katex/main.css","pkg://SQLiteDB/libsqlite.js"]}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){let i;!function(e){class i extends t.GUI.BasicDialog{constructor(){super("BloggerCategoryDialog",i.scheme)}main(){if(super.main(),this.tree=this.find("tree"),this.txtinput=this.find("txtinput"),this.find("bt-ok").onbtclick=t=>{const e=this.tree.selectedItem;if(!e)return this.notify(__("Please select a parent category"));const i=e.data,a=this.txtinput.value;return""!==a||this.data.selonly?this.data.cat&&this.data.cat.id===i.id?this.notify(__("Parent can not be the category itself")):(this.handle&&this.handle({p:i,value:a}),this.quit()):this.notify(__("Please enter category name"))},this.find("bt-cancel").onbtclick=t=>this.quit(),this.data&&this.data.tree){if(this.data&&this.data.cat){let t;this.txtinput.value=this.data.cat.name,t="0"===this.data.cat.pid?this.data.tree:this.findDataByID(this.data.cat.pid,this.data.tree.nodes),t&&(t.selected=!0)}return this.tree.data=this.data.tree,this.tree.expandAll()}}findDataByID(t,e){for(let i of e){if(i.id===t)return i;i.nodes&&this.findDataByID(t,i.nodes)}}}e.BloggerCategoryDialog=i,i.scheme='\n \n \n \n \n \n \n
\n \n \n
\n \n \n ';class a extends t.GUI.BasicDialog{constructor(){super("BloggerCVSectionDiaglog")}main(){super.main(),this.editor=new EasyMDE({autoDownloadFontAwesome:!1,element:this.find("contentarea"),status:!1,toolbar:!1}),$(this.select('[class = "CodeMirror-scroll"]')[0]).css("min-height","50px"),$(this.select('[class="CodeMirror cm-s-paper CodeMirror-wrap"]')[0]).css("min-height","50px");const t=this.select("[input-class='user-input']");if(this.data&&this.data.section)for(let e of t)$(e).val(this.data.section[e.name]);return this.data&&this.data.section&&this.editor.value(this.data.section.content),this.find("section-publish").swon=!!(this.data&&this.data.section&&Number(this.data.section.publish)),this.find("bt-cv-sec-save").onbtclick=e=>{const i={};for(let e of t)i[e.name]=$(e).val();if(i.content=this.editor.value(),""===i.title&&""===i.content)return this.notify(__("Title or content must not be blank"));this.data&&this.data.section&&(i.id=this.data.section.id);const a=this.find("section-publish").swon;return i.publish=!0===a?1:0,this.handle&&this.handle(i),this.quit()},this.on("resize",()=>this.resizeContent()),this.resizeContent()}resizeContent(){const t=this.find("editor-container"),e=$(".EasyMDEContainer",t).children(),i=$(t).height()-30;return $(e[0]).css("height",i+"px")}}e.BloggerCVSectionDiaglog=a,a.scheme='\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n
\n
\n \n \n
\n \n';class s extends t.GUI.BasicDialog{constructor(){super("BloggerSendmailDiaglog")}main(){super.main(),this.maillinglist=this.find("email-list");const t=new RegExp("^#+(.*)\n","g").exec(this.data.content);this.find("mail-title").value=t[1];const e=this.data.content.substring(0,500)+"...";this.find("contentarea").value=s.template.format(this.data.id,e);const i=this.data.mails.map(t=>({text:t.name,email:t.email,switch:!0,checked:!0}));return console.log(i),this.maillinglist.items=i,this.find("bt-sendmail").onbtclick=t=>{const e=this.maillinglist.items,i=[];for(let t of e)!0===t.checked&&i.push(t);if(0===i.length)return this.notify(__("No email selected"));const a={path:this.meta().path+"/api/sendmail.lua",parameters:{to:i,title:this.find("mail-title").value,content:this.find("contentarea").value,user:this.find("mail-user").value,password:this.find("mail-password").value}};return this._api.apigateway(a,!1).then(t=>{if(t.error){const e=t.result.join(",");return this.notify(__("Unable to send mail to: {0}",e))}return this.quit()}).catch(t=>this.error(__("Error sending mail: {0}",t.toString()),t))}}}e.BloggerSendmailDiaglog=s,s.scheme='\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n',s.template="Hello,\n\nDany LE has just published a new post on his blog: https://blog.iohub.dev/post/id/{0}\n\n==========\n{1}\n==========\n\n\nRead the full article via:\nhttps://blog.iohub.dev/post/id/{0}\n\nYou receive this email because you have been subscribed to his blog.\n\nHave a nice day,\n\nSent from Blogger, an AntOS application"}(i=e.blogger||(e.blogger={}))}(e=t.application||(t.application={}))}(OS||(OS={})),function(t){let e;!function(e){let i;!function(e){class i extends t.GUI.tag.ListViewItemTag{constructor(){super()}ondatachange(){if(!this.data)return;const t=this.data,e=["content","start","end"];return this.closable=t.closable,(()=>{const i=[];for(let a in this.refs){const s=this.refs[a];t[a]&&""!==t[a]?e.includes(a)?i.push($(s).text(t[a])):i.push(s.text=t[a]):i.push(void 0)}return i})()}reload(){}init(){}itemlayout(){return{el:"div",children:[{el:"afx-label",ref:"title",class:"afx-cv-sec-title"},{el:"afx-label",ref:"subtitle",class:"afx-cv-sec-subtitle"},{el:"p",ref:"content",class:"afx-cv-sec-content"},{el:"p",class:"afx-cv-sec-period",children:[{el:"i",ref:"start"},{el:"i",ref:"end",class:"period-end"}]},{el:"afx-label",ref:"location",class:"afx-cv-sec-loc"}]}}}t.GUI.tag.define("afx-blogger-cvsection-item",i);class a extends t.GUI.tag.ListViewItemTag{constructor(){super()}ondatachange(){if(!this.data)return;const t=this.data;t.closable=!0,this.closable=t.closable,this.refs.title.text=t.title,this.refs.ctimestr.text=__("Created: {0}",t.ctimestr),this.refs.utimestr.text=__("Updated: {0}",t.utimestr)}reload(){}init(){}itemlayout(){return{el:"div",children:[{el:"afx-label",ref:"title",class:"afx-blogpost-title"},{el:"afx-label",ref:"ctimestr",class:"blog-dates"},{el:"afx-label",ref:"utimestr",class:"blog-dates"}]}}}t.GUI.tag.define("afx-blogger-post-item",a)}(i=e.blogger||(e.blogger={}))}(e=t.application||(t.application={}))}(OS||(OS={}));
\ No newline at end of file
diff --git a/Blogger/build/debug/package.json b/Blogger/build/debug/package.json
index bd0f367..f03e29a 100644
--- a/Blogger/build/debug/package.json
+++ b/Blogger/build/debug/package.json
@@ -6,7 +6,7 @@
"author": "Xuan Sang LE",
"email": "xsang.le@gmail.com"
},
- "version": "0.2.12-a",
+ "version": "0.2.13-a",
"category": "Internet",
"iconclass": "fa fa-book",
"dependencies": [
diff --git a/Blogger/build/release/Blogger.zip b/Blogger/build/release/Blogger.zip
index 564d0b7..dccbd2f 100644
Binary files a/Blogger/build/release/Blogger.zip and b/Blogger/build/release/Blogger.zip differ
diff --git a/Blogger/main.ts b/Blogger/main.ts
index daae524..dda6ea8 100644
--- a/Blogger/main.ts
+++ b/Blogger/main.ts
@@ -815,7 +815,9 @@ namespace OS {
else
{
//data.text = data.title;
- selel.data = data;
+ selel.data.utime = data.utime;
+ selel.data.utimestr = data.utimestr;
+ selel.data = selel.data;
}
}
catch(e)
diff --git a/Blogger/package.json b/Blogger/package.json
index bd0f367..f03e29a 100644
--- a/Blogger/package.json
+++ b/Blogger/package.json
@@ -6,7 +6,7 @@
"author": "Xuan Sang LE",
"email": "xsang.le@gmail.com"
},
- "version": "0.2.12-a",
+ "version": "0.2.13-a",
"category": "Internet",
"iconclass": "fa fa-book",
"dependencies": [
diff --git a/packages.json b/packages.json
index 9b8722c..ced71e0 100644
--- a/packages.json
+++ b/packages.json
@@ -95,7 +95,7 @@
"description": "https://raw.githubusercontent.com/lxsang/antosdk-apps/2.0.x/Blogger/README.md",
"category": "Internet",
"author": "Xuan Sang LE",
- "version": "0.2.12-a",
+ "version": "0.2.13-a",
"dependencies": ["SimpleMDE@2.18.0-r","Katex@0.11.1-r","SQLiteDB@0.1.0-a"],"mimes":["none"],
"download": "https://raw.githubusercontent.com/lxsang/antosdk-apps/2.0.x/Blogger/build/release/Blogger.zip"
},
diff --git a/release/Blogger.md b/release/Blogger.md
index a0f637b..d39795f 100644
--- a/release/Blogger.md
+++ b/release/Blogger.md
@@ -6,6 +6,7 @@ Blackend for my blog at https://blog.iohub.dev
## Change logs
### v0.2.x-a
+* Patch 13: fix bug on blog save
* Patch 12: support send mail via SSL
* Patch 11: Add TFIDF analyse functionality
* Patch 10: Migrate code to typescript, use SQLiteDB lib for database access
diff --git a/release/Blogger.zip b/release/Blogger.zip
index 564d0b7..dccbd2f 100644
Binary files a/release/Blogger.zip and b/release/Blogger.zip differ
diff --git a/release/packages.json b/release/packages.json
index 9cf6de5..bbc5e77 100644
--- a/release/packages.json
+++ b/release/packages.json
@@ -1 +1 @@
-[{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Archive.zip","category":"Utility","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Archive.md","dependencies":[],"name":"Archive","pkgname":"Archive","version":"0.0.4-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libwvnc.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libwvnc.md","dependencies":["libjpeg@0.1.1-a"],"name":"libwvnc","pkgname":"libwvnc","version":"0.1.2-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/About.zip","category":"Utility","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/About.md","dependencies":[],"name":"About AntOS","pkgname":"About","version":"0.1.2-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Blogger.zip","category":"Internet","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Blogger.md","dependencies":["SimpleMDE@2.18.0-r","Katex@0.11.1-r","SQLiteDB@0.1.0-a"],"name":"Blogging application","pkgname":"Blogger","version":"0.2.12-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GitGraph.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GitGraph.md","dependencies":[],"name":"GIT Visualization","pkgname":"GitGraph","version":"0.1.5-b","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LibreOffice.zip","category":"Office","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LibreOffice.md","dependencies":[],"name":"Libre Office Online","pkgname":"LibreOffice","version":"0.1.4-a","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AceDiff.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AceDiff.md","dependencies":["ACECore@1.4.12-r"],"name":"AceDiff addon library","pkgname":"AceDiff","version":"3.0.3-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/OnlyOffice.zip","category":"Office","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/OnlyOffice.md","dependencies":[],"name":"Office Suite","pkgname":"OnlyOffice","version":"0.1.8-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vfsx.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vfsx.md","dependencies":[],"name":"AntOS VFS handles","pkgname":"vfsx","version":"0.1.1-b","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/PDFLib.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/PDFLib.md","dependencies":[],"name":"PDFLib","pkgname":"PDFLib","version":"1.17.1","author":"pdf-lib.js.org"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antedit.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antedit.md","dependencies":["MonacoCore@0.33.0-r"],"name":"Antos Editor","pkgname":"Antedit","version":"0.2.5-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Booklet.zip","category":"Office","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Booklet.md","dependencies":["SimpleMDE@2.18.0-r","Katex@0.11.1-r"],"name":"Booklet","pkgname":"Booklet","version":"0.2.5-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Docify.zip","category":"Office","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Docify.md","dependencies":["SQLiteDB@0.1.0-a","libpdfjs@2.6.347-r","PDFLib@1.17.1"],"name":"Docify","pkgname":"Docify","version":"0.1.1-b","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelPlugins.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelPlugins.md","dependencies":["Antunnel@0.2.0-b"],"name":"Antunnel Plugins","pkgname":"AntunnelPlugins","version":"0.1.2-a","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShowCase.zip","category":"Utility","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShowCase.md","dependencies":[],"name":"ShowCase","pkgname":"ShowCase","version":"0.0.7-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteDesktop.zip","category":"Internet","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteDesktop.md","dependencies":[],"name":"WVNC remote desktop","pkgname":"RemoteDesktop","version":"0.1.16-b","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Clipper.zip","category":"Utility","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Clipper.md","dependencies":[],"name":"Clipper","pkgname":"Clipper","version":"0.1.4-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antunnel.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antunnel.md","dependencies":[],"name":"Antunnel","pkgname":"Antunnel","version":"0.2.1-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteCamera.zip","category":"Graphics","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteCamera.md","dependencies":["libjpeg@0.1.1-a","Antunnel@0.1.8-a"],"name":"Remote Camera","pkgname":"RemoteCamera","version":"0.1.5-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DiffEditor.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DiffEditor.md","dependencies":["AceDiff@3.0.3-r"],"name":"Diff Editor","pkgname":"DiffEditor","version":"0.1.6-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libthreejs.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libthreejs.md","dependencies":[],"name":"libthreejs","pkgname":"libthreejs","version":"0.0.129-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SimpleMDE.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SimpleMDE.md","dependencies":[],"name":"EasyMDE","pkgname":"SimpleMDE","version":"2.18.0-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SQLiteDB.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SQLiteDB.md","dependencies":[],"name":"SQLite3 Browser","pkgname":"SQLiteDB","version":"0.1.0-a","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GPClient.zip","category":"Internet","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GPClient.md","dependencies":[],"name":"Generic Purpose client","pkgname":"GPClient","version":"0.1.4-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vTerm.zip","category":"System","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vTerm.md","dependencies":["Antunnel@0.2.1-b","xTerm@5.1.0-r"],"name":"Virtual Terminal","pkgname":"vTerm","version":"0.1.20-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ActivityMonitor.zip","category":"System","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ActivityMonitor.md","dependencies":[],"name":"Activity monitor","pkgname":"ActivityMonitor","version":"0.0.8-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShaderPlayground.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShaderPlayground.md","dependencies":["libthreejs@0.0.129-r"],"name":"OpenGL Shader Playground","pkgname":"ShaderPlayground","version":"0.0.4-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MonacoCore.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MonacoCore.md","dependencies":[],"name":"Monaco editor core","pkgname":"MonacoCore","version":"0.33.0-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LuaPlayground.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LuaPlayground.md","dependencies":["ACECore@1.4.12-r"],"name":"LuaPlayground","pkgname":"LuaPlayground","version":"0.1.1-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/VizApp.zip","category":"Graphics","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/VizApp.md","dependencies":["ACECore@1.4.12-r"],"name":"Viz editor","pkgname":"VizApp","version":"0.1.0-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ServerLogClient.zip","category":"System","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ServerLogClient.md","dependencies":["Antunnel@0.2.1-b"],"name":"Server log monitor","pkgname":"ServerLogClient","version":"0.1.3-b","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Dockman.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Dockman.md","dependencies":[],"name":"Remote Docker Manager","pkgname":"Dockman","version":"0.1.1-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SystemControl.zip","category":"System","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SystemControl.md","dependencies":["Antunnel@0.2.1-b"],"name":"System monitoring","pkgname":"SystemControl","version":"0.1.12-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libpdfjs.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libpdfjs.md","dependencies":[],"name":"PDF JS library","pkgname":"libpdfjs","version":"2.6.347-r","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ImageEditor.zip","category":"Graphics","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ImageEditor.md","dependencies":["libfabric@4.4.0-r"],"name":"Image editor","pkgname":"ImageEditor","version":"0.1.0-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DBDecoder.zip","category":"Other","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DBDecoder.md","dependencies":[],"name":"DBDecoder","pkgname":"DBDecoder","version":"0.0.2-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Katex.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Katex.md","dependencies":[],"name":"Katex","pkgname":"Katex","version":"0.11.1-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libplotly.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libplotly.md","dependencies":[],"name":"Plotly","pkgname":"libplotly","version":"2.6.2-r","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/CodePad.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/CodePad.md","dependencies":["ACECore@1.4.12-r"],"name":"Code","pkgname":"CodePad","version":"0.1.7-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelTestClient.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelTestClient.md","dependencies":["Antunnel@0.2.1-b"],"name":"AntunnelTestClient","pkgname":"AntunnelTestClient","version":"0.1.0-a","author":"Dany LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libjpeg.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libjpeg.md","dependencies":[],"name":"libjpeg","pkgname":"libjpeg","version":"0.1.1-a","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libfabric.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libfabric.md","dependencies":[],"name":"Fabric.js library","pkgname":"libfabric","version":"4.4.0-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libantosdk.zip","category":"Development","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libantosdk.md","dependencies":[],"name":"AntOS SDK builder","pkgname":"libantosdk","version":"0.1.2-b","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/xTerm.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/xTerm.md","dependencies":[],"name":"xTerm Library","pkgname":"xTerm","version":"5.1.0-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/TinyEditor.zip","category":"Other","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/TinyEditor.md","dependencies":[],"name":"Tiny editor","pkgname":"TinyEditor","version":"0.0.4-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ACECore.zip","category":"Library","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ACECore.md","dependencies":[],"name":"ACE Editor core","pkgname":"ACECore","version":"1.4.12-r","author":""},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Preview.zip","category":"Graphics","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Preview.md","dependencies":["libpdfjs@2.6.347-r"],"name":"Preview","pkgname":"Preview","version":"0.1.3-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GraphEditor.zip","category":"Graphics","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GraphEditor.md","dependencies":["ACECore@1.4.12-r"],"name":"Graph Editor","pkgname":"GraphEditor","version":"0.1.2-a","author":"Xuan Sang LE"},{"download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MarkOn.zip","category":"Office","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MarkOn.md","dependencies":["SimpleMDE@2.18.0-r"],"name":"Markdown editor","pkgname":"MarkOn","version":"0.1.1-a","author":"Xuan Sang LE"}]
\ No newline at end of file
+[{"name":"Archive","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Archive.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Archive.zip","pkgname":"Archive","dependencies":[],"category":"Utility","version":"0.0.4-a"},{"name":"libwvnc","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libwvnc.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libwvnc.zip","pkgname":"libwvnc","dependencies":["libjpeg@0.1.1-a"],"category":"Library","version":"0.1.2-a"},{"name":"About AntOS","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/About.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/About.zip","pkgname":"About","dependencies":[],"category":"Utility","version":"0.1.2-b"},{"name":"Blogging application","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Blogger.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Blogger.zip","pkgname":"Blogger","dependencies":["SimpleMDE@2.18.0-r","Katex@0.11.1-r","SQLiteDB@0.1.0-a"],"category":"Internet","version":"0.2.13-a"},{"name":"GIT Visualization","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GitGraph.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GitGraph.zip","pkgname":"GitGraph","dependencies":[],"category":"Development","version":"0.1.5-b"},{"name":"Libre Office Online","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LibreOffice.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LibreOffice.zip","pkgname":"LibreOffice","dependencies":[],"category":"Office","version":"0.1.4-a"},{"name":"AceDiff addon library","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AceDiff.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AceDiff.zip","pkgname":"AceDiff","dependencies":["ACECore@1.4.12-r"],"category":"Library","version":"3.0.3-r"},{"name":"Office Suite","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/OnlyOffice.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/OnlyOffice.zip","pkgname":"OnlyOffice","dependencies":[],"category":"Office","version":"0.1.8-a"},{"name":"AntOS VFS handles","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vfsx.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vfsx.zip","pkgname":"vfsx","dependencies":[],"category":"Library","version":"0.1.1-b"},{"name":"PDFLib","author":"pdf-lib.js.org","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/PDFLib.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/PDFLib.zip","pkgname":"PDFLib","dependencies":[],"category":"Library","version":"1.17.1"},{"name":"Antos Editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antedit.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antedit.zip","pkgname":"Antedit","dependencies":["MonacoCore@0.33.0-r"],"category":"Development","version":"0.2.5-b"},{"name":"Booklet","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Booklet.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Booklet.zip","pkgname":"Booklet","dependencies":["SimpleMDE@2.18.0-r","Katex@0.11.1-r"],"category":"Office","version":"0.2.5-a"},{"name":"Docify","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Docify.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Docify.zip","pkgname":"Docify","dependencies":["SQLiteDB@0.1.0-a","libpdfjs@2.6.347-r","PDFLib@1.17.1"],"category":"Office","version":"0.1.1-b"},{"name":"Antunnel Plugins","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelPlugins.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelPlugins.zip","pkgname":"AntunnelPlugins","dependencies":["Antunnel@0.2.0-b"],"category":"Library","version":"0.1.2-a"},{"name":"ShowCase","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShowCase.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShowCase.zip","pkgname":"ShowCase","dependencies":[],"category":"Utility","version":"0.0.7-a"},{"name":"WVNC remote desktop","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteDesktop.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteDesktop.zip","pkgname":"RemoteDesktop","dependencies":[],"category":"Internet","version":"0.1.16-b"},{"name":"Clipper","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Clipper.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Clipper.zip","pkgname":"Clipper","dependencies":[],"category":"Utility","version":"0.1.4-a"},{"name":"Antunnel","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antunnel.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Antunnel.zip","pkgname":"Antunnel","dependencies":[],"category":"Library","version":"0.2.1-b"},{"name":"Remote Camera","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteCamera.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/RemoteCamera.zip","pkgname":"RemoteCamera","dependencies":["libjpeg@0.1.1-a","Antunnel@0.1.8-a"],"category":"Graphics","version":"0.1.5-a"},{"name":"Diff Editor","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DiffEditor.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DiffEditor.zip","pkgname":"DiffEditor","dependencies":["AceDiff@3.0.3-r"],"category":"Development","version":"0.1.6-a"},{"name":"libthreejs","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libthreejs.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libthreejs.zip","pkgname":"libthreejs","dependencies":[],"category":"Library","version":"0.0.129-r"},{"name":"EasyMDE","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SimpleMDE.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SimpleMDE.zip","pkgname":"SimpleMDE","dependencies":[],"category":"Library","version":"2.18.0-r"},{"name":"SQLite3 Browser","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SQLiteDB.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SQLiteDB.zip","pkgname":"SQLiteDB","dependencies":[],"category":"Library","version":"0.1.0-a"},{"name":"Generic Purpose client","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GPClient.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GPClient.zip","pkgname":"GPClient","dependencies":[],"category":"Internet","version":"0.1.4-a"},{"name":"Virtual Terminal","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vTerm.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/vTerm.zip","pkgname":"vTerm","dependencies":["Antunnel@0.2.1-b","xTerm@5.1.0-r"],"category":"System","version":"0.1.20-a"},{"name":"Activity monitor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ActivityMonitor.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ActivityMonitor.zip","pkgname":"ActivityMonitor","dependencies":[],"category":"System","version":"0.0.8-b"},{"name":"OpenGL Shader Playground","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShaderPlayground.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ShaderPlayground.zip","pkgname":"ShaderPlayground","dependencies":["libthreejs@0.0.129-r"],"category":"Development","version":"0.0.4-a"},{"name":"Monaco editor core","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MonacoCore.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MonacoCore.zip","pkgname":"MonacoCore","dependencies":[],"category":"Library","version":"0.33.0-r"},{"name":"LuaPlayground","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LuaPlayground.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/LuaPlayground.zip","pkgname":"LuaPlayground","dependencies":["ACECore@1.4.12-r"],"category":"Development","version":"0.1.1-a"},{"name":"Viz editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/VizApp.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/VizApp.zip","pkgname":"VizApp","dependencies":["ACECore@1.4.12-r"],"category":"Graphics","version":"0.1.0-a"},{"name":"Server log monitor","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ServerLogClient.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ServerLogClient.zip","pkgname":"ServerLogClient","dependencies":["Antunnel@0.2.1-b"],"category":"System","version":"0.1.3-b"},{"name":"Remote Docker Manager","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Dockman.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Dockman.zip","pkgname":"Dockman","dependencies":[],"category":"Development","version":"0.1.1-b"},{"name":"System monitoring","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SystemControl.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/SystemControl.zip","pkgname":"SystemControl","dependencies":["Antunnel@0.2.1-b"],"category":"System","version":"0.1.12-a"},{"name":"PDF JS library","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libpdfjs.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libpdfjs.zip","pkgname":"libpdfjs","dependencies":[],"category":"Library","version":"2.6.347-r"},{"name":"Image editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ImageEditor.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ImageEditor.zip","pkgname":"ImageEditor","dependencies":["libfabric@4.4.0-r"],"category":"Graphics","version":"0.1.0-a"},{"name":"DBDecoder","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DBDecoder.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/DBDecoder.zip","pkgname":"DBDecoder","dependencies":[],"category":"Other","version":"0.0.2-a"},{"name":"Katex","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Katex.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Katex.zip","pkgname":"Katex","dependencies":[],"category":"Library","version":"0.11.1-r"},{"name":"Plotly","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libplotly.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libplotly.zip","pkgname":"libplotly","dependencies":[],"category":"Library","version":"2.6.2-r"},{"name":"Code","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/CodePad.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/CodePad.zip","pkgname":"CodePad","dependencies":["ACECore@1.4.12-r"],"category":"Development","version":"0.1.7-b"},{"name":"AntunnelTestClient","author":"Dany LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelTestClient.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/AntunnelTestClient.zip","pkgname":"AntunnelTestClient","dependencies":["Antunnel@0.2.1-b"],"category":"Development","version":"0.1.0-a"},{"name":"libjpeg","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libjpeg.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libjpeg.zip","pkgname":"libjpeg","dependencies":[],"category":"Library","version":"0.1.1-a"},{"name":"Fabric.js library","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libfabric.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libfabric.zip","pkgname":"libfabric","dependencies":[],"category":"Library","version":"4.4.0-r"},{"name":"AntOS SDK builder","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libantosdk.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/libantosdk.zip","pkgname":"libantosdk","dependencies":[],"category":"Development","version":"0.1.2-b"},{"name":"xTerm Library","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/xTerm.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/xTerm.zip","pkgname":"xTerm","dependencies":[],"category":"Library","version":"5.1.0-r"},{"name":"Tiny editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/TinyEditor.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/TinyEditor.zip","pkgname":"TinyEditor","dependencies":[],"category":"Other","version":"0.0.4-a"},{"name":"ACE Editor core","author":"","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ACECore.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/ACECore.zip","pkgname":"ACECore","dependencies":[],"category":"Library","version":"1.4.12-r"},{"name":"Preview","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Preview.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/Preview.zip","pkgname":"Preview","dependencies":["libpdfjs@2.6.347-r"],"category":"Graphics","version":"0.1.3-a"},{"name":"Graph Editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GraphEditor.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/GraphEditor.zip","pkgname":"GraphEditor","dependencies":["ACECore@1.4.12-r"],"category":"Graphics","version":"0.1.2-a"},{"name":"Markdown editor","author":"Xuan Sang LE","description":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MarkOn.md","download":"https://ci.iohub.dev/public/antos-release/packages/2.0.x/MarkOn.zip","pkgname":"MarkOn","dependencies":["SimpleMDE@2.18.0-r"],"category":"Office","version":"0.1.1-a"}]
\ No newline at end of file