simplemdg-dev-cli 2.5.0 → 2.6.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.
Files changed (69) hide show
  1. package/README.md +33 -0
  2. package/USER_GUIDE.md +57 -0
  3. package/dist/commands/cache.command.d.ts +2 -0
  4. package/dist/commands/cache.command.js +129 -0
  5. package/dist/commands/cache.command.js.map +1 -0
  6. package/dist/commands/cf.command.js +201 -122
  7. package/dist/commands/cf.command.js.map +1 -1
  8. package/dist/commands/gitlab.command.js +33 -23
  9. package/dist/commands/gitlab.command.js.map +1 -1
  10. package/dist/core/cache/smart-cache-events.d.ts +3 -0
  11. package/dist/core/cache/smart-cache-events.js +20 -0
  12. package/dist/core/cache/smart-cache-events.js.map +1 -0
  13. package/dist/core/cache/smart-cache-manager.d.ts +20 -0
  14. package/dist/core/cache/smart-cache-manager.js +148 -0
  15. package/dist/core/cache/smart-cache-manager.js.map +1 -0
  16. package/dist/core/cache/smart-cache-store.d.ts +8 -0
  17. package/dist/core/cache/smart-cache-store.js +74 -0
  18. package/dist/core/cache/smart-cache-store.js.map +1 -0
  19. package/dist/core/cache/smart-cache.d.ts +18 -0
  20. package/dist/core/cache/smart-cache.js +117 -0
  21. package/dist/core/cache/smart-cache.js.map +1 -0
  22. package/dist/core/cache/smart-cache.types.d.ts +62 -0
  23. package/dist/core/cache/smart-cache.types.js +17 -0
  24. package/dist/core/cache/smart-cache.types.js.map +1 -0
  25. package/dist/core/cf/cf-target-cache.d.ts +7 -0
  26. package/dist/core/cf/cf-target-cache.js +58 -0
  27. package/dist/core/cf/cf-target-cache.js.map +1 -0
  28. package/dist/core/cf/cf-target.types.d.ts +11 -0
  29. package/dist/core/cf/cf-target.types.js +11 -0
  30. package/dist/core/cf/cf-target.types.js.map +1 -0
  31. package/dist/core/db/db-studio-client.d.ts +1 -1
  32. package/dist/core/db/db-studio-client.js +173 -44
  33. package/dist/core/db/db-studio-client.js.map +1 -1
  34. package/dist/core/db/db-studio-server.js +125 -0
  35. package/dist/core/db/db-studio-server.js.map +1 -1
  36. package/dist/core/db/db-studio-styles.d.ts +1 -1
  37. package/dist/core/db/db-studio-styles.js +36 -0
  38. package/dist/core/db/db-studio-styles.js.map +1 -1
  39. package/dist/core/db/db-types.d.ts +54 -0
  40. package/dist/core/db/studio/sql-formatter.d.ts +25 -0
  41. package/dist/core/db/studio/sql-formatter.js +139 -0
  42. package/dist/core/db/studio/sql-formatter.js.map +1 -0
  43. package/dist/core/db/studio/studio-settings.d.ts +4 -0
  44. package/dist/core/db/studio/studio-settings.js +39 -0
  45. package/dist/core/db/studio/studio-settings.js.map +1 -0
  46. package/dist/core/db/studio/workspace-cache.d.ts +3 -0
  47. package/dist/core/db/studio/workspace-cache.js +51 -0
  48. package/dist/core/db/studio/workspace-cache.js.map +1 -0
  49. package/dist/index.js +3 -1
  50. package/dist/index.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/commands/cache.command.ts +159 -0
  53. package/src/commands/cf.command.ts +232 -129
  54. package/src/commands/gitlab.command.ts +37 -21
  55. package/src/core/cache/smart-cache-events.ts +20 -0
  56. package/src/core/cache/smart-cache-manager.ts +169 -0
  57. package/src/core/cache/smart-cache-store.ts +83 -0
  58. package/src/core/cache/smart-cache.ts +97 -0
  59. package/src/core/cache/smart-cache.types.ts +79 -0
  60. package/src/core/cf/cf-target-cache.ts +61 -0
  61. package/src/core/cf/cf-target.types.ts +17 -0
  62. package/src/core/db/db-studio-client.ts +173 -44
  63. package/src/core/db/db-studio-server.ts +109 -1
  64. package/src/core/db/db-studio-styles.ts +36 -0
  65. package/src/core/db/db-types.ts +61 -0
  66. package/src/core/db/studio/sql-formatter.ts +139 -0
  67. package/src/core/db/studio/studio-settings.ts +36 -0
  68. package/src/core/db/studio/workspace-cache.ts +51 -0
  69. package/src/index.ts +3 -1
@@ -39,6 +39,8 @@ function $(id){return document.getElementById(id);}
39
39
  function el(tag,attrs,kids){var n=document.createElement(tag);if(attrs)for(var k in attrs){var v=attrs[k];if(v==null)continue;if(k==="class")n.className=v;else if(k==="text")n.textContent=v;else if(k==="html")n.innerHTML=v;else if(k.slice(0,2)==="on"&&typeof v==="function")n.addEventListener(k.slice(2),v);else n.setAttribute(k,v);}if(kids!=null){(Array.isArray(kids)?kids:[kids]).forEach(function(c){if(c==null)return;n.appendChild(typeof c==="string"||typeof c==="number"?document.createTextNode(String(c)):c);});}return n;}
40
40
  function clear(n){while(n&&n.firstChild)n.removeChild(n.firstChild);return n;}
41
41
  function esc(v){return String(v==null?"":v).replace(/[&<>"']/g,function(s){return {"&":"&amp;","<":"&lt;",">":"&gt;","\\"":"&quot;","'":"&#39;"}[s];});}
42
+ function highlightMatch(text,search){var t=String(text==null?"":text);var s=String(search==null?"":search).trim();if(!s)return esc(t);var lt=t.toLowerCase(),ls=s.toLowerCase(),out="",i=0,idx;while((idx=lt.indexOf(ls,i))>=0){out+=esc(t.slice(i,idx))+'<mark class="hl">'+esc(t.slice(idx,idx+s.length))+'</mark>';i=idx+s.length;}out+=esc(t.slice(i));return out;}
43
+ function wireSearch(input,onRun,onClear){input.addEventListener("keydown",function(e){if(e.key==="Enter"){e.preventDefault();onRun();}else if(e.key==="Escape"){e.preventDefault();input.value="";if(onClear)onClear();else onRun();}});}
42
44
  function debounce(fn,ms){var t;return function(){var a=arguments,c=this;clearTimeout(t);t=setTimeout(function(){fn.apply(c,a);},ms||220);};}
43
45
  function topSpin(on){$("topSpin").className=on?"spin":"spin hidden";}
44
46
 
@@ -53,7 +55,7 @@ function setConnStatus(text,kind){$("stConn").innerHTML="";$("stConn").appendChi
53
55
  function setRun(on){$("stConn").firstChild.className="st-dot "+(on?"run":"ok");}
54
56
 
55
57
  /* ---------- global state ---------- */
56
- var S = { connections:[], activeConnId:"", connType:"", activeSchema:"", readOnly:RO_DEFAULT, tabs:[], activeTabId:"", seq:0, savedQueries:[] };
58
+ var S = { connections:[], activeConnId:"", connType:"", activeSchema:"", readOnly:RO_DEFAULT, tabs:[], activeTabId:"", seq:0, savedQueries:[], settings:{ restoreWorkspace:true, defaultRowLimit:100, defaultSchema:"", readOnlyByDefault:RO_DEFAULT, queryTimeoutMs:30000, autoFormatGeneratedSql:true, autoSaveDelayMs:500, maxHistoryItems:300, showProductionWarning:true, theme:"dark" } };
57
59
  function activeConn(){return S.connections.filter(function(c){return c.id===S.activeConnId;})[0];}
58
60
 
59
61
  /* ====================================================================
@@ -74,10 +76,10 @@ function closeModal(){$("modalRoot").classList.add("hidden");clear($("modalRoot"
74
76
  CONNECTIONS
75
77
  ==================================================================== */
76
78
  function loadConnections(){var box=$("connList");box.innerHTML='<div class="skel"></div><div class="skel"></div>';return api("GET","/api/connections").then(function(r){S.connections=r.connections||[];renderConnections();}).catch(function(e){box.innerHTML='<div class="empty">'+esc(e.message)+'</div>';});}
77
- function renderConnections(){var q=($("connSearch").value||"").toLowerCase();var box=clear($("connList"));var rows=S.connections.filter(function(c){return (c.name+" "+c.type+" "+(c.org||"")+" "+(c.app||"")+" "+(c.environment||"")).toLowerCase().indexOf(q)>=0;});rows.sort(function(a,b){return (b.isFavorite?1:0)-(a.isFavorite?1:0);});if(!rows.length){box.appendChild(el("div",{class:"empty",text:S.connections.length?"No match.":"No connections yet. Click + New or Import."}));return;}rows.forEach(function(c){box.appendChild(connCard(c));});}
78
- function connCard(c){var color=c.color||ENV_COLORS[c.environment]||"#64748b";var card=el("div",{class:"conn-card"+(c.id===S.activeConnId?" active":""),style:"border-left-color:"+color,oncontextmenu:function(e){e.preventDefault();connMenu(e,c);}});card.addEventListener("click",function(){activateConnection(c.id);});
79
+ function renderConnections(){var raw=($("connSearch").value||"");var q=raw.toLowerCase();var box=clear($("connList"));var rows=S.connections.filter(function(c){return (c.name+" "+c.type+" "+(c.org||"")+" "+(c.app||"")+" "+(c.environment||"")).toLowerCase().indexOf(q)>=0;});rows.sort(function(a,b){return (b.isFavorite?1:0)-(a.isFavorite?1:0);});if(!rows.length){box.appendChild(el("div",{class:"empty",text:S.connections.length?"No results found":"No connections yet. Click + New or Import."}));return;}rows.forEach(function(c){box.appendChild(connCard(c,raw));});}
80
+ function connCard(c,q){var color=c.color||ENV_COLORS[c.environment]||"#64748b";var card=el("div",{class:"conn-card"+(c.id===S.activeConnId?" active":""),style:"border-left-color:"+color,oncontextmenu:function(e){e.preventDefault();connMenu(e,c);}});card.addEventListener("click",function(){activateConnection(c.id);});
79
81
  var star=el("span",{class:"star"+(c.isFavorite?" on":""),title:"Favorite",onclick:function(e){e.stopPropagation();toggleFavorite(c);}});star.innerHTML=svgFor("star");
80
- card.appendChild(el("div",{class:"conn-top"},[icEl("db","db"),el("span",{class:"conn-name",text:c.name,title:c.name}),star]));
82
+ card.appendChild(el("div",{class:"conn-top"},[icEl("db","db"),el("span",{class:"conn-name",html:highlightMatch(c.name,q),title:c.name}),star]));
81
83
  var sub=[c.org,c.space].filter(Boolean).join(" / ")||c.host;
82
84
  card.appendChild(el("div",{class:"conn-sub",text:sub,title:sub}));
83
85
  var tags=el("div",{class:"conn-tags"});
@@ -113,7 +115,7 @@ function editConnModal(c){var sel={color:c.color||"",env:c.environment||""};var
113
115
  ==================================================================== */
114
116
  function treeNode(opts){
115
117
  var chev=el("span",{class:"tchev"+(opts.leaf?" leaf":""),html:"\\u203a"});
116
- var label=el("span",{class:"tlabel",text:opts.label,title:opts.label});
118
+ var label=el("span",{class:"tlabel",title:opts.label});if(opts.labelHtml)label.innerHTML=opts.labelHtml;else label.textContent=opts.label;
117
119
  var badge=el("span",{class:"tbadge"});
118
120
  var spin=el("span",{class:"hidden"});
119
121
  var row=el("div",{class:"trow"},[chev,icEl(opts.icon,opts.iconCls),label,badge,spin]);
@@ -153,11 +155,11 @@ function folderNode(schema,label,kind,iconCls){return treeNode({label:label,icon
153
155
  var search=el("input",{class:"input",placeholder:"Search "+label.toLowerCase()+"..."});
154
156
  var sb=el("div",{class:"searchbox tsearch"},[el("span",{html:svgFor("search")}),search]);
155
157
  kids.appendChild(sb);kids.appendChild(listBox);
156
- var run=function(){var sp=el("span",{class:"spin"});clear(listBox).appendChild(el("div",{class:"tnote"},[sp," loading..."]));api("GET","/api/catalog/objects?"+qstr({connectionId:S.activeConnId,schema:schema,kinds:kind,search:search.value||""})).then(function(r){var objs=r.objects||[];setBadge(objs.length);clear(listBox);if(!objs.length){listBox.appendChild(el("div",{class:"tnote",text:"None."}));return;}objs.forEach(function(o){listBox.appendChild(objectNode(schema,o,iconCls));});}).catch(function(e){clear(listBox).appendChild(el("div",{class:"tnote",text:e.message}));});};
157
- search.addEventListener("input",debounce(run,250));
158
+ var run=function(){var sp=el("span",{class:"spin"});clear(listBox).appendChild(el("div",{class:"tnote"},[sp," loading..."]));var q=search.value||"";api("GET","/api/catalog/objects?"+qstr({connectionId:S.activeConnId,schema:schema,kinds:kind,search:q})).then(function(r){var objs=r.objects||[];setBadge(objs.length);clear(listBox);if(!objs.length){listBox.appendChild(el("div",{class:"tnote",text:q?"No results found":"None."}));return;}objs.forEach(function(o){listBox.appendChild(objectNode(schema,o,iconCls,q));});}).catch(function(e){clear(listBox).appendChild(el("div",{class:"tnote",text:e.message}));});};
159
+ search.addEventListener("input",debounce(run,250));wireSearch(search,run);
158
160
  run();
159
161
  }});}
160
- function objectNode(schema,o,iconCls){var canData=o.kind==="table"||o.kind==="view"||o.kind==="column-view";return treeNode({label:o.name,icon:iconCls,iconCls:iconCls,leaf:true,
162
+ function objectNode(schema,o,iconCls,q){var canData=o.kind==="table"||o.kind==="view"||o.kind==="column-view";return treeNode({label:o.name,labelHtml:highlightMatch(o.name,q),icon:iconCls,iconCls:iconCls,leaf:true,
161
163
  onClick:function(){selectTreeRow(this);},
162
164
  onDblClick:canData?function(){openDataTab(schema,o.name);}:null,
163
165
  onMenu:canData?function(e){objectMenu(e,schema,o);}:function(e){objectMenu(e,schema,o,true);}
@@ -166,19 +168,64 @@ var _selRow=null;
166
168
  function selectTreeRow(node){if(_selRow)_selRow._row.classList.remove("sel");_selRow=node;node._row.classList.add("sel");}
167
169
  function objectMenu(e,schema,o,limited){var qn='"'+schema+'"."'+o.name+'"';var items=[];if(!limited){items.push({label:"Open Data",icon:"table2",onClick:function(){openDataTab(schema,o.name);}});items.push({label:"Open Structure",icon:"col",onClick:function(){openStructureTab(schema,o.name);}});items.push({sep:true});items.push({label:"Generate SELECT",icon:"sql",onClick:function(){api("POST","/api/table/sql",{connectionId:S.activeConnId,schema:schema,table:o.name,limit:100}).then(function(r){openSqlTab(r.select,o.name);});}});items.push({label:"Generate COUNT",icon:"sql",onClick:function(){api("POST","/api/table/sql",{connectionId:S.activeConnId,schema:schema,table:o.name}).then(function(r){openSqlTab(r.count,o.name);});}});}
168
170
  items.push({label:"Copy Full Name",icon:"col",onClick:function(){navigator.clipboard.writeText(qn);logMsg("Copied "+qn,"ok");}});
171
+ items.push({label:"Copy Name",icon:"col",onClick:function(){navigator.clipboard.writeText(o.name);logMsg("Copied "+o.name,"ok");}});
172
+ if(!limited){items.push({sep:true});
173
+ items.push({label:"Copy SELECT",icon:"sql",onClick:function(){api("POST","/api/table/generate-sql",{connectionId:S.activeConnId,schema:schema,table:o.name,limit:100}).then(function(r){navigator.clipboard.writeText(r.select);logMsg("Copied SELECT","ok");}).catch(function(er){logMsg(er.message,"err");});}});
174
+ items.push({label:"Copy INSERT template",icon:"sql",onClick:function(){api("POST","/api/table/generate-sql",{connectionId:S.activeConnId,schema:schema,table:o.name}).then(function(r){navigator.clipboard.writeText(r.insert);logMsg("Copied INSERT template","ok");}).catch(function(er){logMsg(er.message,"err");});}});
175
+ items.push({label:"Copy UPDATE template",icon:"sql",onClick:function(){api("POST","/api/table/generate-sql",{connectionId:S.activeConnId,schema:schema,table:o.name}).then(function(r){navigator.clipboard.writeText(r.update);logMsg("Copied UPDATE template","ok");}).catch(function(er){logMsg(er.message,"err");});}});
176
+ }
169
177
  showCtx(e.clientX,e.clientY,items);}
170
178
 
171
179
  /* ====================================================================
172
180
  WORKSPACE TABS
173
181
  ==================================================================== */
174
- function renderTabBar(){var bar=clear($("tabbar"));S.tabs.forEach(function(tab){var chip=el("div",{class:"wtab"+(tab.id===S.activeTabId?" active":""),onclick:function(){switchTab(tab.id);}});chip.appendChild(el("span",{class:"t-ico",html:svgFor(tab.icon||"sql")}));chip.appendChild(el("span",{class:"t-title",text:tab.title,title:tab.title}));if(tab.dirty)chip.appendChild(el("span",{class:"dot"}));if(tab.closable!==false)chip.appendChild(el("span",{class:"x",html:svgFor("x"),onclick:function(e){e.stopPropagation();closeTab(tab.id);}}));bar.appendChild(chip);});}
175
- function openTab(spec){var ex=S.tabs.filter(function(t){return t.key===spec.key;})[0];if(ex){switchTab(ex.id);return ex;}var id="wt"+(++S.seq);var pane=el("div",{class:"tabpane hidden"});$("tabcontent").appendChild(pane);var tab={id:id,key:spec.key,kind:spec.kind,title:spec.title,icon:spec.icon,dirty:false,closable:spec.closable!==false,el:pane,state:{}};S.tabs.push(tab);spec.build(pane,tab);renderTabBar();switchTab(id);return tab;}
176
- function switchTab(id){S.activeTabId=id;S.tabs.forEach(function(t){t.el.classList.toggle("hidden",t.id!==id);});renderTabBar();var tab=tabById(id);if(tab&&tab.onShow)tab.onShow();updatePendingStatus();}
182
+ var _dragTabId=null;
183
+ function orderedTabs(){return S.tabs.slice().sort(function(a,b){return (b.pinned?1:0)-(a.pinned?1:0);});}
184
+ function renderTabBar(){var bar=clear($("tabbar"));orderedTabs().forEach(function(tab){var chip=el("div",{class:"wtab"+(tab.id===S.activeTabId?" active":"")+(tab.pinned?" pinned":""),draggable:"true"});chip.addEventListener("click",function(){switchTab(tab.id);});chip.addEventListener("contextmenu",function(e){e.preventDefault();tabMenu(e,tab);});
185
+ chip.addEventListener("dragstart",function(e){_dragTabId=tab.id;chip.classList.add("dragging");if(e.dataTransfer)e.dataTransfer.effectAllowed="move";});
186
+ chip.addEventListener("dragend",function(){chip.classList.remove("dragging");Array.prototype.forEach.call(bar.children,function(x){x.classList.remove("dragover");});});
187
+ chip.addEventListener("dragover",function(e){e.preventDefault();chip.classList.add("dragover");});
188
+ chip.addEventListener("dragleave",function(){chip.classList.remove("dragover");});
189
+ chip.addEventListener("drop",function(e){e.preventDefault();chip.classList.remove("dragover");if(_dragTabId&&_dragTabId!==tab.id)reorderTab(_dragTabId,tab.id);});
190
+ chip.appendChild(el("span",{class:"t-ico"+(tab.pinned?" pin":""),html:svgFor(tab.pinned?"star":(tab.icon||"sql"))}));
191
+ chip.appendChild(el("span",{class:"t-title",text:tab.title,title:tab.title}));
192
+ if(tab.dirty)chip.appendChild(el("span",{class:"dot"}));
193
+ if(tab.closable!==false)chip.appendChild(el("span",{class:"x",html:svgFor("x"),onclick:function(e){e.stopPropagation();closeTab(tab.id);}}));
194
+ bar.appendChild(chip);});}
195
+ function reorderTab(srcId,targetId){var src=tabById(srcId);var ti=-1;for(var i=0;i<S.tabs.length;i++)if(S.tabs[i].id===targetId)ti=i;if(!src||ti<0)return;S.tabs=S.tabs.filter(function(t){return t.id!==srcId;});var idx=-1;for(var j=0;j<S.tabs.length;j++)if(S.tabs[j].id===targetId)idx=j;S.tabs.splice(idx,0,src);renderTabBar();scheduleWorkspaceSave();}
196
+ function tabMenu(e,tab){showCtx(e.clientX,e.clientY,[
197
+ {label:"Close",icon:"x",onClick:function(){closeTab(tab.id);}},
198
+ {label:"Close Others",icon:"x",onClick:function(){closeOtherTabs(tab.id);}},
199
+ {label:"Close Tabs to the Right",icon:"x",onClick:function(){closeTabsToRight(tab.id);}},
200
+ {sep:true},
201
+ {label:tab.pinned?"Unpin Tab":"Pin Tab",icon:"star",onClick:function(){tab.pinned=!tab.pinned;renderTabBar();scheduleWorkspaceSave();}},
202
+ {label:"Rename Tab",icon:"gear",onClick:function(){var n=prompt("Tab name",tab.title);if(n){tab.title=n;renderTabBar();scheduleWorkspaceSave();}}},
203
+ {label:"Duplicate Tab",icon:"plus",onClick:function(){duplicateTab(tab);}}
204
+ ]);}
205
+ function duplicateTab(tab){if(tab.kind==="sql"&&tab.state.editor)openSqlTab(tab.state.editor.value,tab.title);else if(tab.kind==="data"&&tab.meta)openDataTab(tab.meta.schema,tab.meta.table);else if(tab.kind==="structure"&&tab.meta)openStructureTab(tab.meta.schema,tab.meta.table);}
206
+ function closeOtherTabs(keepId){var others=S.tabs.filter(function(t){return t.id!==keepId&&t.closable!==false;});if(others.some(function(t){return t.dirty;})&&!confirm("Close other tabs? Unsaved changes will be lost."))return;others.forEach(function(t){t.el.remove();});S.tabs=S.tabs.filter(function(t){return t.id===keepId||t.closable===false;});switchTab(keepId);renderTabBar();scheduleWorkspaceSave();}
207
+ function closeTabsToRight(fromId){var ord=orderedTabs();var i=0;for(var k=0;k<ord.length;k++)if(ord[k].id===fromId)i=k;var toClose=ord.slice(i+1).filter(function(t){return t.closable!==false;});if(toClose.some(function(t){return t.dirty;})&&!confirm("Close tabs to the right? Unsaved changes will be lost."))return;var ids={};toClose.forEach(function(t){ids[t.id]=1;t.el.remove();});S.tabs=S.tabs.filter(function(t){return !ids[t.id];});if(ids[S.activeTabId])switchTab(fromId);renderTabBar();scheduleWorkspaceSave();}
208
+ function nextTab(dir){var ord=orderedTabs();if(ord.length<2)return;var i=0;for(var k=0;k<ord.length;k++)if(ord[k].id===S.activeTabId)i=k;var ni=(i+dir+ord.length)%ord.length;switchTab(ord[ni].id);}
209
+ function openTab(spec){var ex=S.tabs.filter(function(t){return t.key===spec.key;})[0];if(ex){switchTab(ex.id);return ex;}var id=spec.restoreId||("wt"+(++S.seq));var pane=el("div",{class:"tabpane hidden"});$("tabcontent").appendChild(pane);var now=new Date().toISOString();var tab={id:id,key:spec.key,kind:spec.kind,title:spec.title,icon:spec.icon,dirty:false,closable:spec.closable!==false,el:pane,state:{},connectionId:spec.connectionId||"",meta:spec.meta||null,pinned:!!spec.pinned,openedAt:now};S.tabs.push(tab);spec.build(pane,tab);renderTabBar();switchTab(id);scheduleWorkspaceSave();return tab;}
210
+ function switchTab(id){S.activeTabId=id;S.tabs.forEach(function(t){t.el.classList.toggle("hidden",t.id!==id);});renderTabBar();var tab=tabById(id);if(tab&&tab.onShow)tab.onShow();updatePendingStatus();scheduleWorkspaceSave();}
177
211
  function tabById(id){return S.tabs.filter(function(t){return t.id===id;})[0];}
178
212
  function setDirty(tab,on){tab.dirty=on;renderTabBar();updatePendingStatus();}
179
- function closeTab(id){var tab=tabById(id);if(!tab)return;if(tab.dirty&&!confirm("'"+tab.title+"' has unsaved changes. Close anyway?"))return;tab.el.remove();var idx=S.tabs.indexOf(tab);S.tabs=S.tabs.filter(function(t){return t.id!==id;});if(S.activeTabId===id){var next=S.tabs[Math.max(0,idx-1)];if(next)switchTab(next.id);else openWelcome();}renderTabBar();}
213
+ function closeTab(id){var tab=tabById(id);if(!tab)return;if(tab.dirty&&!confirm("'"+tab.title+"' has unsaved changes. Close anyway?"))return;tab.el.remove();var idx=S.tabs.indexOf(tab);S.tabs=S.tabs.filter(function(t){return t.id!==id;});if(S.activeTabId===id){var next=S.tabs[Math.max(0,idx-1)];if(next)switchTab(next.id);else openWelcome();}renderTabBar();scheduleWorkspaceSave();}
180
214
  function updatePendingStatus(){var tab=tabById(S.activeTabId);var n=tab&&tab.state&&tab.state.g?pendingCount(tab.state.g):0;$("stPending").textContent=n>0?(n+" pending change"+(n>1?"s":"")):"";$("stPending").className=n>0?"st-item st-pending":"st-item";}
181
215
 
216
+ /* ---- workspace persistence ---- */
217
+ function kindToType(k){return k==="data"?"data-grid":k==="structure"?"metadata":k;}
218
+ function serializeWorkspace(){var tabs=S.tabs.filter(function(t){return t.kind!=="welcome";}).map(function(t){var st={id:t.id,type:kindToType(t.kind),title:t.title,pinned:!!t.pinned,dirty:!!t.dirty,connectionId:t.connectionId||undefined,openedAt:t.openedAt||new Date().toISOString(),updatedAt:new Date().toISOString()};if(t.meta){st.schema=t.meta.schema;st.objectName=t.meta.table;st.objectType=t.meta.objectType;}if(t.kind==="sql"&&t.state.editor)st.sql=t.state.editor.value;if(t.kind==="data"&&t.state.g){var g=t.state.g;st.filter=g.whereI.value;st.pageSize=parseInt(g.pageSel.value,10);st.pageIndex=g.offset;st.sort=g.orderBy?[{column:g.orderBy,direction:g.orderDir}]:[];}return st;});return {version:1,activeTabId:S.activeTabId,tabs:tabs,tabGroups:[],layout:{readOnly:S.readOnly,sidebarWidth:$("sidebar").offsetWidth},updatedAt:new Date().toISOString()};}
219
+ var _wsTimer=null;
220
+ function scheduleWorkspaceSave(){if(_wsTimer)clearTimeout(_wsTimer);_wsTimer=setTimeout(function(){api("PUT","/api/studio/workspace",serializeWorkspace()).catch(function(){});},Math.max(400,S.settings.autoSaveDelayMs||500));}
221
+ function loadSettings(){return api("GET","/api/studio/settings").then(function(r){if(r.settings)S.settings=r.settings;}).catch(function(){});}
222
+ function restoreWorkspace(){if(!S.settings.restoreWorkspace)return Promise.resolve();return api("GET","/api/studio/workspace").then(function(r){var ws=r.workspace;if(!ws||!ws.tabs||!ws.tabs.length)return;var active=null;ws.tabs.forEach(function(st){try{
223
+ if(st.type==="sql"){if(st.connectionId){var c=byId(st.connectionId);if(c){S.activeConnId=c.id;S.connType=c.type;}}var t=openSqlTab(st.sql||"",null);t.title=st.title;t.pinned=!!st.pinned;if(st.id===ws.activeTabId)active=t.id;}
224
+ else if(st.type==="data-grid"){var cd=byId(st.connectionId);if(!cd){logMsg("Tab '"+st.title+"' not restored: connection removed.","warn");return;}S.activeConnId=cd.id;S.connType=cd.type;var dt=openDataTab(st.schema,st.objectName,{where:st.filter,pageSize:st.pageSize,sort:st.sort,offset:st.pageIndex});dt.pinned=!!st.pinned;if(st.id===ws.activeTabId)active=dt.id;}
225
+ else if(st.type==="metadata"){var cm=byId(st.connectionId);if(!cm)return;S.activeConnId=cm.id;S.connType=cm.type;var mt=openStructureTab(st.schema,st.objectName);mt.pinned=!!st.pinned;if(st.id===ws.activeTabId)active=mt.id;}
226
+ }catch(e){}});updateTopBadges();renderTabBar();if(active)switchTab(active);logMsg("Workspace restored ("+ws.tabs.length+" tab"+(ws.tabs.length>1?"s":"")+").","ok");}).catch(function(){});}
227
+ function byId(id){return S.connections.filter(function(c){return c.id===id;})[0];}
228
+
182
229
  /* ====================================================================
183
230
  WELCOME
184
231
  ==================================================================== */
@@ -202,20 +249,29 @@ function wcard(icon,title,desc,onClick){return el("div",{class:"wcard",onclick:o
202
249
  SQL CONSOLE TAB
203
250
  ==================================================================== */
204
251
  var DANGER=/\\b(drop|truncate|alter|grant|revoke)\\b/i;
205
- function openSqlTab(sql,nameHint){var title="SQL"+(nameHint?": "+nameHint:" Console");openTab({key:"sql:"+(++S.seq),kind:"sql",title:title,icon:"sql",build:function(pane,tab){buildSqlPane(pane,tab,sql||"select * from DUMMY");}});}
252
+ function openSqlTab(sql,nameHint,queryId){var title="SQL"+(nameHint?": "+nameHint:" Console");return openTab({key:"sql:"+(++S.seq),kind:"sql",title:title,icon:"sql",connectionId:S.activeConnId,meta:{queryId:queryId||null},build:function(pane,tab){tab.connectionId=S.activeConnId;buildSqlPane(pane,tab,sql!=null?sql:"select * from DUMMY");}});}
206
253
  function buildSqlPane(pane,tab,initialSql){
207
254
  var editor=el("textarea",{class:"editor",spellcheck:"false"});editor.value=initialSql;tab.state.editor=editor;
208
- editor.addEventListener("input",function(){setDirty(tab,true);});
209
- editor.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&e.key==="Enter"){e.preventDefault();runSqlTab(tab);}});
210
- var limitSel=el("select",{class:"select",style:"width:auto"});["100","500","1000","5000","0"].forEach(function(v){limitSel.appendChild(el("option",{value:v,text:v==="0"?"No limit":v}));});tab.state.limit=limitSel;
211
- var runBtn=el("button",{class:"btn",onclick:function(){runSqlTab(tab);}},[el("span",{html:svgFor("run")})," Run"]);tab.state.runBtn=runBtn;
212
- var tb=el("div",{class:"toolbar"},[runBtn,el("button",{class:"btn sec",text:"Format",onclick:function(){editor.value=formatSql(editor.value);}}),el("button",{class:"btn ghost",text:"Explain",onclick:function(){explainSqlTab(tab);}}),el("span",{class:"note",text:"Limit"}),limitSel,el("button",{class:"btn ghost",text:"Save",onclick:function(){saveQueryTab(tab);}}),el("button",{class:"btn ghost",text:"CSV",onclick:function(){exportResult(tab,"csv");}}),el("button",{class:"btn ghost",text:"JSON",onclick:function(){exportResult(tab,"json");}}),el("span",{class:"grow"}),el("span",{class:"note",id:"sqlmeta_"+tab.id})]);
255
+ var gutter=el("div",{class:"gutter",text:"1"});
256
+ function syncGutter(){var lines=editor.value.split("\\n").length;var s="";for(var i=1;i<=lines;i++)s+=i+"\\n";gutter.textContent=s;gutter.scrollTop=editor.scrollTop;}
257
+ tab.state.syncGutter=syncGutter;
258
+ editor.addEventListener("input",function(){setDirty(tab,true);syncGutter();scheduleWorkspaceSave();});
259
+ editor.addEventListener("scroll",function(){gutter.scrollTop=editor.scrollTop;});
260
+ editor.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&e.key==="Enter"){e.preventDefault();runMode(tab,"selected");}else if(e.key==="F5"){e.preventDefault();runMode(tab,"all");}else if((e.ctrlKey||e.metaKey)&&(e.key==="s"||e.key==="S")){e.preventDefault();saveQueryTab(tab);}});
261
+ var editwrap=el("div",{class:"editwrap"},[gutter,editor]);
262
+ var limitSel=el("select",{class:"select",style:"width:auto"});["100","500","1000","5000","0"].forEach(function(v){limitSel.appendChild(el("option",{value:v,text:v==="0"?"No limit":v}));});limitSel.value=String(S.settings.defaultRowLimit||100);tab.state.limit=limitSel;
263
+ var runBtn=el("button",{class:"btn",onclick:function(){runMode(tab,"selected");}},[el("span",{html:svgFor("run")})," Run"]);tab.state.runBtn=runBtn;
264
+ var runMenu=el("button",{class:"btn",style:"padding:6px 7px",html:"\\u25be",title:"Run options",onclick:function(e){showCtx(e.clientX,e.clientY,[{label:"Run Selected (Ctrl+Enter)",icon:"run",onClick:function(){runMode(tab,"selected");}},{label:"Run Current Statement",icon:"run",onClick:function(){runMode(tab,"current");}},{label:"Run All (F5)",icon:"run",onClick:function(){runMode(tab,"all");}},{label:"Explain",icon:"sql",onClick:function(){runMode(tab,"explain");}}]);}});
265
+ var tb=el("div",{class:"toolbar"},[runBtn,runMenu,el("button",{class:"btn sec",text:"Format",onclick:function(){api("POST","/api/sql/format",{sql:editor.value}).then(function(r){setEditorValue(tab,r.sql);}).catch(function(){setEditorValue(tab,formatSql(editor.value));});}}),el("span",{class:"note",text:"Limit"}),limitSel,el("button",{class:"btn ghost",text:"Save",title:"Ctrl+S",onclick:function(){saveQueryTab(tab);}}),el("button",{class:"btn ghost",text:"CSV",onclick:function(){exportResult(tab,"csv");}}),el("button",{class:"btn ghost",text:"JSON",onclick:function(){exportResult(tab,"json");}}),el("span",{class:"grow"}),el("span",{class:"note",id:"sqlmeta_"+tab.id})]);
213
266
  var body=el("div",{class:"pane-body"});var errBox=el("div",{class:"errbox hidden"});var grid=el("div",{class:"gridwrap"});tab.state.err=errBox;tab.state.grid=grid;
214
- body.appendChild(editor);body.appendChild(errBox);body.appendChild(el("div",{class:"note",text:"Result"}));body.appendChild(grid);
215
- pane.appendChild(tb);pane.appendChild(body);
267
+ body.appendChild(editwrap);body.appendChild(errBox);body.appendChild(el("div",{class:"note",text:"Result"}));body.appendChild(grid);
268
+ pane.appendChild(tb);pane.appendChild(body);syncGutter();
216
269
  }
217
- function currentSqlText(tab){var ed=tab.state.editor;if(ed.selectionStart!=ed.selectionEnd){var s=ed.value.substring(ed.selectionStart,ed.selectionEnd).trim();if(s)return s;}return ed.value.trim();}
218
- function runSqlTab(tab,confirmed){var sql=currentSqlText(tab);if(!sql)return logMsg("Editor is empty.","warn");if(!S.activeConnId)return logMsg("Select a connection first.","warn");
270
+ function setEditorValue(tab,v){tab.state.editor.value=v;if(tab.state.syncGutter)tab.state.syncGutter();setDirty(tab,true);scheduleWorkspaceSave();}
271
+ function splitRangesClient(sql){var ranges=[],buf="",start=-1,inStr=false,q="",inLine=false,inBlock=false;function push(e){if(buf.trim())ranges.push({sql:buf.trim(),start:start,end:e});buf="";start=-1;}for(var i=0;i<sql.length;i++){var ch=sql[i],nx=sql[i+1];if(start===-1&&!/\\s/.test(ch))start=i;if(inLine){buf+=ch;if(ch==="\\n")inLine=false;continue;}if(inBlock){buf+=ch;if(ch==="*"&&nx==="/"){buf+=nx;i++;inBlock=false;}continue;}if(inStr){buf+=ch;if(ch===q)inStr=false;continue;}if(ch==="-"&&nx==="-"){inLine=true;buf+=ch;continue;}if(ch==="/"&&nx==="*"){inBlock=true;buf+=ch;continue;}if(ch==="'"||ch==='"'){inStr=true;q=ch;buf+=ch;continue;}if(ch===";"){push(i);continue;}buf+=ch;}push(sql.length);return ranges;}
272
+ function statementAtCursor(sql,off){var r=splitRangesClient(sql);for(var i=0;i<r.length;i++)if(off>=r[i].start&&off<=r[i].end+1)return r[i].sql;return (r[r.length-1]||{}).sql||sql.trim();}
273
+ function runMode(tab,mode){var ed=tab.state.editor;var hasSel=ed.selectionStart!=ed.selectionEnd;var sel=hasSel?ed.value.substring(ed.selectionStart,ed.selectionEnd).trim():"";var sql;if(mode==="all")sql=ed.value.trim();else if(mode==="current")sql=statementAtCursor(ed.value,ed.selectionStart);else if(mode==="explain"){if(S.connType!=="postgresql")return logMsg("Explain currently supports PostgreSQL.","warn");sql="EXPLAIN "+(sel||statementAtCursor(ed.value,ed.selectionStart));}else sql=sel||statementAtCursor(ed.value,ed.selectionStart);if(!sql)return logMsg("Nothing to run.","warn");execSql(tab,sql);}
274
+ function execSql(tab,sql,confirmed){if(!S.activeConnId)return logMsg("Select a connection first.","warn");
219
275
  if(!confirmed&&DANGER.test(sql)){if(!confirm("This statement may modify or drop data:\\n\\n"+sql.slice(0,160)+"\\n\\nRun anyway?"))return;}
220
276
  tab.state.err.classList.add("hidden");tab.state.runBtn.disabled=true;tab.state.runBtn.innerHTML="";tab.state.runBtn.appendChild(el("span",{class:"spin"}));tab.state.runBtn.appendChild(document.createTextNode(" Running..."));setRun(true);setConnStatus("Running query...","run");
221
277
  var limit=parseInt(tab.state.limit.value,10);
@@ -228,34 +284,41 @@ function runSqlTab(tab,confirmed){var sql=currentSqlText(tab);if(!sql)return log
228
284
  $("stDuration").textContent=r.result.durationMs+"ms";$("stRows").textContent=r.result.rowCount+" rows";
229
285
  }).catch(function(e){tab.state.runBtn.innerHTML=svgFor("run")+" Run";tab.state.runBtn.disabled=false;setConnStatus("Connected","ok");tab.state.err.textContent=e.message;tab.state.err.classList.remove("hidden");});
230
286
  }
231
- function explainSqlTab(tab){if(S.connType!=="postgresql")return logMsg("Explain currently supports PostgreSQL.","warn");tab.state.editor.value="EXPLAIN "+currentSqlText(tab);runSqlTab(tab,true);}
232
- function saveQueryTab(tab){var sql=tab.state.editor.value.trim();if(!sql)return logMsg("Nothing to save.","warn");var name=prompt("Query name","Query "+new Date().toLocaleString());if(!name)return;api("POST","/api/queries",{name:name,sql:sql,connectionId:S.activeConnId,connectionType:S.connType}).then(function(){setDirty(tab,false);loadSavedQueries();logMsg("Query saved.","ok");}).catch(function(e){logMsg(e.message,"err");});}
287
+ function saveQueryTab(tab){var sql=tab.state.editor.value.trim();if(!sql)return logMsg("Nothing to save.","warn");var qid=tab.meta&&tab.meta.queryId;if(qid){api("PUT","/api/queries/"+encodeURIComponent(qid),{name:tab.title.replace(/^SQL: ?/,""),sql:sql}).then(function(){setDirty(tab,false);loadSavedQueries();logMsg("Query updated.","ok");}).catch(function(e){logMsg(e.message,"err");});return;}var name=prompt("Save query as","Query "+new Date().toLocaleString());if(!name)return;api("POST","/api/queries",{name:name,sql:sql,connectionId:S.activeConnId,connectionType:S.connType}).then(function(r){if(tab.meta)tab.meta.queryId=r.query.id;tab.title="SQL: "+name;setDirty(tab,false);renderTabBar();loadSavedQueries();logMsg("Query saved.","ok");}).catch(function(e){logMsg(e.message,"err");});}
233
288
  function exportResult(tab,fmt){var res=tab.state.lastResult;if(!res||!res.rows.length)return logMsg("No result to export.","warn");var fields=res.fields&&res.fields.length?res.fields:Object.keys(res.rows[0]);fetch(fmt==="csv"?"/api/export/csv":"/api/export/json",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({fields:fields,rows:res.rows})}).then(function(r){return r.blob();}).then(function(b){var a=document.createElement("a");a.href=URL.createObjectURL(b);a.download=fmt==="csv"?"result.csv":"result.json";a.click();logMsg("Exported "+fmt.toUpperCase(),"ok");});}
234
289
  function formatSql(sql){return sql.replace(/\\s+/g," ").replace(/\\b(select|from|where|and|or|order by|group by|having|limit|offset|inner join|left join|right join|join|on|union|values|set|insert into|update|delete from|create table|alter table)\\b/gi,function(m){return "\\n"+m.toUpperCase();}).trim();}
235
290
 
236
291
  /* generic read-only result grid (sql console) */
237
- function renderResultGrid(box,result,onSort){clear(box);if(!result||!result.rows||!result.rows.length){box.appendChild(el("div",{class:"empty",text:result&&result.affectedRows!=null?("Affected rows: "+result.affectedRows):"No rows."}));return;}var fields=result.fields&&result.fields.length?result.fields:Object.keys(result.rows[0]);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){htr.appendChild(el("th",{text:f,title:f}));});thead.appendChild(htr);table.appendChild(thead);var tb=el("tbody");result.rows.forEach(function(row,ri){var tr=el("tr");tr.appendChild(el("td",{class:"rowhdr",text:ri+1}));fields.forEach(function(f){var v=row[f];var disp=v==null?"":typeof v==="object"?JSON.stringify(v):String(v);var td=el("td",{class:typeof v==="number"?"num":"",title:disp,text:disp.length>400?disp.slice(0,400)+"\\u2026":disp});td.addEventListener("dblclick",function(){navigator.clipboard.writeText(disp);setConnStatus("Cell copied","ok");});tr.appendChild(td);});tb.appendChild(tr);});table.appendChild(tb);box.appendChild(table);}
292
+ function renderResultGrid(box,result,onSort){clear(box);if(!result||!result.rows||!result.rows.length){box.appendChild(el("div",{class:"empty",text:result&&result.affectedRows!=null?("Affected rows: "+result.affectedRows):"No rows."}));return;}var fields=result.fields&&result.fields.length?result.fields:Object.keys(result.rows[0]);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){htr.appendChild(el("th",{text:f,title:f}));});thead.appendChild(htr);table.appendChild(thead);var tb=el("tbody");result.rows.forEach(function(row,ri){var tr=el("tr");tr.appendChild(el("td",{class:"rowhdr",text:ri+1}));fields.forEach(function(f){var v=row[f];var disp=v==null?"":typeof v==="object"?JSON.stringify(v):String(v);var td=el("td",{class:typeof v==="number"?"num":"",title:disp,text:disp.length>400?disp.slice(0,400)+"\\u2026":disp});td.addEventListener("dblclick",function(){openCellViewer(v);});tr.appendChild(td);});tb.appendChild(tr);});table.appendChild(tb);box.appendChild(table);}
238
293
 
239
294
  /* ====================================================================
240
295
  DATA GRID TAB (editable, pending changes)
241
296
  ==================================================================== */
242
- function openDataTab(schema,table){openTab({key:"data:"+S.activeConnId+":"+schema+"."+table,kind:"data",title:table,icon:"table2",build:function(pane,tab){buildDataPane(pane,tab,schema,table);}});}
297
+ function openDataTab(schema,table,restore){return openTab({key:"data:"+S.activeConnId+":"+schema+"."+table,kind:"data",title:table,icon:"table2",connectionId:S.activeConnId,meta:{schema:schema,table:table,objectType:"table"},build:function(pane,tab){tab.connectionId=S.activeConnId;buildDataPane(pane,tab,schema,table,restore);}});}
298
+ var _pop=null;
299
+ function showPopover(node,anchor){closePop();_pop=node;node.style.position="fixed";document.body.appendChild(node);var r=anchor.getBoundingClientRect();node.style.left=Math.min(r.left,window.innerWidth-node.offsetWidth-10)+"px";node.style.top=(r.bottom+6)+"px";setTimeout(function(){document.addEventListener("mousedown",popOutside,true);},0);}
300
+ function popOutside(e){if(_pop&&!_pop.contains(e.target))closePop();}
301
+ function closePop(){if(_pop){_pop.remove();_pop=null;document.removeEventListener("mousedown",popOutside,true);}}
302
+ function showFilterSql(tab,anchor){var g=tab.state.g;api("POST","/api/sql/generate-table-query",{connectionId:S.activeConnId,schema:g.schema,table:g.table,where:g.whereI.value||"",sort:g.orderBy?[{column:g.orderBy,direction:g.orderDir}]:[],limit:parseInt(g.pageSel.value,10),offset:g.offset}).then(function(r){var pre=el("pre",{text:r.sql});var pop=el("div",{class:"popover"},[el("div",{class:"row",style:"margin-bottom:8px"},[el("b",{text:"Generated SQL"}),el("span",{style:"flex:1"}),el("button",{class:"btn sm sec",text:"Copy",onclick:function(){navigator.clipboard.writeText(r.sql);logMsg("Copied SQL","ok");}}),el("button",{class:"btn sm",text:"Open in Console",onclick:function(){closePop();openSqlTab(r.sql,g.table);}}),el("button",{class:"btn sm ghost",text:"Close",onclick:closePop})]),pre]);showPopover(pop,anchor);}).catch(function(e){logMsg(e.message,"err");});}
243
303
  function pendingCount(g){return Object.keys(g.edits).length+Object.keys(g.deletes).length+g.inserts.length;}
244
304
  function rowKeyOf(g,row){return g.pk.map(function(k){return String(row[k]);}).join("\\u0001");}
245
- function buildDataPane(pane,tab,schema,table){
246
- 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};tab.state.g=g;
305
+ function buildDataPane(pane,tab,schema,table,restore){
306
+ 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;
247
307
  var whereI=el("input",{class:"input",style:"flex:1",placeholder:"WHERE clause, e.g. STATUS = 'A'"});g.whereI=whereI;
248
308
  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;
249
309
  var pageInfo=el("span",{class:"note"});g.pageInfo=pageInfo;
250
- 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 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");}})]);
310
+ wireSearch(whereI,function(){g.where=whereI.value;g.offset=0;loadData(tab);});
311
+ 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");}})]);
251
312
  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;
252
313
  var editHint=el("span",{class:"note"});g.editHint=editHint;
253
314
  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]);
254
315
  var grid=el("div",{class:"gridwrap"});g.grid=grid;
255
- pane.appendChild(tb1);pane.appendChild(tb2);pane.appendChild(grid);
316
+ var changeBar=el("div",{class:"changebar hidden"});g.changeBar=changeBar;
317
+ pane.appendChild(crumbs((activeConn()||{}).name,schema,table));pane.appendChild(tb1);pane.appendChild(tb2);pane.appendChild(changeBar);pane.appendChild(grid);
256
318
  updateDirtyButtons(tab);
257
319
  api("GET","/api/catalog/columns?"+qstr({connectionId:S.activeConnId,schema:schema,table:table})).then(function(r){g.columns=r.columns||[];}).catch(function(){});
258
320
  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="";});
321
+ 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;}}
259
322
  loadData(tab);
260
323
  loadCount(tab);
261
324
  }
@@ -263,13 +326,13 @@ function loadData(tab){var g=tab.state.g;g.pageSize=parseInt(g.pageSel.value,10)
263
326
  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(){});}
264
327
  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);}
265
328
  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;}
266
- var fields=g.columns.length?g.columns.map(function(c){return c.name;}):(g.rows[0]?Object.keys(g.rows[0]):[]);
329
+ var fields=g.columns.length?g.columns.map(function(c){return c.name;}):(g.rows[0]?Object.keys(g.rows[0]):[]);g.fields=fields;
267
330
  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);
268
331
  var tbody=el("tbody");
269
- 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 ":"")});
332
+ 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});
270
333
  var flag=edited?'<span class="rowflag d"></span>':(deleted?'<span class="rowflag del"></span>':"");
271
- var num=el("td",{class:"rowhdr",html:flag+(g.offset+ri+1)});num.addEventListener("click",function(){g.selRow=(g.selRow===ri?null:ri);renderGrid(tab);});tr.appendChild(num);
272
- 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(){navigator.clipboard.writeText(disp);setConnStatus("Cell copied","ok");});}tr.appendChild(td);});
334
+ 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);
335
+ 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);});
273
336
  if(err){var etr=tr;etr.title=err;}
274
337
  tbody.appendChild(tr);});
275
338
  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);}}));
@@ -279,11 +342,13 @@ function renderGrid(tab){var g=tab.state.g;var box=clear(g.grid);if(!g.rows.leng
279
342
  table.appendChild(tbody);box.appendChild(table);
280
343
  updateDirtyButtons(tab);
281
344
  }
282
- function startEdit(tab,td,ri,field,row){if(td.querySelector("input"))return;var g=tab.state.g;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;done=true;var origStr=row[field]==null?"":String(row[field]);if(input.value===origStr){if(g.edits[key]){delete g.edits[key][field];if(!Object.keys(g.edits[key]).length)delete g.edits[key];}}else{g.edits[key]=g.edits[key]||{};g.edits[key][field]=input.value;}renderGrid(tab);}input.addEventListener("keydown",function(e){if(e.key==="Enter"){e.preventDefault();commit();}else if(e.key==="Escape"){e.preventDefault();done=true;renderGrid(tab);}});input.addEventListener("blur",commit);}
283
- 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");var key=rowKeyOf(g,g.rows[g.selRow]);if(g.deletes[key])delete g.deletes[key];else g.deletes[key]=true;renderGrid(tab);}
284
- function addInsertRow(tab){var g=tab.state.g;g.inserts.push({iseq:++g.iseq,values:{}});renderGrid(tab);}
345
+ 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;}
346
+ 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);}});
347
+ input.addEventListener("blur",function(){if(!done){commit();renderGrid(tab);}});}
348
+ 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);}
349
+ function addInsertRow(tab){var g=tab.state.g;gridPushUndo(tab);g.inserts.push({iseq:++g.iseq,values:{}});renderGrid(tab);}
285
350
  function revertAll(tab){var g=tab.state.g;g.edits={};g.deletes={};g.inserts=[];g.errors={};renderGrid(tab);logMsg("Reverted pending changes.","ok");}
286
- function updateDirtyButtons(tab){var g=tab.state.g;var n=pendingCount(g);g.saveBtn.style.display=n>0?"":"none";g.revertBtn.style.display=n>0?"":"none";setDirty(tab,n>0);}
351
+ 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");}}
287
352
  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();});}
288
353
  function saveDataChanges(tab){var g=tab.state.g;if(S.readOnly)return logMsg("Read-only mode is on.","warn");
289
354
  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]};});
@@ -312,13 +377,14 @@ function rowKeyFromKeyObj(g,keyObj){return g.pk.map(function(k){return String(ke
312
377
  /* ====================================================================
313
378
  STRUCTURE / METADATA TAB
314
379
  ==================================================================== */
315
- function openStructureTab(schema,table){openTab({key:"struct:"+S.activeConnId+":"+schema+"."+table,kind:"structure",title:"Structure: "+table,icon:"col",build:function(pane,tab){buildStructure(pane,tab,schema,table);}});}
380
+ function openStructureTab(schema,table){return openTab({key:"struct:"+S.activeConnId+":"+schema+"."+table,kind:"structure",title:"Structure: "+table,icon:"col",connectionId:S.activeConnId,meta:{schema:schema,table:table,objectType:"table"},build:function(pane,tab){tab.connectionId=S.activeConnId;buildStructure(pane,tab,schema,table);}});}
316
381
  function buildStructure(pane,tab,schema,table){
317
382
  var subtabs=el("div",{class:"meta-tabs"});var body=el("div",{class:"pane-body"});
318
383
  var defs=[["columns","Columns"],["indexes","Indexes"],["ddl","DDL"],["info","Info"]];
319
384
  var active="columns";var data={};
320
385
  function render(){clear(body);if(active==="columns")renderCols();else if(active==="indexes")renderIdx();else if(active==="ddl")renderDdl();else renderInfo();Array.prototype.forEach.call(subtabs.children,function(ch,i){ch.classList.toggle("active",defs[i][0]===active);});}
321
386
  defs.forEach(function(d){subtabs.appendChild(el("div",{class:"meta-tab",text:d[1],onclick:function(){active=d[0];render();}}));});
387
+ pane.appendChild(crumbs((activeConn()||{}).name,schema,table));
322
388
  pane.appendChild(el("div",{class:"toolbar"},[el("b",{text:'"'+schema+'"."'+table+'"'}),el("span",{class:"grow"}),el("button",{class:"btn sec",text:"Open Data",onclick:function(){openDataTab(schema,table);}})]));
323
389
  pane.appendChild(subtabs);pane.appendChild(body);
324
390
  function renderCols(){body.appendChild(el("div",{class:"empty"},[el("span",{class:"spin"})," loading..."]));api("GET","/api/catalog/columns?"+qstr({connectionId:S.activeConnId,schema:schema,table:table})).then(function(r){data.columns=r.columns||[];if(active!=="columns")return;clear(body);var t=el("table",{class:"grid"});t.appendChild(el("thead",{html:"<tr><th>Name</th><th>Type</th><th>Length</th><th>Scale</th><th>Nullable</th><th>Key</th><th>Default</th><th>Comment</th></tr>"}));var tb=el("tbody");data.columns.forEach(function(c){tb.appendChild(el("tr",{html:"<td>"+esc(c.name)+"</td><td>"+esc(c.dataType)+'</td><td class="num">'+esc(c.length==null?"":c.length)+'</td><td class="num">'+esc(c.scale==null?"":c.scale)+"</td><td>"+(c.nullable?"YES":"NO")+"</td><td>"+(c.isPrimaryKey?'<span class="pill pk">PK</span>':"")+"</td><td>"+esc(c.defaultValue==null?"":c.defaultValue)+"</td><td>"+esc(c.comment==null?"":c.comment)+"</td>"}));});t.appendChild(tb);clear(body).appendChild(t);}).catch(function(e){clear(body).appendChild(el("div",{class:"errbox",text:e.message}));});}
@@ -332,7 +398,7 @@ function buildStructure(pane,tab,schema,table){
332
398
  SAVED QUERIES
333
399
  ==================================================================== */
334
400
  function loadSavedQueries(){return api("GET","/api/queries").then(function(r){S.savedQueries=r.queries||[];renderSavedQueries();}).catch(function(){});}
335
- function renderSavedQueries(){var q=($("querySearch").value||"").toLowerCase();var box=clear($("queryList"));var rows=S.savedQueries.filter(function(x){return (x.name+" "+(x.tags||[]).join(" ")).toLowerCase().indexOf(q)>=0;});if(!rows.length){box.appendChild(el("div",{class:"empty",text:"No saved queries."}));return;}rows.forEach(function(x){var item=el("div",{class:"wli",onclick:function(){openSqlTab(x.sql,x.name);},oncontextmenu:function(e){e.preventDefault();queryMenu(e,x);}});item.appendChild(el("b",{text:x.name}));item.appendChild(el("div",{class:"note",text:(x.connectionType||"")+" \\u00b7 "+new Date(x.updatedAt).toLocaleDateString()}));box.appendChild(item);});}
401
+ function renderSavedQueries(){var raw=($("querySearch").value||"");var q=raw.toLowerCase();var box=clear($("queryList"));var rows=S.savedQueries.filter(function(x){return (x.name+" "+(x.tags||[]).join(" ")).toLowerCase().indexOf(q)>=0;});if(!rows.length){box.appendChild(el("div",{class:"empty",text:S.savedQueries.length?"No results found":"No saved queries."}));return;}rows.forEach(function(x){var item=el("div",{class:"wli",onclick:function(){openSqlTab(x.sql,x.name,x.id);},oncontextmenu:function(e){e.preventDefault();queryMenu(e,x);}});item.appendChild(el("b",{html:highlightMatch(x.name,raw)}));item.appendChild(el("div",{class:"note",text:(x.connectionType||"")+" \\u00b7 "+new Date(x.updatedAt).toLocaleDateString()}));box.appendChild(item);});}
336
402
  function queryMenu(e,x){showCtx(e.clientX,e.clientY,[{label:"Open",icon:"sql",onClick:function(){openSqlTab(x.sql,x.name);}},{label:"Rename",icon:"gear",onClick:function(){var n=prompt("New name",x.name);if(n)api("PUT","/api/queries/"+encodeURIComponent(x.id),{name:n}).then(loadSavedQueries);}},{label:"Delete",icon:"x",danger:true,onClick:function(){if(confirm("Delete '"+x.name+"'?"))api("DELETE","/api/queries/"+encodeURIComponent(x.id)).then(loadSavedQueries);}}]);}
337
403
 
338
404
  /* ====================================================================
@@ -372,6 +438,68 @@ function openBtpWizard(){var stState={apps:[],services:[],app:"",svc:null,color:
372
438
  /* ====================================================================
373
439
  READ-ONLY + INIT
374
440
  ==================================================================== */
441
+ /* ====================================================================
442
+ KEYBOARD + COMMAND PALETTE + SETTINGS
443
+ ==================================================================== */
444
+ function isTyping(t){return t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.isContentEditable);}
445
+ function saveActive(){var t=tabById(S.activeTabId);if(!t)return;if(t.kind==="sql")saveQueryTab(t);else if(t.kind==="data")saveDataChanges(t);else logMsg("Nothing to save in this tab.","warn");}
446
+ function onGlobalKey(e){var k=e.key;var ctrl=e.ctrlKey||e.metaKey;
447
+ if(ctrl&&e.shiftKey&&(k==="P"||k==="p")){e.preventDefault();openCommandPalette();return;}
448
+ if(k==="Escape"){hideCtx();closePop();if(_palette){closePalette();return;}return;}
449
+ if(ctrl&&k==="Tab"){e.preventDefault();nextTab(e.shiftKey?-1:1);return;}
450
+ if(ctrl&&(k==="w"||k==="W")){e.preventDefault();if(S.activeTabId)closeTab(S.activeTabId);return;}
451
+ if(ctrl&&!e.shiftKey&&(k==="f"||k==="F")&&!isTyping(e.target)){e.preventDefault();$("topSearch").focus();return;}
452
+ if(k==="F5"&&!isTyping(e.target)){var ft=tabById(S.activeTabId);if(ft&&ft.kind==="sql"){e.preventDefault();runMode(ft,"all");}return;}
453
+ if(ctrl&&(k==="s"||k==="S")&&!isTyping(e.target)){e.preventDefault();saveActive();return;}
454
+ var at=tabById(S.activeTabId);
455
+ if(at&&at.kind==="data"&&at.state.g&&!isTyping(e.target)){var g=at.state.g;
456
+ if(ctrl&&(k==="s"||k==="S")){e.preventDefault();saveDataChanges(at);return;}
457
+ if(ctrl&&(k==="z"||k==="Z")){e.preventDefault();gridUndo(at);return;}
458
+ if(ctrl&&(k==="y"||k==="Y")){e.preventDefault();gridRedo(at);return;}
459
+ if(k==="Delete"){e.preventDefault();toggleDeleteSelected(at);return;}
460
+ }
461
+ }
462
+ var PALETTE=[
463
+ {label:"New SQL Console",run:function(){if(!S.activeConnId)logMsg("Select a connection first.","warn");else openSqlTab();}},
464
+ {label:"Run SQL (current tab)",run:function(){var t=tabById(S.activeTabId);if(t&&t.kind==="sql")runMode(t,"selected");}},
465
+ {label:"Save Query / Grid Changes",run:saveActive},
466
+ {label:"Import from BTP App",run:function(){openBtpWizard();}},
467
+ {label:"New direct connection",run:function(){newConnModal();}},
468
+ {label:"Focus Connections",run:function(){$("connSearch").focus();}},
469
+ {label:"Focus Object Explorer",run:function(){var s=document.querySelector("#secTree .tsearch input");if(s)s.focus();else $("topSearch").focus();}},
470
+ {label:"Toggle Read-only",run:function(){toggleReadOnly();}},
471
+ {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");}},
472
+ {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");}},
473
+ {label:"Close Active Tab",run:function(){if(S.activeTabId)closeTab(S.activeTabId);}},
474
+ {label:"Show Keyboard Shortcuts",run:function(){showShortcuts();}},
475
+ {label:"Open Settings",run:function(){openSettings();}}
476
+ ];
477
+ var _palette=null;
478
+ function openCommandPalette(){closePalette();var input=el("input",{placeholder:"Type a command..."});var list=el("div",{class:"pitems"});var sel=0;var filtered=PALETTE.slice();
479
+ function draw(){clear(list);var q=input.value.toLowerCase();filtered=PALETTE.filter(function(c){return c.label.toLowerCase().indexOf(q)>=0;});filtered.forEach(function(c,i){list.appendChild(el("div",{class:"pitem"+(i===sel?" sel":""),onclick:function(){closePalette();c.run();}},[el("span",{html:highlightMatch(c.label,input.value)})]));});}
480
+ input.addEventListener("input",function(){sel=0;draw();});
481
+ input.addEventListener("keydown",function(e){if(e.key==="ArrowDown"){e.preventDefault();sel=Math.min(filtered.length-1,sel+1);draw();}else if(e.key==="ArrowUp"){e.preventDefault();sel=Math.max(0,sel-1);draw();}else if(e.key==="Enter"){e.preventDefault();if(filtered[sel]){closePalette();filtered[sel].run();}}else if(e.key==="Escape"){e.preventDefault();closePalette();}});
482
+ var ov=el("div",{class:"modal",onclick:function(e){if(e.target===ov)closePalette();}},[el("div",{class:"palette"},[input,list])]);document.body.appendChild(ov);_palette=ov;draw();input.focus();}
483
+ function closePalette(){if(_palette){_palette.remove();_palette=null;}}
484
+ function showShortcuts(){var rows=[["Ctrl+Shift+P","Command palette"],["Ctrl+Tab","Next tab"],["Ctrl+Shift+Tab","Previous tab"],["Ctrl+W","Close active tab"],["Ctrl+S","Save query / grid changes"],["Ctrl+F","Focus search"],["Escape","Close popup / clear search"],["Ctrl+Enter","Run selected / current SQL"],["F5","Run all SQL"],["Ctrl+Z / Ctrl+Y","Undo / redo (editor & grid)"],["Delete","Mark grid row for delete"],["Enter / Tab","Confirm cell edit & move"],["Esc","Cancel cell edit"]];
485
+ var grid=el("div",{class:"shorts"});rows.forEach(function(r){grid.appendChild(el("div",{class:"srow"},[el("span",{text:r[1]}),el("span",{class:"kbd",text:r[0]})]));});
486
+ openModal(el("div",{class:"dialog"},[el("h3",{text:"Keyboard shortcuts"}),grid,el("div",{class:"row right",style:"margin-top:14px"},[el("button",{class:"btn",text:"Close",onclick:closeModal})])]));}
487
+ function openSettings(){var s=S.settings;var restore=el("input",{type:"checkbox"});restore.checked=s.restoreWorkspace;var ro=el("input",{type:"checkbox"});ro.checked=s.readOnlyByDefault;var prod=el("input",{type:"checkbox"});prod.checked=s.showProductionWarning;var fmt=el("input",{type:"checkbox"});fmt.checked=s.autoFormatGeneratedSql;var limit=el("input",{class:"input",value:String(s.defaultRowLimit)});var schema=el("input",{class:"input",value:s.defaultSchema||""});var timeout=el("input",{class:"input",value:String(s.queryTimeoutMs)});var maxh=el("input",{class:"input",value:String(s.maxHistoryItems)});var delay=el("input",{class:"input",value:String(s.autoSaveDelayMs)});
488
+ var d=el("div",{class:"dialog"},[el("h3",{text:"Studio settings"}),el("label",{class:"toggle"},[restore," Restore workspace on startup"]),el("label",{class:"toggle"},[ro," Read-only by default"]),el("label",{class:"toggle"},[prod," Show production warning"]),el("label",{class:"toggle"},[fmt," Auto-format generated SQL"]),el("div",{class:"field"},[el("label",{text:"Default row limit"}),limit]),el("div",{class:"field"},[el("label",{text:"Default schema"}),schema]),el("div",{class:"field"},[el("label",{text:"Query timeout (ms)"}),timeout]),el("div",{class:"field"},[el("label",{text:"Max history items"}),maxh]),el("div",{class:"field"},[el("label",{text:"Auto-save delay (ms)"}),delay]),el("div",{class:"row right"},[el("button",{class:"btn ghost",text:"Cancel",onclick:closeModal}),el("button",{class:"btn",text:"Save",onclick:function(){api("PUT","/api/studio/settings",{restoreWorkspace:restore.checked,readOnlyByDefault:ro.checked,showProductionWarning:prod.checked,autoFormatGeneratedSql:fmt.checked,defaultRowLimit:parseInt(limit.value,10)||100,defaultSchema:schema.value.trim(),queryTimeoutMs:parseInt(timeout.value,10)||30000,maxHistoryItems:parseInt(maxh.value,10)||300,autoSaveDelayMs:parseInt(delay.value,10)||500}).then(function(r){S.settings=r.settings;closeModal();logMsg("Settings saved.","ok");}).catch(function(e){logMsg(e.message,"err");});}})])]);openModal(d);}
489
+ function openCellViewer(value){var raw=value==null?"":typeof value==="object"?JSON.stringify(value):String(value);var pretty=raw;try{pretty=JSON.stringify(JSON.parse(raw),null,2);}catch(e){}var ta=el("textarea",{class:"editor",style:"min-height:300px"});ta.value=pretty;openModal(el("div",{class:"dialog",style:"width:700px"},[el("h3",{text:"Cell value"}),ta,el("div",{class:"row right",style:"margin-top:10px"},[el("button",{class:"btn ghost",text:"Copy",onclick:function(){navigator.clipboard.writeText(raw);logMsg("Copied value","ok");}}),el("button",{class:"btn",text:"Close",onclick:closeModal})])]));}
490
+ function crumbs(connName,schema,table){var c=el("div",{class:"crumbs"});c.appendChild(el("a",{text:connName||"Connection",onclick:function(){if(S.activeConnId)activateConnection(S.activeConnId);}}));c.appendChild(el("span",{class:"sep",text:"\\u203a"}));c.appendChild(el("a",{text:schema,onclick:function(){S.activeSchema=schema;updateTopBadges();}}));c.appendChild(el("span",{class:"sep",text:"\\u203a"}));c.appendChild(el("span",{text:table}));return c;}
491
+
492
+ /* ---- grid undo/redo + change review ---- */
493
+ function gridSnapshot(g){return JSON.stringify({edits:g.edits,deletes:g.deletes,inserts:g.inserts});}
494
+ function gridApplySnap(g,s){var o=JSON.parse(s);g.edits=o.edits;g.deletes=o.deletes;g.inserts=o.inserts;}
495
+ function gridPushUndo(tab){var g=tab.state.g;g.undo.push(gridSnapshot(g));if(g.undo.length>60)g.undo.shift();g.redo=[];}
496
+ function gridUndo(tab){var g=tab.state.g;if(!g.undo.length)return logMsg("Nothing to undo.","warn");g.redo.push(gridSnapshot(g));gridApplySnap(g,g.undo.pop());renderGrid(tab);}
497
+ function gridRedo(tab){var g=tab.state.g;if(!g.redo.length)return logMsg("Nothing to redo.","warn");g.undo.push(gridSnapshot(g));gridApplySnap(g,g.redo.pop());renderGrid(tab);}
498
+ function showChanges(tab){var g=tab.state.g;var out=[];Object.keys(g.edits).forEach(function(key){var row=g.rows.filter(function(r){return rowKeyOf(g,r)===key;})[0]||{};Object.keys(g.edits[key]).forEach(function(col){out.push(["update",key,col,String(row[col]==null?"":row[col]),String(g.edits[key][col])]);});});Object.keys(g.deletes).forEach(function(key){out.push(["delete",key,"","",""]);});g.inserts.forEach(function(ins){out.push(["insert","(new)",Object.keys(ins.values).join(","),"",JSON.stringify(ins.values)]);});
499
+ var t=el("table",{class:"grid"});t.appendChild(el("thead",{html:"<tr><th>Type</th><th>Row key</th><th>Column</th><th>Old</th><th>New</th></tr>"}));var tb=el("tbody");if(!out.length)tb.appendChild(el("tr",{html:'<td colspan="5">No pending changes.</td>'}));out.forEach(function(r){tb.appendChild(el("tr",{html:r.map(function(c){return "<td>"+esc(c)+"</td>";}).join("")}));});t.appendChild(tb);
500
+ openModal(el("div",{class:"dialog",style:"width:780px"},[el("h3",{text:"Pending changes"}),el("div",{class:"gridwrap",style:"max-height:52vh"},[t]),el("div",{class:"row right",style:"margin-top:12px"},[el("button",{class:"btn",text:"Close",onclick:closeModal})])]));}
501
+ function editAt(tab,ri,colIdx){var g=tab.state.g;if(ri<0||ri>=g.rows.length||colIdx<0||!g.fields||colIdx>=g.fields.length)return;var tr=g.grid.querySelector('tr[data-ri="'+ri+'"]');if(!tr)return;var tds=tr.querySelectorAll("td");var td=tds[colIdx+1];if(td)startEdit(tab,td,ri,g.fields[colIdx],g.rows[ri]);}
502
+
375
503
  function applyReadOnly(){var b=$("roBadge");b.className="badge ro"+(S.readOnly?" active":"");b.textContent=S.readOnly?"Read-only":"Read/Write";}
376
504
  function toggleReadOnly(){S.readOnly=!S.readOnly;applyReadOnly();logMsg("Read-only mode: "+(S.readOnly?"ON":"OFF"),"ok");}
377
505
 
@@ -385,15 +513,16 @@ window.addEventListener("load",function(){
385
513
  $("btnImport").addEventListener("click",openBtpWizard);
386
514
  $("btnImport2").addEventListener("click",openBtpWizard);
387
515
  $("btnNewConn").addEventListener("click",newConnModal);
388
- $("btnSettings").addEventListener("click",function(){logMsg("Read-only toggle and connection colors are in the top bar / connection menu.","ok");});
516
+ $("btnSettings").addEventListener("click",openSettings);
389
517
  $("btnHome").addEventListener("click",openWelcome);
390
- $("connSearch").addEventListener("input",debounce(renderConnections,200));
391
- $("querySearch").addEventListener("input",debounce(renderSavedQueries,200));
518
+ window.addEventListener("keydown",onGlobalKey);
519
+ $("connSearch").addEventListener("input",debounce(renderConnections,200));wireSearch($("connSearch"),renderConnections);
520
+ $("querySearch").addEventListener("input",debounce(renderSavedQueries,200));wireSearch($("querySearch"),renderSavedQueries);
392
521
  $("topSearch").addEventListener("input",debounce(function(){$("connSearch").value=$("topSearch").value;renderConnections();},200));
393
522
  $("btnNewQuery").addEventListener("click",function(){if(!S.activeConnId)return logMsg("Select a connection first.","warn");openSqlTab();});
394
523
  setConnStatus("Ready","ok");
395
524
  openWelcome();
396
- loadConnections();
525
+ loadSettings().then(function(){return loadConnections();}).then(function(){return restoreWorkspace();});
397
526
  loadSavedQueries();
398
527
  });
399
528
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"db-studio-client.js","sourceRoot":"","sources":["../../../src/core/db/db-studio-client.ts"],"names":[],"mappings":";;;AAAa,QAAA,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4YnC,CAAC"}
1
+ {"version":3,"file":"db-studio-client.js","sourceRoot":"","sources":["../../../src/core/db/db-studio-client.ts"],"names":[],"mappings":";;;AAAa,QAAA,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6gBnC,CAAC"}