simplemdg-dev-cli 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,10 +26,16 @@ var ICONS = {
26
26
  gear:"M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z|M19.4 13a7.9 7.9 0 0 0 0-2l2-1.5-2-3.4-2.3 1a8 8 0 0 0-1.7-1l-.4-2.6h-4l-.4 2.6a8 8 0 0 0-1.7 1l-2.3-1-2 3.4L4.6 11a7.9 7.9 0 0 0 0 2l-2 1.5 2 3.4 2.3-1a8 8 0 0 0 1.7 1l.4 2.6h4l.4-2.6a8 8 0 0 0 1.7-1l2.3 1 2-3.4z",
27
27
  home:"M3 11l9-8 9 8|M5 10v10h14V10",
28
28
  table2:"M4 4h16v16H4z|M4 9h16|M9 4v16",
29
- col:"M5 4v16|M12 4v16|M19 4v16"
29
+ col:"M5 4v16|M12 4v16|M19 4v16",
30
+ trash:"M4 7h16|M9 7V4h6v3|M6 7l1 13h10l1-13|M10 11v6|M14 11v6",
31
+ chevL:"M15 6l-6 6 6 6",
32
+ chevR:"M9 6l6 6-6 6",
33
+ filter:"M3 5h18l-7 8v6l-4 2v-8z",
34
+ undo:"M9 7L4 12l5 5|M4 12h11a5 5 0 0 1 0 10h-3"
30
35
  };
31
36
  function svgFor(name){var d=ICONS[name]||"";return '<svg class="ic" viewBox="0 0 24 24">'+d.split("|").map(function(p){return '<path d="'+p+'"></path>';}).join("")+'</svg>';}
32
37
  function icEl(name,cls){var s=document.createElement("span");s.className="ticon "+(cls||"");s.innerHTML=svgFor(name);return s;}
38
+ function gbtn(icon,title,onClick,extra){var b=el("button",{class:"gbtn "+(extra||""),title:title,html:svgFor(icon)});b.addEventListener("click",function(e){onClick(e);});return b;}
33
39
 
34
40
  /* ---------- dom helpers ---------- */
35
41
  function $(id){return document.getElementById(id);}
@@ -300,37 +306,46 @@ function showFilterSql(tab,anchor){var g=tab.state.g;api("POST","/api/sql/genera
300
306
  function pendingCount(g){return Object.keys(g.edits).length+Object.keys(g.deletes).length+g.inserts.length;}
301
307
  function rowKeyOf(g,row){return g.pk.map(function(k){return String(row[k]);}).join("\\u0001");}
302
308
  function buildDataPane(pane,tab,schema,table,restore){
303
- var g={schema:schema,table:table,pk:[],columns:[],rows:[],offset:0,pageSize:100,where:"",orderBy:"",orderDir:"asc",total:null,edits:{},deletes:{},inserts:[],errors:{},editable:false,selRow:null,iseq:0,undo:[],redo:[]};tab.state.g=g;
304
- var whereI=el("input",{class:"input",style:"flex:1",placeholder:"WHERE clause, e.g. STATUS = 'A'"});g.whereI=whereI;
305
- var pageSel=el("select",{class:"select",style:"width:auto"});["100","500","1000"].forEach(function(v){pageSel.appendChild(el("option",{value:v,text:v}));});g.pageSel=pageSel;
306
- var pageInfo=el("span",{class:"note"});g.pageInfo=pageInfo;
307
- wireSearch(whereI,function(){g.where=whereI.value;g.offset=0;loadData(tab);});
308
- var tb1=el("div",{class:"toolbar"},[whereI,el("button",{class:"btn",text:"Apply",onclick:function(){g.where=whereI.value;g.offset=0;loadData(tab);}}),el("button",{class:"btn ghost",text:"Show SQL",onclick:function(e){showFilterSql(tab,e.currentTarget);}}),el("button",{class:"btn sec",text:"Refresh",onclick:function(){loadData(tab);}}),el("span",{class:"note",text:"Page"}),pageSel,el("button",{class:"btn sec",text:"Prev",onclick:function(){g.offset=Math.max(0,g.offset-parseInt(pageSel.value,10));loadData(tab);}}),el("button",{class:"btn sec",text:"Next",onclick:function(){g.offset+=parseInt(pageSel.value,10);loadData(tab);}}),pageInfo,el("span",{class:"grow"}),el("button",{class:"btn ghost",text:"CSV",onclick:function(){exportData(tab,"csv");}}),el("button",{class:"btn ghost",text:"JSON",onclick:function(){exportData(tab,"json");}})]);
309
- var saveBtn=el("button",{class:"btn",text:"Save changes",onclick:function(){saveDataChanges(tab);}});var revertBtn=el("button",{class:"btn ghost",text:"Revert all",onclick:function(){revertAll(tab);}});g.saveBtn=saveBtn;g.revertBtn=revertBtn;
310
- var editHint=el("span",{class:"note"});g.editHint=editHint;
311
- var tb2=el("div",{class:"toolbar"},[el("button",{class:"btn sec",text:"+ Insert row",onclick:function(){addInsertRow(tab);}}),el("button",{class:"btn ghost",text:"Delete selected",onclick:function(){toggleDeleteSelected(tab);}}),el("button",{class:"btn ghost",text:"Structure",onclick:function(){openStructureTab(schema,table);}}),saveBtn,revertBtn,editHint]);
312
- var grid=el("div",{class:"gridwrap"});g.grid=grid;
309
+ var g={schema:schema,table:table,pk:[],columns:[],rows:[],offset:0,pageSize:100,where:"",orderBy:"",orderDir:"asc",total:null,edits:{},deletes:{},inserts:[],errors:{},editable:false,sel:{},iseq:0,undo:[],redo:[]};tab.state.g=g;
310
+ var whereI=el("input",{spellcheck:"false",placeholder:"WHERE clause, e.g. STATUS = 'A' AND CREATEDBY LIKE '%admin%'"});g.whereI=whereI;
311
+ var clr=el("span",{class:"clr",html:svgFor("x"),title:"Clear filter"});
312
+ var whereBox=el("div",{class:"wherebox"},[el("span",{html:svgFor("filter")}),whereI,clr]);
313
+ var apply=function(){g.where=whereI.value;g.offset=0;applyBtn.classList.remove("on");loadData(tab);};
314
+ var applyBtn=gbtn("run","Apply filter (Enter)",apply);g.applyBtn=applyBtn;
315
+ clr.addEventListener("click",function(){whereI.value="";whereBox.classList.remove("has");apply();});
316
+ whereI.addEventListener("input",function(){whereBox.classList.toggle("has",!!whereI.value);applyBtn.classList.toggle("on",whereI.value!==g.where);});
317
+ whereI.addEventListener("keydown",function(e){if(e.key==="Enter"){e.preventDefault();apply();if(e.ctrlKey||e.metaKey)showFilterSql(tab,applyBtn);}else if(e.key==="Escape"){e.preventDefault();whereI.value="";whereBox.classList.remove("has");apply();}});
318
+ var insBtn=gbtn("plus","Insert row",function(){addInsertRow(tab);});g.insBtn=insBtn;
319
+ var delBtn=gbtn("trash","Mark selected rows for delete",function(){toggleDeleteSelected(tab);},"danger");g.delBtn=delBtn;
320
+ var tb=el("div",{class:"gtoolbar"},[whereBox,applyBtn,gbtn("sql","Show generated SQL",function(e){showFilterSql(tab,e.currentTarget);}),gbtn("refresh","Refresh data",function(e){var b=e.currentTarget;b.classList.add("spinning");loadData(tab,function(){b.classList.remove("spinning");});}),el("span",{class:"gsep"}),insBtn,delBtn,gbtn("col","Open structure",function(){openStructureTab(schema,table);}),el("span",{class:"gsep"}),gbtn("imp","Export data",function(e){openExportMenu(tab,e.currentTarget);})]);
313
321
  var changeBar=el("div",{class:"changebar hidden"});g.changeBar=changeBar;
314
- pane.appendChild(crumbs((activeConn()||{}).name,schema,table));pane.appendChild(tb1);pane.appendChild(tb2);pane.appendChild(changeBar);pane.appendChild(grid);
322
+ var grid=el("div",{class:"gridwrap"});g.grid=grid;
323
+ var pageSel=el("select");["100","500","1000"].forEach(function(v){pageSel.appendChild(el("option",{value:v,text:v}));});g.pageSel=pageSel;
324
+ pageSel.addEventListener("change",function(){g.offset=0;loadData(tab);});
325
+ var rangeSpan=el("span",{class:"note"});g.rangeSpan=rangeSpan;var durSpan=el("span",{class:"note"});g.durSpan=durSpan;
326
+ var footer=el("div",{class:"gridfoot"},[rangeSpan,el("span",{style:"flex:1"}),el("span",{class:"pg"},[gbtn("chevL","Previous page",function(){g.offset=Math.max(0,g.offset-parseInt(pageSel.value,10));loadData(tab);}),el("span",{class:"note",text:"Rows"}),pageSel,gbtn("chevR","Next page",function(){g.offset+=parseInt(pageSel.value,10);loadData(tab);})]),durSpan]);
327
+ pane.appendChild(crumbs((activeConn()||{}).name,schema,table));pane.appendChild(tb);pane.appendChild(changeBar);pane.appendChild(grid);pane.appendChild(footer);
315
328
  updateDirtyButtons(tab);
316
329
  api("GET","/api/catalog/columns?"+qstr({connectionId:S.activeConnId,schema:schema,table:table})).then(function(r){g.columns=r.columns||[];}).catch(function(){});
317
- api("GET","/api/catalog/primary-key?"+qstr({connectionId:S.activeConnId,schema:schema,table:table})).then(function(r){g.pk=(r.primaryKey&&r.primaryKey.columns)||[];g.editable=g.pk.length>0;editHint.textContent=g.editable?("Editable \\u00b7 double-click a cell \\u00b7 key: "+g.pk.join(", ")):"Read-only (no primary key detected)";renderGrid(tab);}).catch(function(){editHint.textContent="";});
318
- if(restore){g.where=restore.where||"";whereI.value=g.where;g.offset=restore.offset||0;if(restore.pageSize)pageSel.value=String(restore.pageSize);if(restore.sort&&restore.sort[0]){g.orderBy=restore.sort[0].column;g.orderDir=restore.sort[0].direction;}}
330
+ api("GET","/api/catalog/primary-key?"+qstr({connectionId:S.activeConnId,schema:schema,table:table})).then(function(r){g.pk=(r.primaryKey&&r.primaryKey.columns)||[];g.editable=g.pk.length>0;insBtn.disabled=!g.editable;delBtn.disabled=!g.editable;insBtn.title=g.editable?"Insert row":"Read-only (no primary key)";renderGrid(tab);}).catch(function(){});
331
+ if(restore){g.where=restore.where||"";whereI.value=g.where;whereBox.classList.toggle("has",!!g.where);g.offset=restore.offset||0;if(restore.pageSize)pageSel.value=String(restore.pageSize);if(restore.sort&&restore.sort[0]){g.orderBy=restore.sort[0].column;g.orderDir=restore.sort[0].direction;}}
319
332
  loadData(tab);
320
333
  loadCount(tab);
321
334
  }
322
- function loadData(tab){var g=tab.state.g;g.pageSize=parseInt(g.pageSel.value,10);clear(g.grid).appendChild(el("div",{class:"empty"},[el("span",{class:"spin"})," loading data..."]));api("POST","/api/table/data",{connectionId:S.activeConnId,schema:g.schema,table:g.table,limit:g.pageSize,offset:g.offset,where:g.where,orderBy:g.orderBy,orderDirection:g.orderDir}).then(function(r){g.rows=r.result.rows;g.selRow=null;renderGrid(tab);g.pageInfo.textContent="Offset "+g.offset+" \\u00b7 "+r.result.rowCount+" rows \\u00b7 "+r.result.durationMs+"ms";$("stDuration").textContent=r.result.durationMs+"ms";$("stRows").textContent=(g.total!=null?g.total+" total":r.result.rowCount+" rows");}).catch(function(e){clear(g.grid).appendChild(el("div",{class:"errbox",text:"Cannot load data.\\nReason: "+e.message+"\\nAction: test the connection or refresh from BTP app env."}));});}
335
+ function selectedKeys(g){return Object.keys(g.sel);}
336
+ function loadData(tab,onDone){var g=tab.state.g;g.pageSize=parseInt(g.pageSel.value,10);clear(g.grid).appendChild(el("div",{class:"empty"},[el("span",{class:"spin"})," loading data..."]));$("stDuration").textContent="…";api("POST","/api/table/data",{connectionId:S.activeConnId,schema:g.schema,table:g.table,limit:g.pageSize,offset:g.offset,where:g.where,orderBy:g.orderBy,orderDirection:g.orderDir}).then(function(r){g.rows=r.result.rows;g.sel={};renderGrid(tab);var to=g.offset+r.result.rowCount;g.rangeSpan.textContent="Showing "+(r.result.rowCount?g.offset+1:0)+"-"+to+(g.total!=null?" of "+g.total.toLocaleString():"");g.durSpan.textContent="Duration: "+r.result.durationMs+"ms · Offset: "+g.offset;$("stDuration").textContent=r.result.durationMs+"ms";$("stRows").textContent=(g.total!=null?g.total+" total":r.result.rowCount+" rows");if(onDone)onDone();}).catch(function(e){clear(g.grid).appendChild(el("div",{class:"errbox",text:"Cannot load data.\\nReason: "+e.message+"\\nAction: test the connection or refresh from BTP app env."}));if(onDone)onDone();});}
323
337
  function loadCount(tab){var g=tab.state.g;api("POST","/api/table/count",{connectionId:S.activeConnId,schema:g.schema,table:g.table}).then(function(r){g.total=r.count;$("stRows").textContent=r.count+" total";}).catch(function(){});}
324
338
  function dataSortToggle(tab,field){var g=tab.state.g;if(g.orderBy===field)g.orderDir=g.orderDir==="asc"?"desc":"asc";else{g.orderBy=field;g.orderDir="asc";}g.offset=0;loadData(tab);}
325
339
  function renderGrid(tab){var g=tab.state.g;var box=clear(g.grid);if(!g.rows.length&&!g.inserts.length){box.appendChild(el("div",{class:"empty",text:"No rows."}));return;}
326
340
  var fields=g.columns.length?g.columns.map(function(c){return c.name;}):(g.rows[0]?Object.keys(g.rows[0]):[]);g.fields=fields;
327
341
  var table=el("table",{class:"grid"});var thead=el("thead");var htr=el("tr");htr.appendChild(el("th",{class:"rowhdr",text:"#"}));fields.forEach(function(f){var arrow=g.orderBy===f?(g.orderDir==="desc"?" \\u25BC":" \\u25B2"):"";var th=el("th",{title:"Click to sort",text:f+arrow});th.addEventListener("click",function(){dataSortToggle(tab,f);});htr.appendChild(th);});thead.appendChild(htr);table.appendChild(thead);
328
342
  var tbody=el("tbody");
329
- g.rows.forEach(function(row,ri){var key=rowKeyOf(g,row);var deleted=!!g.deletes[key];var edited=g.edits[key];var err=g.errors[key];var tr=el("tr",{class:(g.selRow===ri?"selrow ":"")+(deleted?"row-del ":"")+(err?"row-err ":""),"data-ri":ri});
343
+ g.rows.forEach(function(row,ri){var key=rowKeyOf(g,row);var deleted=!!g.deletes[key];var edited=g.edits[key];var err=g.errors[key];var tr=el("tr",{class:(g.sel[key]?"selrow ":"")+(deleted?"row-del ":"")+(err?"row-err ":""),"data-ri":ri});
344
+ tr.addEventListener("contextmenu",function(e){e.preventDefault();rowContextMenu(e,tab,row);});
330
345
  var flag=edited?'<span class="rowflag d"></span>':(deleted?'<span class="rowflag del"></span>':"");
331
- var num=el("td",{class:"rowhdr",html:flag+(g.offset+ri+1),title:err||""});num.addEventListener("click",function(){g.selRow=(g.selRow===ri?null:ri);renderGrid(tab);});tr.appendChild(num);
346
+ var num=el("td",{class:"rowhdr",html:flag+(g.offset+ri+1),title:err||""});num.addEventListener("click",function(e){if(!(e.ctrlKey||e.metaKey||e.shiftKey))g.sel={};if(g.sel[key])delete g.sel[key];else g.sel[key]=true;renderGrid(tab);});tr.appendChild(num);
332
347
  fields.forEach(function(f){var hasEdit=edited&&Object.prototype.hasOwnProperty.call(edited,f);var v=hasEdit?edited[f]:row[f];var disp=v==null?"":typeof v==="object"?JSON.stringify(v):String(v);var td=el("td",{class:(typeof v==="number"?"num ":"")+(hasEdit?"edited":""),title:disp,text:disp.length>400?disp.slice(0,400)+"\\u2026":disp});if(g.editable&&!deleted){td.addEventListener("dblclick",function(){startEdit(tab,td,ri,f,row);});}else{td.addEventListener("dblclick",function(){openCellViewer(v);});}tr.appendChild(td);});
333
- if(err){var etr=tr;etr.title=err;}
348
+ if(err){tr.title=err;}
334
349
  tbody.appendChild(tr);});
335
350
  g.inserts.forEach(function(ins){var tr=el("tr",{class:"row-ins"});tr.appendChild(el("td",{class:"rowhdr",html:'<span class="rowflag ins"></span>'+"new",onclick:function(){g.inserts=g.inserts.filter(function(x){return x!==ins;});updateDirtyButtons(tab);renderGrid(tab);}}));
336
351
  fields.forEach(function(f){var inp=el("input",{class:"cellinput",value:ins.values[f]!=null?ins.values[f]:""});inp.addEventListener("input",function(){if(inp.value==="")delete ins.values[f];else ins.values[f]=inp.value;});var td=el("td");td.appendChild(inp);tr.appendChild(td);});
@@ -342,11 +357,61 @@ function renderGrid(tab){var g=tab.state.g;var box=clear(g.grid);if(!g.rows.leng
342
357
  function startEdit(tab,td,ri,field,row){if(td.querySelector("input"))return;var g=tab.state.g;var colIdx=g.fields?g.fields.indexOf(field):-1;var key=rowKeyOf(g,row);var cur=g.edits[key]&&Object.prototype.hasOwnProperty.call(g.edits[key],field)?g.edits[key][field]:row[field];var input=el("input",{class:"cellinput"});input.value=cur==null?"":typeof cur==="object"?JSON.stringify(cur):String(cur);clear(td).appendChild(input);input.focus();input.select();var done=false;function commit(){if(done)return true;done=true;var origStr=row[field]==null?"":String(row[field]);if(input.value!==origStr){gridPushUndo(tab);g.edits[key]=g.edits[key]||{};g.edits[key][field]=input.value;}else if(g.edits[key]){delete g.edits[key][field];if(!Object.keys(g.edits[key]).length)delete g.edits[key];}return true;}
343
358
  input.addEventListener("keydown",function(e){if(e.key==="Enter"){e.preventDefault();commit();renderGrid(tab);editAt(tab,ri+1,colIdx);}else if(e.key==="Tab"){e.preventDefault();commit();renderGrid(tab);editAt(tab,ri,colIdx+1);}else if(e.key==="Escape"){e.preventDefault();done=true;renderGrid(tab);}});
344
359
  input.addEventListener("blur",function(){if(!done){commit();renderGrid(tab);}});}
345
- function toggleDeleteSelected(tab){var g=tab.state.g;if(g.selRow==null)return logMsg("Click a row number to select it first.","warn");if(!g.editable)return logMsg("Cannot delete: no primary key.","warn");gridPushUndo(tab);var key=rowKeyOf(g,g.rows[g.selRow]);if(g.deletes[key])delete g.deletes[key];else g.deletes[key]=true;renderGrid(tab);}
360
+ function toastAction(msg,actionLabel,onAction){var t=el("div",{class:"toast"});t.appendChild(el("span",{text:msg+" "}));t.appendChild(el("a",{class:"link",text:actionLabel,onclick:function(){onAction();t.remove();}}));$("toasts").appendChild(t);setTimeout(function(){t.style.opacity="0";setTimeout(function(){t.remove();},250);},6000);}
361
+ function toggleDeleteSelected(tab){var g=tab.state.g;if(!g.editable)return logMsg("Cannot delete: table has no primary key.","warn");var keys=selectedKeys(g);if(!keys.length)return logMsg("Select one or more rows (click the row number).","warn");
362
+ if(keys.every(function(k){return g.deletes[k];})){gridPushUndo(tab);keys.forEach(function(k){delete g.deletes[k];});renderGrid(tab);return;}
363
+ var mark=function(){gridPushUndo(tab);keys.forEach(function(k){g.deletes[k]=true;});g.sel={};renderGrid(tab);toastAction(keys.length+" row"+(keys.length>1?"s":"")+" marked for delete. They are removed only when you Save Changes.","Undo",function(){gridUndo(tab);});};
364
+ if(keys.length>1){if(confirm("Mark "+keys.length+" selected rows for deletion?\\nThey will not be deleted until you click Save Changes."))mark();}else mark();}
346
365
  function addInsertRow(tab){var g=tab.state.g;gridPushUndo(tab);g.inserts.push({iseq:++g.iseq,values:{}});renderGrid(tab);}
347
366
  function revertAll(tab){var g=tab.state.g;g.edits={};g.deletes={};g.inserts=[];g.errors={};renderGrid(tab);logMsg("Reverted pending changes.","ok");}
348
- function updateDirtyButtons(tab){var g=tab.state.g;var nu=Object.keys(g.edits).length,nd=Object.keys(g.deletes).length,ni=g.inserts.length;var n=nu+nd+ni;g.saveBtn.style.display=n>0?"":"none";g.revertBtn.style.display=n>0?"":"none";setDirty(tab,n>0);if(g.changeBar){if(n>0){g.changeBar.classList.remove("hidden");clear(g.changeBar);g.changeBar.appendChild(el("span",{text:"Pending changes: "+nu+" update(s), "+ni+" insert(s), "+nd+" delete(s)"}));g.changeBar.appendChild(el("span",{class:"grow"}));g.changeBar.appendChild(el("button",{class:"btn sm",text:"Save Changes",onclick:function(){saveDataChanges(tab);}}));g.changeBar.appendChild(el("button",{class:"btn sm ghost",text:"Revert All",onclick:function(){revertAll(tab);}}));g.changeBar.appendChild(el("button",{class:"btn sm ghost",text:"Show Changes",onclick:function(){showChanges(tab);}}));}else g.changeBar.classList.add("hidden");}}
349
- function exportData(tab,fmt){var g=tab.state.g;if(!g.rows.length)return logMsg("No rows.","warn");var fields=g.columns.length?g.columns.map(function(c){return c.name;}):Object.keys(g.rows[0]);fetch(fmt==="csv"?"/api/export/csv":"/api/export/json",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({fields:fields,rows:g.rows})}).then(function(r){return r.blob();}).then(function(b){var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download=fmt==="csv"?g.table+".csv":g.table+".json";a.click();});}
367
+ function updateDirtyButtons(tab){var g=tab.state.g;var nu=Object.keys(g.edits).length,nd=Object.keys(g.deletes).length,ni=g.inserts.length;var n=nu+nd+ni;if(g.saveBtn)g.saveBtn.style.display=n>0?"":"none";if(g.revertBtn)g.revertBtn.style.display=n>0?"":"none";setDirty(tab,n>0);if(g.changeBar){if(n>0){g.changeBar.classList.remove("hidden");clear(g.changeBar);g.changeBar.appendChild(el("span",{},["Pending: ",el("span",{class:"cnt-u",text:nu+" edit"+(nu===1?"":"s")})," · ",el("span",{class:"cnt-i",text:ni+" insert"+(ni===1?"":"s")})," · ",el("span",{class:"cnt-d",text:nd+" delete"+(nd===1?"":"s")})]));g.changeBar.appendChild(el("span",{style:"flex:1"}));g.changeBar.appendChild(el("button",{class:"btn sm",title:"Ctrl+S",text:"Save",onclick:function(){saveDataChanges(tab);}}));g.changeBar.appendChild(el("button",{class:"btn sm ghost",title:"Revert all (Ctrl+Z to undo)",text:"Revert",onclick:function(){revertAll(tab);}}));g.changeBar.appendChild(el("button",{class:"btn sm ghost",text:"Show changes",onclick:function(){showChanges(tab);}}));}else g.changeBar.classList.add("hidden");}}
368
+ function gridFields(g){return g.columns.length?g.columns.map(function(c){return c.name;}):(g.rows[0]?Object.keys(g.rows[0]):[]);}
369
+ function exportFilename(g,fmt,suffix){var conn=(activeConn()||{}).name||"db";var stamp=new Date().toISOString().replace(/[:.]/g,"-").slice(0,19);return (conn+"_"+g.schema+"_"+g.table+(suffix?"_"+suffix:"")+"_"+stamp+"."+fmt).replace(/[^a-z0-9._-]+/gi,"-");}
370
+ function exportRowsToFile(fields,rows,fmt,filename){return fetch(fmt==="csv"?"/api/export/csv":"/api/export/json",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({fields:fields,rows:rows})}).then(function(r){return r.blob();}).then(function(b){var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download=filename;a.click();});}
371
+ function exportCurrentPage(tab,fmt){var g=tab.state.g;if(!g.rows.length)return logMsg("No rows to export.","warn");logMsg("Preparing export…","ok");exportRowsToFile(gridFields(g),g.rows,fmt,exportFilename(g,fmt,"page")).then(function(){logMsg("Exported current page as "+fmt.toUpperCase(),"ok");});}
372
+ function exportSelected(tab,fmt){var g=tab.state.g;var rows=g.rows.filter(function(r){return g.sel[rowKeyOf(g,r)];});if(!rows.length)return logMsg("Select rows first (click row numbers).","warn");exportRowsToFile(gridFields(g),rows,fmt,exportFilename(g,fmt,"selected")).then(function(){logMsg("Exported "+rows.length+" selected row(s) as "+fmt.toUpperCase(),"ok");});}
373
+ function exportViaApi(tab,source,fmt,extra){var g=tab.state.g;logMsg("Preparing export…","ok");var body={connectionId:S.activeConnId,schema:g.schema,objectName:g.table,objectType:"table",source:source,format:fmt,whereClause:g.where,limit:g.pageSize,offset:g.offset,sort:g.orderBy?[{column:g.orderBy,direction:g.orderDir}]:[]};if(extra)for(var k in extra)body[k]=extra[k];return fetch("/api/export/data",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(body)}).then(function(r){if(!r.ok)return r.text().then(function(t){throw new Error(t||("HTTP "+r.status));});return r.blob();}).then(function(b){var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download=exportFilename(g,fmt,source);a.click();logMsg("Export completed ("+fmt.toUpperCase()+")","ok");}).catch(function(e){logMsg("Export failed: "+e.message,"err");});}
374
+ function openExportMenu(tab,anchor){var r=anchor.getBoundingClientRect();showCtx(r.left,r.bottom+4,[
375
+ {label:"Current page · CSV",icon:"imp",onClick:function(){exportCurrentPage(tab,"csv");}},
376
+ {label:"Current page · JSON",icon:"imp",onClick:function(){exportCurrentPage(tab,"json");}},
377
+ {sep:true},
378
+ {label:"Current query/filter · CSV",icon:"imp",onClick:function(){exportViaApi(tab,"current-query","csv");}},
379
+ {label:"Current query/filter · JSON",icon:"imp",onClick:function(){exportViaApi(tab,"current-query","json");}},
380
+ {sep:true},
381
+ {label:"Selected rows · CSV",icon:"imp",onClick:function(){exportSelected(tab,"csv");}},
382
+ {label:"Selected rows · JSON",icon:"imp",onClick:function(){exportSelected(tab,"json");}},
383
+ {sep:true},
384
+ {label:"Export custom…",icon:"gear",onClick:function(){exportCustomModal(tab);}}
385
+ ]);}
386
+ function exportCustomModal(tab){var g=tab.state.g;var cols=gridFields(g);
387
+ var src=el("select",{class:"select"});[["current-page","Current page"],["current-query","Current query/filter"],["selected-rows","Selected rows"],["whole-table","Whole table (can be large)"]].forEach(function(o){src.appendChild(el("option",{value:o[0],text:o[1]}));});
388
+ var fmt=el("select",{class:"select"});[["csv","CSV"],["json","JSON"]].forEach(function(f){fmt.appendChild(el("option",{value:f[0],text:f[1]}));});
389
+ var checks={};var list=el("div",{class:"fieldlist"});cols.forEach(function(c){var cb=el("input",{type:"checkbox"});cb.checked=true;checks[c]=cb;list.appendChild(el("label",{},[cb,c]));});
390
+ var warn=el("div",{class:"note"});src.addEventListener("change",function(){warn.textContent=src.value==="whole-table"?"Whole-table export can be large and may take a while.":"";});
391
+ var d=el("div",{class:"dialog"},[el("h3",{text:"Export data"}),
392
+ el("div",{class:"field"},[el("label",{text:"Source"}),src]),warn,
393
+ el("div",{class:"field"},[el("label",{text:"Columns"}),list]),
394
+ el("div",{class:"field"},[el("label",{text:"Format"}),fmt]),
395
+ el("div",{class:"row right"},[el("button",{class:"btn ghost",text:"Cancel",onclick:closeModal}),el("button",{class:"btn",text:"Export",onclick:function(){
396
+ var selectedColumns=cols.filter(function(c){return checks[c].checked;});if(!selectedColumns.length)return logMsg("Select at least one column.","warn");
397
+ var source=src.value,format=fmt.value;
398
+ if(source==="whole-table"&&!confirm("Export the whole table? This can be large."))return;
399
+ closeModal();
400
+ if(source==="current-page"){exportRowsToFile(selectedColumns,g.rows,format,exportFilename(g,format,"page")).then(function(){logMsg("Exported.","ok");});}
401
+ else if(source==="selected-rows"){var rows=g.rows.filter(function(r){return g.sel[rowKeyOf(g,r)];});if(!rows.length)return logMsg("No rows selected.","warn");exportRowsToFile(selectedColumns,rows,format,exportFilename(g,format,"selected")).then(function(){logMsg("Exported.","ok");});}
402
+ else{exportViaApi(tab,source,format,{selectedColumns:selectedColumns});}
403
+ }})])]);
404
+ openModal(d);}
405
+ function rowContextMenu(e,tab,row){var g=tab.state.g;showCtx(e.clientX,e.clientY,[
406
+ {label:"View row details",icon:"viw",onClick:function(){openCellViewer(row);}},
407
+ {label:"Copy row as JSON",icon:"col",onClick:function(){navigator.clipboard.writeText(JSON.stringify(row,null,2));logMsg("Copied row JSON","ok");}},
408
+ {label:"Copy INSERT statement",icon:"sql",onClick:function(){copyRowDml(g,row,"insert");}},
409
+ {label:"Copy UPDATE statement",icon:"sql",onClick:function(){copyRowDml(g,row,"update");}}
410
+ ]);}
411
+ function copyRowDml(g,row,kind){var fields=gridFields(g);var qn='"'+g.schema+'"."'+g.table+'"';function lit(v){return v==null?"NULL":typeof v==="number"?String(v):"'"+String(v).replace(/'/g,"''")+"'";}
412
+ var sql;if(kind==="insert"){sql="INSERT INTO "+qn+" ("+fields.map(function(f){return '"'+f+'"';}).join(", ")+")\\nVALUES ("+fields.map(function(f){return lit(row[f]);}).join(", ")+");";}
413
+ else{var setp=fields.filter(function(f){return g.pk.indexOf(f)<0;}).map(function(f){return '"'+f+'" = '+lit(row[f]);}).join(",\\n ");var wherep=(g.pk.length?g.pk:fields).map(function(f){return '"'+f+'" = '+lit(row[f]);}).join("\\n AND ");sql="UPDATE "+qn+"\\nSET\\n "+setp+"\\nWHERE\\n "+wherep+";";}
414
+ navigator.clipboard.writeText(sql);logMsg("Copied "+kind.toUpperCase()+" statement","ok");}
350
415
  function saveDataChanges(tab){var g=tab.state.g;if(S.readOnly)return logMsg("Read-only mode is on.","warn");
351
416
  var updates=Object.keys(g.edits).map(function(key){var row=g.rows.filter(function(r){return rowKeyOf(g,r)===key;})[0];var keyObj={};g.pk.forEach(function(k){keyObj[k]=row[k];});return {key:keyObj,changes:g.edits[key]};});
352
417
  var deletes=Object.keys(g.deletes).map(function(key){var row=g.rows.filter(function(r){return rowKeyOf(g,r)===key;})[0];var keyObj={};g.pk.forEach(function(k){keyObj[k]=row[k];});return {key:keyObj,_k:key};});
@@ -466,7 +531,7 @@ var PALETTE=[
466
531
  {label:"Focus Object Explorer",run:function(){var s=document.querySelector("#secTree .tsearch input");if(s)s.focus();else $("topSearch").focus();}},
467
532
  {label:"Toggle Read-only",run:function(){toggleReadOnly();}},
468
533
  {label:"Open Active Table Structure",run:function(){var t=tabById(S.activeTabId);if(t&&t.meta&&t.meta.table)openStructureTab(t.meta.schema,t.meta.table);else logMsg("No active table.","warn");}},
469
- {label:"Export Result CSV",run:function(){var t=tabById(S.activeTabId);if(t&&t.kind==="sql")exportResult(t,"csv");else if(t&&t.kind==="data")exportData(t,"csv");}},
534
+ {label:"Export Result CSV",run:function(){var t=tabById(S.activeTabId);if(t&&t.kind==="sql")exportResult(t,"csv");else if(t&&t.kind==="data")exportCurrentPage(t,"csv");}},
470
535
  {label:"Close Active Tab",run:function(){if(S.activeTabId)closeTab(S.activeTabId);}},
471
536
  {label:"Show Keyboard Shortcuts",run:function(){showShortcuts();}},
472
537
  {label:"Open Settings",run:function(){openSettings();}}
@@ -517,6 +582,7 @@ window.addEventListener("load",function(){
517
582
  $("querySearch").addEventListener("input",debounce(renderSavedQueries,200));wireSearch($("querySearch"),renderSavedQueries);
518
583
  $("topSearch").addEventListener("input",debounce(function(){$("connSearch").value=$("topSearch").value;renderConnections();},200));
519
584
  $("btnNewQuery").addEventListener("click",function(){if(!S.activeConnId)return logMsg("Select a connection first.","warn");openSqlTab();});
585
+ Array.prototype.forEach.call(document.querySelectorAll(".searchbox"),function(box){var inp=box.querySelector("input");if(!inp)return;var x=el("span",{class:"sbclr",html:svgFor("x"),title:"Clear (Esc)"});x.addEventListener("click",function(){inp.value="";inp.dispatchEvent(new Event("input"));inp.focus();});inp.addEventListener("input",function(){x.classList.toggle("show",!!inp.value);});box.appendChild(x);});
520
586
  setConnStatus("Ready","ok");
521
587
  openWelcome();
522
588
  loadSettings().then(function(){return loadConnections();}).then(function(){return restoreWorkspace();});
@@ -695,6 +695,53 @@ export async function startStudioServer(options: TStudioServerOptions = {}): Pro
695
695
  return;
696
696
  }
697
697
 
698
+ if (pathname === "/api/export/data" && method === "POST") {
699
+ const body = await readJsonBody(req);
700
+ const source = getString(body, "source");
701
+ const format = getString(body, "format") === "json" ? "json" : "csv";
702
+ const schema = getString(body, "schema");
703
+ const table = getString(body, "objectName");
704
+ const selectedColumns = Array.isArray(body.selectedColumns) ? (body.selectedColumns as string[]) : undefined;
705
+ let rows: Array<Record<string, unknown>> = [];
706
+ let fields: string[] = [];
707
+
708
+ if (source === "selected-rows" && Array.isArray(body.selectedRows)) {
709
+ rows = body.selectedRows as Array<Record<string, unknown>>;
710
+ fields = selectedColumns ?? (rows[0] ? Object.keys(rows[0]) : []);
711
+ } else {
712
+ const adapter = await pool.getAdapter(getString(body, "connectionId"));
713
+ const sort = Array.isArray(body.sort) ? (body.sort as TGridSortState[]) : [];
714
+ const isPage = source === "current-page";
715
+ const result = await adapter.getTableData({
716
+ schema,
717
+ table,
718
+ limit: isPage ? getNumber(body, "limit", 100) : 100000,
719
+ offset: isPage ? getNumber(body, "offset", 0) : 0,
720
+ where: source === "whole-table" ? undefined : getString(body, "whereClause") || undefined,
721
+ orderBy: sort[0]?.column,
722
+ orderDirection: sort[0]?.direction === "desc" ? "desc" : "asc",
723
+ });
724
+ rows = result.rows;
725
+ fields = selectedColumns ?? result.fields;
726
+ }
727
+
728
+ if (selectedColumns) {
729
+ rows = rows.map((row) => {
730
+ const picked: Record<string, unknown> = {};
731
+ for (const column of selectedColumns) picked[column] = row[column];
732
+ return picked;
733
+ });
734
+ fields = selectedColumns;
735
+ }
736
+
737
+ if (format === "json") {
738
+ sendText(res, JSON.stringify(rows, null, 2), "application/json; charset=utf-8", `${table || "result"}.json`);
739
+ } else {
740
+ sendText(res, toCsv(fields, rows), "text/csv; charset=utf-8", `${table || "result"}.csv`);
741
+ }
742
+ return;
743
+ }
744
+
698
745
  res.writeHead(404, { "content-type": "application/json; charset=utf-8" });
699
746
  res.end(JSON.stringify({ error: "Not found" }));
700
747
  };
@@ -61,6 +61,9 @@ svg.ic{width:15px;height:15px;vertical-align:-2px;fill:none;stroke:currentColor;
61
61
  .searchbox svg{color:var(--faint)}
62
62
  .searchbox input{flex:1;background:transparent;border:0;color:var(--text)}
63
63
  .searchbox .sbtn{background:transparent;border:0;color:var(--muted);cursor:pointer}
64
+ .searchbox .sbclr{color:var(--faint);cursor:pointer;display:none;flex:0 0 auto}
65
+ .searchbox .sbclr.show{display:inline-flex}
66
+ .searchbox .sbclr:hover{color:var(--text)}
64
67
 
65
68
  /* buttons + inputs */
66
69
  .btn{background:var(--accent);border:1px solid var(--accent-2);color:#fff;border-radius:8px;padding:6px 11px;cursor:pointer}
@@ -254,4 +257,28 @@ mark.hl{background:#facc15;color:#10151f;border-radius:3px;padding:0 1px}
254
257
  .ac .aci.sel,.ac .aci:hover{background:var(--accent);color:#fff}
255
258
  .ac .aci .t{color:var(--faint);font-size:11px}
256
259
  .toggle{display:flex;align-items:center;gap:8px;padding:6px 0}
260
+ /* compact data-grid toolbar */
261
+ .gtoolbar{display:flex;align-items:center;gap:6px;padding:7px 10px;border-bottom:1px solid var(--border);background:var(--bg-2);flex:0 0 auto}
262
+ .wherebox{display:flex;align-items:center;gap:6px;flex:1;min-width:120px;background:var(--bg-3);border:1px solid var(--border);border-radius:8px;padding:4px 9px}
263
+ .wherebox svg{color:var(--faint);flex:0 0 auto}
264
+ .wherebox input{flex:1;background:transparent;border:0;color:var(--text);font-family:Consolas,monospace}
265
+ .wherebox .clr{color:var(--faint);cursor:pointer;visibility:hidden;flex:0 0 auto}
266
+ .wherebox.has .clr{visibility:visible}
267
+ .wherebox .clr:hover{color:var(--text)}
268
+ .gbtn{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;background:#22304a;border:1px solid var(--border);border-radius:8px;color:#cfe0ff;cursor:pointer;flex:0 0 auto}
269
+ .gbtn:hover{background:#2a3b5a}
270
+ .gbtn.on{border-color:var(--accent);color:#fff;box-shadow:0 0 0 1px var(--accent) inset}
271
+ .gbtn.danger{color:#fca5a5}
272
+ .gbtn.danger:hover{background:#7f1d1d;color:#fff}
273
+ .gbtn:disabled{opacity:.4;cursor:not-allowed}
274
+ .gbtn:disabled:hover{background:#22304a}
275
+ .gbtn.spinning svg{animation:sp .7s linear infinite}
276
+ .gsep{width:1px;height:20px;background:var(--border);margin:0 2px;flex:0 0 auto}
277
+ .gridfoot{display:flex;align-items:center;gap:12px;padding:6px 10px;border-top:1px solid var(--border);background:var(--bg-2);color:var(--muted);font-size:12px;flex:0 0 auto}
278
+ .gridfoot select{background:var(--bg-3);border:1px solid var(--border);border-radius:7px;color:var(--text);padding:3px 7px}
279
+ .gridfoot .pg{display:inline-flex;align-items:center;gap:7px}
280
+ .cnt-u{color:var(--amber)}.cnt-i{color:var(--green)}.cnt-d{color:var(--red)}
281
+ table.grid tr.selrow td{background:var(--sel)}
282
+ .fieldlist{max-height:200px;overflow:auto;border:1px solid var(--border);border-radius:8px;padding:6px}
283
+ .fieldlist label{display:flex;gap:7px;align-items:center;padding:3px 4px;font-size:12.5px}
257
284
  `;
package/src/index.ts CHANGED
@@ -320,7 +320,7 @@ async function runInstallCommand(options: TInstallCommandOptions): Promise<void>
320
320
  process.exitCode = installResult.exitCode;
321
321
  }
322
322
 
323
- program.name("simplemdg").description("SimpleMDG local development helper").version("2.6.0");
323
+ program.name("simplemdg").description("SimpleMDG local development helper").version("2.7.0");
324
324
 
325
325
 
326
326
  program