admindb 2.4.0 → 2.5.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 (135) hide show
  1. package/LICENSE +20 -20
  2. package/dist/app.js +36 -25
  3. package/dist/core/context.d.ts +1 -0
  4. package/dist/core/context.js +2 -0
  5. package/dist/core/errors.d.ts +1 -0
  6. package/dist/core/errors.js +39 -0
  7. package/dist/core/index.d.ts +1 -0
  8. package/dist/core/index.js +20 -0
  9. package/dist/core/result.d.ts +1 -0
  10. package/dist/core/result.js +10 -0
  11. package/dist/core/router.d.ts +1 -0
  12. package/dist/core/router.js +37 -0
  13. package/dist/data/chain.js +1 -1
  14. package/dist/data/engine.js +1 -1
  15. package/dist/db/database.d.ts +1 -1
  16. package/dist/db/database.js +9 -31
  17. package/dist/db/dialects/index.d.ts +1 -0
  18. package/dist/db/dialects/index.js +58 -0
  19. package/dist/db/dialects/postgres/ddl.d.ts +1 -0
  20. package/dist/db/dialects/postgres/ddl.js +107 -0
  21. package/dist/db/dialects/postgres/dialect.d.ts +1 -0
  22. package/dist/db/dialects/postgres/dialect.js +28 -0
  23. package/dist/db/dialects/postgres/introspector.d.ts +1 -0
  24. package/dist/db/dialects/postgres/introspector.js +265 -0
  25. package/dist/db/dialects/postgres/types.d.ts +1 -0
  26. package/dist/db/dialects/postgres/types.js +83 -0
  27. package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
  28. package/dist/db/dialects/sqlite/ddl.js +244 -0
  29. package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
  30. package/dist/db/dialects/sqlite/dialect.js +25 -0
  31. package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
  32. package/dist/db/dialects/sqlite/introspector.js +214 -0
  33. package/dist/db/dialects/sqlite/types.d.ts +1 -0
  34. package/dist/db/dialects/sqlite/types.js +69 -0
  35. package/dist/db/dialects/types.d.ts +1 -0
  36. package/dist/db/dialects/types.js +15 -0
  37. package/dist/db/export.js +1 -1
  38. package/dist/db/index.d.ts +1 -1
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/manager.d.ts +1 -1
  41. package/dist/db/manager.js +101 -21
  42. package/dist/db/postgres.d.ts +1 -1
  43. package/dist/db/postgres.js +20 -284
  44. package/dist/db/types.d.ts +1 -1
  45. package/dist/db/types.js +0 -4
  46. package/dist/index.d.ts +1 -1
  47. package/dist/index.js +4 -1
  48. package/dist/modules/databases/databases.routes.d.ts +1 -0
  49. package/dist/modules/databases/databases.routes.js +150 -0
  50. package/dist/modules/databases/databases.service.d.ts +1 -0
  51. package/dist/modules/databases/databases.service.js +134 -0
  52. package/dist/modules/databases/index.d.ts +1 -0
  53. package/dist/{routes → modules/databases}/index.js +2 -3
  54. package/dist/modules/erd/erd.routes.d.ts +1 -0
  55. package/dist/modules/erd/erd.routes.js +32 -0
  56. package/dist/modules/erd/erd.service.d.ts +1 -0
  57. package/dist/modules/erd/erd.service.js +39 -0
  58. package/dist/modules/erd/index.d.ts +1 -0
  59. package/dist/modules/erd/index.js +18 -0
  60. package/dist/modules/index.d.ts +1 -0
  61. package/dist/modules/index.js +62 -0
  62. package/dist/modules/query/index.d.ts +1 -0
  63. package/dist/modules/query/index.js +18 -0
  64. package/dist/modules/query/query.routes.d.ts +1 -0
  65. package/dist/modules/query/query.routes.js +54 -0
  66. package/dist/modules/query/query.service.d.ts +1 -0
  67. package/dist/modules/query/query.service.js +95 -0
  68. package/dist/modules/rows/blob.helper.d.ts +1 -0
  69. package/dist/modules/rows/blob.helper.js +40 -0
  70. package/dist/modules/rows/display.helper.d.ts +1 -0
  71. package/dist/modules/rows/display.helper.js +73 -0
  72. package/dist/modules/rows/index.d.ts +1 -0
  73. package/dist/modules/rows/index.js +20 -0
  74. package/dist/modules/rows/rows.routes.d.ts +1 -0
  75. package/dist/modules/rows/rows.routes.js +555 -0
  76. package/dist/modules/rows/rows.service.d.ts +1 -0
  77. package/dist/modules/rows/rows.service.js +433 -0
  78. package/dist/modules/schema/index.d.ts +1 -0
  79. package/dist/modules/schema/index.js +18 -0
  80. package/dist/modules/schema/schema.routes.d.ts +1 -0
  81. package/dist/modules/schema/schema.routes.js +171 -0
  82. package/dist/modules/schema/schema.service.d.ts +1 -0
  83. package/dist/modules/schema/schema.service.js +58 -0
  84. package/dist/modules/seed/index.d.ts +1 -0
  85. package/dist/modules/seed/index.js +18 -0
  86. package/dist/modules/seed/seed.routes.d.ts +1 -0
  87. package/dist/modules/seed/seed.routes.js +212 -0
  88. package/dist/modules/seed/seed.service.d.ts +1 -0
  89. package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
  90. package/dist/modules/tables/index.d.ts +1 -0
  91. package/dist/modules/tables/index.js +18 -0
  92. package/dist/modules/tables/tables.routes.d.ts +1 -0
  93. package/dist/modules/tables/tables.routes.js +194 -0
  94. package/dist/modules/tables/tables.service.d.ts +1 -0
  95. package/dist/modules/tables/tables.service.js +218 -0
  96. package/dist/public/js/browse.js +1 -1
  97. package/dist/public/js/query.js +1 -1
  98. package/dist/types/api.d.ts +1 -1
  99. package/dist/utils/common.d.ts +1 -1
  100. package/dist/utils/common.js +1 -3
  101. package/dist/utils/stream.d.ts +1 -0
  102. package/dist/utils/stream.js +131 -0
  103. package/dist/views/pages/error.hbs +10 -1
  104. package/dist/views/pages/home.hbs +1 -1
  105. package/dist/views/pages/query.hbs +1 -1
  106. package/dist/views/pages/seed-select.hbs +1 -252
  107. package/dist/views/pages/seed.hbs +1 -307
  108. package/dist/views/pages/table.hbs +1 -1
  109. package/dist/views/partials/sidebar.hbs +1 -1
  110. package/docs/API.md +4 -17
  111. package/docs/EXAMPLES.md +579 -579
  112. package/docs/SECURITY.md +265 -265
  113. package/package.json +4 -2
  114. package/dist/db/introspection.d.ts +0 -1
  115. package/dist/db/introspection.js +0 -164
  116. package/dist/routes/api/erd.d.ts +0 -1
  117. package/dist/routes/api/erd.js +0 -45
  118. package/dist/routes/api/helpers.d.ts +0 -1
  119. package/dist/routes/api/import-export.d.ts +0 -1
  120. package/dist/routes/api/import-export.js +0 -84
  121. package/dist/routes/api/index.d.ts +0 -1
  122. package/dist/routes/api/index.js +0 -55
  123. package/dist/routes/api/query.d.ts +0 -1
  124. package/dist/routes/api/query.js +0 -77
  125. package/dist/routes/api/rows.d.ts +0 -1
  126. package/dist/routes/api/rows.js +0 -373
  127. package/dist/routes/api/seed.d.ts +0 -1
  128. package/dist/routes/api/seed.js +0 -180
  129. package/dist/routes/api/tables.d.ts +0 -1
  130. package/dist/routes/api/tables.js +0 -230
  131. package/dist/routes/databases.d.ts +0 -1
  132. package/dist/routes/databases.js +0 -212
  133. package/dist/routes/index.d.ts +0 -1
  134. package/dist/routes/pages.d.ts +0 -1
  135. package/dist/routes/pages.js +0 -540
@@ -1 +1 @@
1
- !function(){"use strict";const e=window.APP&&window.APP.basePath||"",t=!(!window.APP||!window.APP.readonly),n=document.getElementById("browse-config"),s=n?JSON.parse(n.textContent):{table:"",filters:{},pkCols:[],hasPk:!1,readonly:t},o=encodeURIComponent(window.APP&&window.APP.dbId||s&&s.dbId||""),l=encodeURIComponent(s.table),a=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let i=null,r={};function c(e){return i?i.columns.find(t=>t.name===e):null}function d(e){return!!i&&-1!==i.primaryKey.indexOf(e)}function u(e){return i?i.foreignKeys.find(t=>t.from===e):null}function p(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function f(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function m(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function h(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=u(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,d="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||d||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",m(n,"","(any)"),m(n,"__NULL__","(not set / NULL)"),(r[e.name]||[]).forEach(e=>m(n,String(e.value),""===e.label?String(e.value):e.label));let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",m(n,"","(any)"),m(n,"1","true"),m(n,"0","false");let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||i){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":d?"time":"datetime-local",(d||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=p(y,"gte")??p(y,"gt")??a.min,r=p(y,"lte")??p(y,"lt")??a.max;return null!=i&&(s.value=g?f(i,c,d):String(i)),null!=r&&(o.value=g?f(r,c,d):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=p(y,"prefix"),t=p(y,"eq"),n=p(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function g(t){let n=s.table;if(!n){const e=window.location.pathname.match(/\/tables\/[^/]+\/([^/]+)(?:\/|$)/);e&&(n=decodeURIComponent(e[1]))}if(!n)return;const l=document.getElementById("page-size"),a=new URLSearchParams({page:"1",size:l?l.value:"50"});Object.keys(t).length&&a.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+o+"/"+encodeURIComponent(n)+"?"+a.toString()}function b(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&i&&(i.columns.forEach(e=>n.appendChild(h(e))),e.addEventListener("submit",e=>{e.preventDefault(),g(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>g({}))}let y=null;function v(){y&&(y.classList.remove("cell-focused"),y=null)}function x(e){e&&e!==y&&!e.classList.contains("js-editing")&&(v(),y=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function w(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;E(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,L.delete(n+"::"+s),S(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function E(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,l=e.dataset.origValue,i="true"===e.dataset.origNull,r=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":l)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=c(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+a(r)+'">'+a(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+a(r)+'">'+a(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function k(e){return"true"===e.dataset.null?null:e.dataset.value??""}const L=new Map;function I(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=c(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const r=(l.type||"").toUpperCase(),p="BOOLEAN"===r||"BOOL"===r,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),L.delete(s+"::"+o),E(t,i?null:a),void S();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),E(t,i?null:a),L.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:d(o),isFk:!!u(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),S()}function S(){if(!j||!C)return;const e=L.size;C.textContent=String(e),j.classList.toggle("hidden",0===e)}function T(){if(!L.size||t)return;const e=new Map;for(const t of L.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];B&&(B.disabled=!0),Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),B&&(B.disabled=!1)})}async function A(){const e=L.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of L.values())E(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;L.clear(),S(),UI.showToast("Pending edits discarded.","info")}}function N(){if(L.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";L.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+a(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+a(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+a(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+L.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">&times;</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+L.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=L.get(t);n&&(w(n.td),e.closest("tr").remove(),L.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),T()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await A()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let j=null,C=null,B=null;function U(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!c(e.dataset.col))return;x(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=k(e);const o=function(e){const t=e.dataset.col,n=c(t);if(!n)return null;const s=u(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,i="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),d="DATETIME"===o||o.includes("TIMESTAMP"),p=a||i||d,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,E="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=k(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",m(S,"","(not set / NULL)"),(r[t]||[]).forEach(e=>{const t=m(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(p)S=document.createElement("input"),S.type=a?"date":i?"time":"datetime-local",(i||d)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=f(String(L),a,i)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):E?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?I(n,e,o):function(e){E(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?R("down"):"right"===s?R("right"):"left"===s&&R("left"))};e._finish=a,o.addEventListener("keydown",e=>{"Enter"===e.key?(e.preventDefault(),a(!0,"down")):"Tab"===e.key?(e.preventDefault(),a(!0,e.shiftKey?"left":"right")):"Escape"===e.key&&(e.preventDefault(),a(!1))}),"SELECT"===o.tagName||"checkbox"===o.type?o.addEventListener("change",()=>a(!0)):o.addEventListener("blur",()=>a(!0)),o.focus(),"function"==typeof o.select&&o.select()}function R(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!y){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&x(n[0])}return}const n=y.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(y);if("right"===e)if(o>=0&&o<s.length-1)x(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&x(n[0])}}else if("left"===e)if(o>0)x(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&x(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}}function M(){j=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),C=document.getElementById("changes-count"),B=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");B&&B.addEventListener("click",T),e&&e.addEventListener("click",A),n&&n.addEventListener("click",N),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&w(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&x(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>U(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&y)if("ArrowRight"===e.key)e.preventDefault(),R("right");else if("ArrowLeft"===e.key)e.preventDefault(),R("left");else if("ArrowDown"===e.key)e.preventDefault(),R("down");else if("ArrowUp"===e.key)e.preventDefault(),R("up");else if("Tab"===e.key)e.preventDefault(),R(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||U(y);else if("Escape"===e.key)v();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),z(y);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=k(y);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=c(y.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=y.closest("tr"),n=k(y),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";I(t,y,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),z(y)}})}function q(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function O(e){const t=q();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(s),a.download=n.filename,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(a.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function D(){const e=q();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const i=n&&n.references||[],r=i.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+i.map(e=>'<li><code class="font-mono font-bold">'+a(e.table)+"."+a(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:r,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function W(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let P=null;function _(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),P&&(W(P,!1),P.setAttribute("aria-expanded","false"),P.classList.remove("opacity-50"),P=null)}function V(t,n){_();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let l='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+a(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+a(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+a(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+a(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?l+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+a(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+a(s)+'">'+a(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':l+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),l=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+l>window.innerHeight-8&&(c=Math.max(8,i.top-l-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",P=t;const d=e=>{e&&e.target&&s.contains(e.target)||_()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||_()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&_()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}function z(e){if(!e)return;const n=e.closest("tr");if(!n)return;const l=e.dataset.col,a="true"===e.dataset.null,i=e.dataset.value,r=n.dataset.pk,d=n.dataset.table||s.table,u="true"===e.dataset.blob,p="true"===e.dataset.json,f="true"===e.dataset.image,m="true"===e.dataset.audio,h="true"===e.dataset.video,g="true"===e.dataset.pdf,b=e.querySelector("img")&&e.querySelector("img").src||"",y=e.dataset.type||(c(l)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({dbId:o,table:d,pk:r,col:l,colType:y,value:i,isNull:a,isBlob:u,isJson:p,isImage:f,isAudio:m,isVideo:h,isPdf:g,blobUrl:b,readonly:t,onSave:s=>{if(!t){I(n,e,{value:s,type:"text"})}}})}function H(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),l=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),r=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!l)return;l.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),r=i?t.slice(4):"",u=!i&&d(t),p=i?null:c(t),f=p?p.type:"",m=i?r:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+a(r):a(t))+'">'+a(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+a(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),l.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),l.appendChild(h)})}let E=null;r&&r.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!d(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{i=await Api.get("/api/tables/"+o+"/"+l+"/info")}catch(e){i=null}try{r=await Api.get("/api/tables/"+o+"/"+l+"/fk-options")}catch(e){r={}}H(),b(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/"+l+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),M(),function(){let e=null;document.addEventListener("click",e=>{const t=e.target.closest(".js-inspect-btn"),n=e.target.closest("td.cell-editable");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&z(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),z(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,r="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||r||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(c(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:r,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&I(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear"),r=Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"));function c(){const l=q().length;s&&(s.textContent=String(l)),e.classList.toggle("hidden",0===l),n&&(n.checked=l>0&&l===r.length),o&&(o.disabled=t||0===l)}r.forEach(e=>e.addEventListener("change",c)),n&&n.addEventListener("change",()=>{r.forEach(e=>{e.checked=n.checked}),c()}),i&&i.addEventListener("click",()=>{r.forEach(e=>{e.checked=!1}),c()}),l&&l.addEventListener("click",()=>O("csv")),a&&a.addEventListener("click",()=>O("json")),o&&o.addEventListener("click",D)}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+o+"/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.table,s=t.value,o=t.dataset.orderby||"",l=t.dataset.orderdir||"asc",a=t.dataset.filters||"";let i=e+"/tables/"+encodeURIComponent(n)+"?page=1&size="+encodeURIComponent(s)+"&orderBy="+encodeURIComponent(o)+"&orderDir="+encodeURIComponent(l);a&&(i+="&"+a),window.location.href=i})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void _();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),W(e,!0),V(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
1
+ !function(){"use strict";const e=window.APP&&window.APP.basePath||"",t=!(!window.APP||!window.APP.readonly),n=document.getElementById("browse-config"),s=n?JSON.parse(n.textContent):{table:"",filters:{},pkCols:[],hasPk:!1,readonly:t},o=encodeURIComponent(window.APP&&window.APP.dbId||s&&s.dbId||""),l=encodeURIComponent(s.table),a=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let i=null,r={};function c(e){return i?i.columns.find(t=>t.name===e):null}function d(e){return!!i&&-1!==i.primaryKey.indexOf(e)}function u(e){return i?i.foreignKeys.find(t=>t.from===e):null}function p(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function f(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function m(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function h(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=u(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,d="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||d||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",m(n,"","(any)"),m(n,"__NULL__","(not set / NULL)"),(r[e.name]||[]).forEach(e=>m(n,String(e.value),""===e.label?String(e.value):e.label));let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",m(n,"","(any)"),m(n,"1","true"),m(n,"0","false");let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||i){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":d?"time":"datetime-local",(d||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=p(y,"gte")??p(y,"gt")??a.min,r=p(y,"lte")??p(y,"lt")??a.max;return null!=i&&(s.value=g?f(i,c,d):String(i)),null!=r&&(o.value=g?f(r,c,d):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=p(y,"prefix"),t=p(y,"eq"),n=p(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function g(t){let n=s.table;if(!n){const e=window.location.pathname.match(/\/tables\/[^/]+\/([^/]+)(?:\/|$)/);e&&(n=decodeURIComponent(e[1]))}if(!n)return;const l=document.getElementById("page-size"),a=new URLSearchParams({page:"1",size:l?l.value:"50"});Object.keys(t).length&&a.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+o+"/"+encodeURIComponent(n)+"?"+a.toString()}function b(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&i&&(i.columns.forEach(e=>n.appendChild(h(e))),e.addEventListener("submit",e=>{e.preventDefault(),g(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>g({}))}let y=null;function v(){y&&(y.classList.remove("cell-focused"),y=null)}function x(e){e&&e!==y&&!e.classList.contains("js-editing")&&(v(),y=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function w(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;E(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,L.delete(n+"::"+s),S(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function E(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,l=e.dataset.origValue,i="true"===e.dataset.origNull,r=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":l)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=c(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+a(r)+'">'+a(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+a(r)+'">'+a(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function k(e){return"true"===e.dataset.null?null:e.dataset.value??""}const L=new Map;function I(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=c(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const r=(l.type||"").toUpperCase(),p="BOOLEAN"===r||"BOOL"===r,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),L.delete(s+"::"+o),E(t,i?null:a),void S();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),E(t,i?null:a),L.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:d(o),isFk:!!u(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),S()}function S(){if(!j||!C)return;const e=L.size;C.textContent=String(e),j.classList.toggle("hidden",0===e)}function T(){if(!L.size||t)return;const e=new Map;for(const t of L.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];B&&(B.disabled=!0),Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),B&&(B.disabled=!1)})}async function A(){const e=L.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of L.values())E(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;L.clear(),S(),UI.showToast("Pending edits discarded.","info")}}function N(){if(L.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";L.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+a(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+a(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+a(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+L.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">&times;</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+L.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=L.get(t);n&&(w(n.td),e.closest("tr").remove(),L.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),T()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await A()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let j=null,C=null,B=null;function U(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!c(e.dataset.col))return;x(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=k(e);const o=function(e){const t=e.dataset.col,n=c(t);if(!n)return null;const s=u(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,i="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),d="DATETIME"===o||o.includes("TIMESTAMP"),p=a||i||d,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,E="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=k(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",m(S,"","(not set / NULL)"),(r[t]||[]).forEach(e=>{const t=m(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(p)S=document.createElement("input"),S.type=a?"date":i?"time":"datetime-local",(i||d)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=f(String(L),a,i)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):E?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?I(n,e,o):function(e){E(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?R("down"):"right"===s?R("right"):"left"===s&&R("left"))};e._finish=a,o.addEventListener("keydown",e=>{"Enter"===e.key?(e.preventDefault(),a(!0,"down")):"Tab"===e.key?(e.preventDefault(),a(!0,e.shiftKey?"left":"right")):"Escape"===e.key&&(e.preventDefault(),a(!1))}),"SELECT"===o.tagName||"checkbox"===o.type?o.addEventListener("change",()=>a(!0)):o.addEventListener("blur",()=>a(!0)),o.focus(),"function"==typeof o.select&&o.select()}function R(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!y){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&x(n[0])}return}const n=y.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(y);if("right"===e)if(o>=0&&o<s.length-1)x(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&x(n[0])}}else if("left"===e)if(o>0)x(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&x(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}}function M(){j=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),C=document.getElementById("changes-count"),B=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");B&&B.addEventListener("click",T),e&&e.addEventListener("click",A),n&&n.addEventListener("click",N),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&w(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&x(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>U(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&y)if("ArrowRight"===e.key)e.preventDefault(),R("right");else if("ArrowLeft"===e.key)e.preventDefault(),R("left");else if("ArrowDown"===e.key)e.preventDefault(),R("down");else if("ArrowUp"===e.key)e.preventDefault(),R("up");else if("Tab"===e.key)e.preventDefault(),R(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||U(y);else if("Escape"===e.key)v();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),z(y);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=k(y);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=c(y.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=y.closest("tr"),n=k(y),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";I(t,y,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),z(y)}})}function q(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function O(e){const t=q();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(s),a.download=n.filename,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(a.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function D(){const e=q();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const i=n&&n.references||[],r=i.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+i.map(e=>'<li><code class="font-mono font-bold">'+a(e.table)+"."+a(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:r,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function W(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let P=null;function _(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),P&&(W(P,!1),P.setAttribute("aria-expanded","false"),P.classList.remove("opacity-50"),P=null)}function V(t,n){_();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let l='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+a(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+a(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+a(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+a(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?l+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+a(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+a(s)+'">'+a(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':l+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),l=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+l>window.innerHeight-8&&(c=Math.max(8,i.top-l-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",P=t;const d=e=>{e&&e.target&&s.contains(e.target)||_()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||_()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&_()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}function z(e){if(!e)return;const n=e.closest("tr");if(!n)return;const l=e.dataset.col,a="true"===e.dataset.null,i=e.dataset.value,r=n.dataset.pk,d=n.dataset.table||s.table,u="true"===e.dataset.blob,p="true"===e.dataset.json,f="true"===e.dataset.image,m="true"===e.dataset.audio,h="true"===e.dataset.video,g="true"===e.dataset.pdf,b=e.querySelector("img")&&e.querySelector("img").src||"",y=e.dataset.type||(c(l)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({dbId:o,table:d,pk:r,col:l,colType:y,value:i,isNull:a,isBlob:u,isJson:p,isImage:f,isAudio:m,isVideo:h,isPdf:g,blobUrl:b,readonly:t,onSave:s=>{if(!t){I(n,e,{value:s,type:"text"})}}})}function H(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),l=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),r=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!l)return;l.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),r=i?t.slice(4):"",u=!i&&d(t),p=i?null:c(t),f=p?p.type:"",m=i?r:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+a(r):a(t))+'">'+a(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+a(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),l.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),l.appendChild(h)})}let E=null;r&&r.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!d(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{i=await Api.get("/api/tables/"+o+"/"+l+"/info")}catch(e){i=null}try{r=await Api.get("/api/tables/"+o+"/"+l+"/fk-options")}catch(e){r={}}H(),b(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/"+l+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),M(),function(){let e=null;document.addEventListener("click",e=>{const t=e.target.closest(".js-inspect-btn"),n=e.target.closest("td.cell-editable");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&z(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),z(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,r="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||r||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(c(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:r,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&I(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear"),r=Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"));function c(){const l=q().length;s&&(s.textContent=String(l)),e.classList.toggle("hidden",0===l),n&&(n.checked=l>0&&l===r.length),o&&(o.disabled=t||0===l)}r.forEach(e=>e.addEventListener("change",c)),n&&n.addEventListener("change",()=>{r.forEach(e=>{e.checked=n.checked}),c()}),i&&i.addEventListener("click",()=>{r.forEach(e=>{e.checked=!1}),c()}),l&&l.addEventListener("click",()=>O("csv")),a&&a.addEventListener("click",()=>O("json")),o&&o.addEventListener("click",D)}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+o+"/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.db,s=t.dataset.table,o=t.value,l=t.dataset.orderby||"",a=t.dataset.orderdir||"asc",i=t.dataset.filters||"";let r=e+"/tables/"+encodeURIComponent(n)+"/"+encodeURIComponent(s)+"?page=1&size="+encodeURIComponent(o)+"&orderBy="+encodeURIComponent(l)+"&orderDir="+encodeURIComponent(a);i&&(r+="&"+i),window.location.href=r})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void _();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),W(e,!0),V(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
@@ -1 +1 @@
1
- !function(){"use strict";const e=encodeURIComponent(window.APP&&window.APP.dbId||""),t=document.getElementById("query-sql"),n=document.getElementById("query-code"),s=document.getElementById("query-highlight"),o=document.getElementById("query-gutter"),r=document.getElementById("editor-cursor-pos"),i=document.getElementById("editor-stats"),a=document.getElementById("run-query"),l=document.getElementById("format-query"),c=document.getElementById("copy-query"),d=document.getElementById("clear-query"),u=document.getElementById("query-message"),p=document.getElementById("query-result"),m=document.getElementById("query-result-head"),f=document.getElementById("query-result-body"),g=document.getElementById("result-summary"),h=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),w=document.getElementById("save-query-name"),y=document.getElementById("saved-query-select"),x=document.getElementById("delete-query"),v=document.getElementById("export-csv"),b=document.getElementById("export-json");let E=[],I=null;const L=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},T=window.Utils?window.Utils.highlightSql:function(e){return L(e)};const k=new class{constructor(e=100){this.stack=[],this.index=-1,this.limit=e,this.lastRecordTime=0}record(e,t,n=250){const s=Date.now(),o=this.index>=0?this.stack[this.index]:null;if(o&&o.text===e)o.selection=t;else{if(o&&s-this.lastRecordTime<n&&1===Math.abs(e.length-o.text.length)&&!/\s|[;(),]/.test(e.slice(-1)))return o.text=e,o.selection=t,void(this.lastRecordTime=s);this.stack=this.stack.slice(0,this.index+1),this.stack.push({text:e,selection:t}),this.stack.length>this.limit?this.stack.shift():this.index++,this.lastRecordTime=s}}undo(){return this.index>0?(this.index--,this.stack[this.index]):null}redo(){return this.index<this.stack.length-1?(this.index++,this.stack[this.index]):null}}(100);function q(){return t?t.value:""}function S(){if(!t||!n)return;const e=t.value;n.innerHTML=T(e)+(e.endsWith("\n")?" ":""),j(),B(),U()}function U(){t&&(s&&(s.scrollTop=t.scrollTop,s.scrollLeft=t.scrollLeft),o&&(o.scrollTop=t.scrollTop))}function R(e,n=!0){if(t&&(t.value=e||"",S(),n)){const t=(e||"").length;k.record(e||"",{start:t,end:t},0)}}function j(){if(!t||!o)return;const e=t.value.split("\n"),n=Math.max(1,e.length);let s="";for(let e=1;e<=n;e++){const t=null!=I&&e===I;s+='<div class="sql-gutter-line'+(t?" has-error":"")+'" data-line="'+e+'" title="'+(t?"Error on line "+e:"Line "+e)+'">'+e+"</div>"}o.innerHTML=s,o.scrollTop=t.scrollTop}function B(){if(!t||!r)return;const e=t.value,n=t.selectionStart||0,s=t.selectionEnd||0,o=e.slice(0,n).split("\n"),a=o.length,l=o[o.length-1].length+1;if(r.textContent="Ln "+a+", Col "+l,i){const t=e.length,o=e.split("\n").length,r=Math.abs(s-n);i.textContent=r>0?t+" chars ("+r+" selected) · "+o+" lines":t+" chars · "+o+" lines"}}function M(e,n){if(!t)return;const s=t.value.split("\n"),o=Math.max(1,Math.min(s.length,e||1));let r=0;for(let e=0;e<o-1;e++)r+=s[e].length+1;n&&n>1&&(r+=Math.min(s[o-1].length,n-1)),t.focus(),t.setSelectionRange(r,r);t.scrollTop=Math.max(0,21*(o-3)),U(),B()}function O(e,t){if(!u)return;if(!e)return void(u.innerHTML="");const n={error:"border-error/40 bg-error/10 text-error",success:"border-success/40 bg-success/10 text-success",info:"border-info/40 bg-info/10 text-info",warning:"border-warning/40 bg-warning/10 text-warning"};u.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border px-4 py-3 text-sm font-medium '+(n[t]||n.info)+'"><span>'+L(e)+"</span></div>"}function N(e){v&&(v.disabled=!e),b&&(b.disabled=!e)}async function A(t){const n=q().trim();if(n)try{const s=await fetch(Api.url("/api/query/"+e+"/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:n,format:t})});if(!s.ok){let e="Export failed ("+s.status+").";try{const t=await s.json();t&&t.error&&(e=t.error)}catch(e){}throw new Error(e)}const o=await s.blob(),r=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(o,"query-"+r+"."+t),window.UI&&UI.showToast&&UI.showToast("Exported as "+t.toUpperCase()+".","success")}catch(e){O(e.message,"error")}}l&&l.addEventListener("click",function(){const e=q();e.trim()&&(R(function(e){if(!e)return"";let t=e.trim().replace(/\r\n/g,"\n").replace(/\t/g," ");return["SELECT","FROM","WHERE","AND","OR","GROUP BY","HAVING","ORDER BY","LIMIT","OFFSET","LEFT JOIN","RIGHT JOIN","INNER JOIN","CROSS JOIN","FULL JOIN","JOIN","UNION ALL","UNION","INSERT INTO","VALUES","UPDATE","SET","DELETE FROM","CREATE TABLE","ALTER TABLE","DROP TABLE"].forEach(function(e){const n=new RegExp("\\b"+e.replace(" ","\\s+")+"\\b","gi");t=t.replace(n,"\n"+e.toUpperCase())}),t.split("\n").map(function(e){return e.trim()}).filter(function(e){return e.length>0}).map(function(e){const t=e.toUpperCase();return t.startsWith("WHERE")||t.startsWith("AND")||t.startsWith("OR")||t.startsWith("JOIN")||t.startsWith("LEFT")||t.startsWith("RIGHT")||t.startsWith("INNER")||t.startsWith("ON")||t.startsWith("SET")||t.startsWith("VALUES")||t.startsWith("HAVING")||t.startsWith("ORDER")||t.startsWith("GROUP")||t.startsWith("LIMIT")||t.startsWith("OFFSET")?" "+e:e}).join("\n")}(e)),t.focus(),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),c&&c.addEventListener("click",function(){const e=q();e.trim()&&navigator.clipboard.writeText(e).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),d&&d.addEventListener("click",function(){R(""),I=null,O("","info"),t.focus()}),document.querySelectorAll(".js-snippet").forEach(function(e){e.addEventListener("click",function(n){n.preventDefault();const s=e.getAttribute("data-snippet");s&&(R(s.replace(/\\n/g,"\n")),I=null,O("","info"),t.focus())})}),o&&o.addEventListener("click",function(e){const t=e.target.closest(".sql-gutter-line");if(!t)return;const n=parseInt(t.dataset.line,10);n&&M(n,1)}),t&&(t.addEventListener("input",function(){I=null,S(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},250)}),t.addEventListener("scroll",U),t.addEventListener("click",B),t.addEventListener("keyup",B),t.addEventListener("select",B),t.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()&&!e.shiftKey)return e.preventDefault(),void function(){const e=k.undo();e&&(t.value=e.text,S(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),B())}();if((e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()||(e.ctrlKey||e.metaKey)&&e.shiftKey&&"z"===e.key.toLowerCase())return e.preventDefault(),void function(){const e=k.redo();e&&(t.value=e.text,S(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),B())}();if((e.ctrlKey||e.metaKey)&&"Enter"===e.key)return e.preventDefault(),void K();if("Enter"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=(t.value.slice(0,n).split("\n").pop()||"").match(/^(\s+)/),r="\n"+(o?o[1]:"");return t.setRangeText(r,n,s,"end"),S(),void k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0)}if((e.ctrlKey||e.metaKey)&&"/"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=t.value,r=o.lastIndexOf("\n",n-1)+1;let i=o.indexOf("\n",s);-1===i&&(i=o.length);const a=o.slice(r,i).split("\n"),l=a.every(function(e){return e.trim().startsWith("--")}),c=a.map(function(e){return l?e.replace(/^(\s*)--\s?/,"$1"):e?"-- "+e:e}).join("\n");return t.setRangeText(c,r,i,"preserve"),t.setSelectionRange(r,r+c.length),S(),void k.record(t.value,{start:r,end:r+c.length},0)}if("Tab"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=t.value;if(n!==s||e.shiftKey){const r=o.lastIndexOf("\n",n-1)+1;let i=o.indexOf("\n",s);-1===i&&(i=o.length);const a=o.slice(r,i).split("\n").map(function(t){return e.shiftKey?t.replace(/^( |\t| )/,""):" "+t}).join("\n");t.setRangeText(a,r,i,"preserve"),t.setSelectionRange(r,r+a.length),S(),k.record(t.value,{start:r,end:r+a.length},0)}else t.setRangeText(" ",n,s,"end"),S(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0);return}})),v&&v.addEventListener("click",()=>A("csv")),b&&b.addEventListener("click",()=>A("json"));const C=window.Utils?window.Utils.isJson:function(e){return!1},H=window.Utils?window.Utils.isHexBlob:function(e){return!1};if(f){let e=null;f.addEventListener("click",function(e){const t=e.target.closest(".js-query-inspect"),n=e.target.closest(".js-query-cell");if(!t&&(!n||e.detail<2&&!e.altKey))return;const s=t?t.closest(".js-query-cell"):n;if(!s)return;const o=s.dataset.col||"Result",r=s.dataset.value||"",i="true"===s.dataset.blob,a="true"===s.dataset.json;window.Inspector&&window.Inspector.open&&window.Inspector.open({col:o,value:r,isNull:!1,isBlob:i,isJson:a,readonly:!0})}),f.addEventListener("mouseover",function(t){const n=t.target.closest(".js-query-cell");n&&(clearTimeout(e),e=setTimeout(function(){const e=n.dataset.value||"";if(!e||e.length<30)return;const t=n.dataset.col||"Result",s="true"===n.dataset.blob,o="true"===n.dataset.json;window.Inspector&&window.Inspector.peek&&window.Inspector.peek(n,{col:t,value:e,isBlob:s,isJson:o,readonly:!0})},450))}),f.addEventListener("mouseout",function(t){clearTimeout(e)})}function D(e,t){m.innerHTML="",f.innerHTML='<tr><td class="px-4 py-8 text-center"><p class="text-2xl font-extrabold text-primary">'+L(e)+'</p><p class="mt-1 text-sm text-base-content/45">'+L(t)+"</p></td></tr>"}function W(e){g&&(g.textContent=e)}async function K(){const t=q().trim();if(!t)return void O("Enter a SQL statement first.","warning");if(function(e){var t=e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim();return!!/\b(drop\s+(table|view|index|trigger)|truncate\b)/i.test(t)||!(!/\bdelete\s+from\b/i.test(t)||/\bwhere\b/i.test(t))}(t)){if(!await UI.confirm({title:"Execute Destructive SQL",message:"This SQL statement contains potentially destructive operations (e.g. DROP, TRUNCATE, or unrestricted DELETE) that may permanently erase database schemas or data.",detailsHtml:'<pre class="font-mono text-xs text-base-content/85 whitespace-pre-wrap break-all">'+L(t.length>350?t.slice(0,350)+"…":t)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}I=null,j(),u.innerHTML="",p.classList.add("hidden"),N(!1);const n=a?a.innerHTML:"";a&&(a.disabled=!0,a.innerHTML='<span class="loading loading-spinner loading-xs"></span> Running…');const s=performance.now();try{const n=await Api.post("/api/query/"+e,{sql:t}),i=Math.max(1,Math.round(performance.now()-s));p.classList.remove("hidden"),p.classList.remove("js-result-slide"),p.offsetWidth,p.classList.add("js-result-slide"),"count"===n.kind?(D(String(n.count),"count"),W("Count query · "+i+"ms")):"select"===n.kind?(o=n.columns,r=n.rows,m.innerHTML="<tr>"+o.map(e=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+L(e)+"</span></th>").join("")+"</tr>",r.length?f.innerHTML=r.map(e=>'<tr class="hover:bg-primary/[0.04]">'+o.map(t=>{const n=e[t];if(null==n)return'<td class="align-middle"><span class="chip chip-null">NULL</span></td>';const s=String(n),o=C(s),r=H(s),i=s.length>140?s.slice(0,140)+"…":s;return o?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-json="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':r?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-blob="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'"><div class="flex items-center justify-between gap-1 group/cell"><span class="truncate font-mono text-[13px]" title="'+L(s)+'">'+L(i)+"</span>"+(s.length>25?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-query-inspect shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></td>"}).join("")+"</tr>").join(""):f.innerHTML='<tr><td colspan="'+Math.max(1,o.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',W(n.rows.length+" row(s) · "+i+"ms"),N(!0)):(D(n.message||"Statement executed.","write"),W(null!=n.changes?n.changes+" row(s) affected · "+i+"ms":i+"ms")),setTimeout(function(){p.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}catch(e){p.classList.add("hidden"),function(e){if(!u)return;const t=e&&e.details,n=e&&e.message||t&&t.message||"Query failed.";if(!t||!t.snippet&&!t.cause&&!t.suggestion)return void O(n,"error");I=t.line||null,j();let s='<div class="sql-error-card space-y-3">';s+='<div class="flex flex-wrap items-center justify-between gap-2 border-b border-error/20 pb-2.5">',s+='<div class="flex items-center gap-2">',s+='<span class="grid h-6 w-6 place-items-center rounded-lg bg-error/15 text-error font-bold">✕</span>',s+='<span class="font-bold text-sm text-error">'+L(n)+"</span>",s+="</div>",t.line&&(s+='<div class="flex items-center gap-1.5">',s+='<span class="badge badge-sm badge-error font-mono font-bold">Line '+t.line+(t.column?", Col "+t.column:"")+"</span>",s+='<button type="button" class="btn btn-xs btn-outline btn-error js-jump-err-line" data-line="'+t.line+'" data-col="'+(t.column||1)+'">Jump to line</button>',s+="</div>"),s+="</div>",t.snippet&&(s+="<div>",s+='<p class="text-[11px] font-bold uppercase tracking-wider text-base-content/45 mb-1.5">Code Snippet</p>',s+='<pre class="sql-error-snippet">'+L(t.snippet)+"</pre>",s+="</div>"),(t.cause||t.suggestion)&&(s+='<div class="grid grid-cols-1 md:grid-cols-2 gap-2.5 pt-1">',t.cause&&(s+='<div class="rounded-lg border border-base-300 bg-base-100/90 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-base-content/80 mb-1">',s+="<span>💡</span><span>Possible Cause</span>",s+="</div>",s+='<p class="text-xs text-base-content/70 leading-relaxed">'+L(t.cause)+"</p>",s+="</div>"),t.suggestion&&(s+='<div class="rounded-lg border border-primary/30 bg-primary/5 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-primary mb-1">',s+="<span>🔧</span><span>Suggested Fix</span>",s+="</div>",s+='<p class="text-xs text-base-content/80 font-medium leading-relaxed">'+L(t.suggestion)+"</p>",s+="</div>"),s+="</div>"),s+="</div>",u.innerHTML=s;const o=u.querySelector(".js-jump-err-line");o&&o.addEventListener("click",function(){M(parseInt(o.dataset.line,10),parseInt(o.dataset.col,10))})}(e),setTimeout(function(){u.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}finally{a&&(a.disabled=!1,a.innerHTML=n)}var o,r}async function J(t){try{E=await Api.get("/api/queries/"+e)}catch(e){E=[]}y.innerHTML='<option value="">— select a saved query —</option>'+E.map(e=>'<option value="'+e.id+'"'+(t&&e.name===t?" selected":"")+">"+L(e.name)+"</option>").join(""),x.disabled=!0}async function P(){const t=q().trim(),n=w.value.trim();if(t)if(n)try{await Api.post("/api/queries/"+e,{name:n,sql:t}),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),await J(n)}catch(e){O(e.message,"error")}else O("Give the query a name.","warning");else O("Write some SQL before saving.","warning")}a&&a.addEventListener("click",K),h.forEach(e=>e.addEventListener("click",P)),y&&y.addEventListener("change",()=>{const e=y.value;if(x.disabled=!e,!e)return;const n=E.find(t=>String(t.id)===String(e));n&&(R(n.sql),w.value=n.name,I=null,O('Loaded "'+n.name+'".',"info"),t.focus())}),x&&x.addEventListener("click",async function(){const t=y.value;if(!t)return;const n=E.find(e=>String(e.id)===String(t)),s=n?n.name:"Saved query";if(await UI.confirm({title:"Delete Saved Query",message:'Are you sure you want to delete the saved query "'+s+'"?',item:s,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{await Api.del("/api/queries/"+e+"/"+t),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),await J()}catch(e){O(e.message,"error")}});const Q=q();Q?R(Q,!0):k.record("",{start:0,end:0},0),S()}();
1
+ !function(){"use strict";const e=encodeURIComponent(window.APP&&window.APP.dbId||""),t=document.getElementById("query-sql"),n=document.getElementById("query-code"),s=document.getElementById("query-highlight"),r=document.getElementById("query-gutter"),o=document.getElementById("editor-cursor-pos"),i=document.getElementById("editor-stats"),a=document.getElementById("run-query"),l=document.getElementById("format-query"),c=document.getElementById("copy-query"),d=document.getElementById("clear-query"),u=document.getElementById("query-message"),p=document.getElementById("query-result"),m=document.getElementById("query-result-head"),f=document.getElementById("query-result-body"),g=document.getElementById("result-summary"),h=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),y=document.getElementById("save-query-name"),v=document.getElementById("saved-query-select"),w=document.getElementById("delete-query"),x=document.getElementById("export-csv"),b=document.getElementById("export-json");let E=[],I=null;const L=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},T=window.Utils?window.Utils.highlightSql:function(e){return L(e)};const k=new class{constructor(e=100){this.stack=[],this.index=-1,this.limit=e,this.lastRecordTime=0}record(e,t,n=250){const s=Date.now(),r=this.index>=0?this.stack[this.index]:null;if(r&&r.text===e)r.selection=t;else{if(r&&s-this.lastRecordTime<n&&1===Math.abs(e.length-r.text.length)&&!/\s|[;(),]/.test(e.slice(-1)))return r.text=e,r.selection=t,void(this.lastRecordTime=s);this.stack=this.stack.slice(0,this.index+1),this.stack.push({text:e,selection:t}),this.stack.length>this.limit?this.stack.shift():this.index++,this.lastRecordTime=s}}undo(){return this.index>0?(this.index--,this.stack[this.index]):null}redo(){return this.index<this.stack.length-1?(this.index++,this.stack[this.index]):null}}(100);function S(){return t?t.value:""}function q(){if(!t||!n)return;const e=t.value;n.innerHTML=T(e)+(e.endsWith("\n")?" ":""),j(),O(),U()}function U(){t&&(s&&(s.scrollTop=t.scrollTop,s.scrollLeft=t.scrollLeft),r&&(r.scrollTop=t.scrollTop))}function R(e,n=!0){if(t&&(t.value=e||"",q(),n)){const t=(e||"").length;k.record(e||"",{start:t,end:t},0)}}function j(){if(!t||!r)return;const e=t.value.split("\n"),n=Math.max(1,e.length);let s="";for(let e=1;e<=n;e++){const t=null!=I&&e===I;s+='<div class="sql-gutter-line'+(t?" has-error":"")+'" data-line="'+e+'" title="'+(t?"Error on line "+e:"Line "+e)+'">'+e+"</div>"}r.innerHTML=s,r.scrollTop=t.scrollTop}function O(){if(!t||!o)return;const e=t.value,n=t.selectionStart||0,s=t.selectionEnd||0,r=e.slice(0,n).split("\n"),a=r.length,l=r[r.length-1].length+1;if(o.textContent="Ln "+a+", Col "+l,i){const t=e.length,r=e.split("\n").length,o=Math.abs(s-n);i.textContent=o>0?t+" chars ("+o+" selected) · "+r+" lines":t+" chars · "+r+" lines"}}function B(e,n){if(!t)return;const s=t.value.split("\n"),r=Math.max(1,Math.min(s.length,e||1));let o=0;for(let e=0;e<r-1;e++)o+=s[e].length+1;n&&n>1&&(o+=Math.min(s[r-1].length,n-1)),t.focus(),t.setSelectionRange(o,o);t.scrollTop=Math.max(0,21*(r-3)),U(),O()}function M(e,t){if(!u)return;if(!e)return void(u.innerHTML="");const n={error:"border-error/40 bg-error/10 text-error",success:"border-success/40 bg-success/10 text-success",info:"border-info/40 bg-info/10 text-info",warning:"border-warning/40 bg-warning/10 text-warning"};u.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border px-4 py-3 text-sm font-medium '+(n[t]||n.info)+'"><span>'+L(e)+"</span></div>"}function N(e){x&&(x.disabled=!e),b&&(b.disabled=!e)}async function C(t){const n=S().trim();if(n)try{const s=await fetch(Api.url("/api/query/"+e+"/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:n,format:t})});if(!s.ok){let e="Export failed ("+s.status+").";try{const t=await s.json();t&&t.error&&(e=t.error)}catch(e){}throw new Error(e)}const r=await s.blob(),o=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(r,"query-"+o+"."+t),window.UI&&UI.showToast&&UI.showToast("Exported as "+t.toUpperCase()+".","success")}catch(e){M(e.message,"error")}}l&&l.addEventListener("click",function(){const e=S();e.trim()&&(R(function(e){if(!e)return"";let t=e.trim().replace(/\r\n/g,"\n").replace(/\t/g," ");return["SELECT","FROM","WHERE","AND","OR","GROUP BY","HAVING","ORDER BY","LIMIT","OFFSET","LEFT JOIN","RIGHT JOIN","INNER JOIN","CROSS JOIN","FULL JOIN","JOIN","UNION ALL","UNION","INSERT INTO","VALUES","UPDATE","SET","DELETE FROM","CREATE TABLE","ALTER TABLE","DROP TABLE"].forEach(function(e){const n=new RegExp("\\b"+e.replace(" ","\\s+")+"\\b","gi");t=t.replace(n,"\n"+e.toUpperCase())}),t.split("\n").map(function(e){return e.trim()}).filter(function(e){return e.length>0}).map(function(e){const t=e.toUpperCase();return t.startsWith("WHERE")||t.startsWith("AND")||t.startsWith("OR")||t.startsWith("JOIN")||t.startsWith("LEFT")||t.startsWith("RIGHT")||t.startsWith("INNER")||t.startsWith("ON")||t.startsWith("SET")||t.startsWith("VALUES")||t.startsWith("HAVING")||t.startsWith("ORDER")||t.startsWith("GROUP")||t.startsWith("LIMIT")||t.startsWith("OFFSET")?" "+e:e}).join("\n")}(e)),t.focus(),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),c&&c.addEventListener("click",function(){const e=S();e.trim()&&navigator.clipboard.writeText(e).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),d&&d.addEventListener("click",function(){R(""),I=null,M("","info"),t.focus()}),document.querySelectorAll(".js-snippet").forEach(function(e){e.addEventListener("click",function(n){n.preventDefault();const s=e.getAttribute("data-snippet");s&&(R(s.replace(/\\n/g,"\n")),I=null,M("","info"),t.focus())})}),r&&r.addEventListener("click",function(e){const t=e.target.closest(".sql-gutter-line");if(!t)return;const n=parseInt(t.dataset.line,10);n&&B(n,1)}),t&&(t.addEventListener("input",function(){I=null,q(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},250)}),t.addEventListener("scroll",U),t.addEventListener("click",O),t.addEventListener("keyup",O),t.addEventListener("select",O),t.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()&&!e.shiftKey)return e.preventDefault(),void function(){const e=k.undo();e&&(t.value=e.text,q(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),O())}();if((e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()||(e.ctrlKey||e.metaKey)&&e.shiftKey&&"z"===e.key.toLowerCase())return e.preventDefault(),void function(){const e=k.redo();e&&(t.value=e.text,q(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),O())}();if((e.ctrlKey||e.metaKey)&&"Enter"===e.key)return e.preventDefault(),void J();if("Enter"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=(t.value.slice(0,n).split("\n").pop()||"").match(/^(\s+)/),o="\n"+(r?r[1]:"");return t.setRangeText(o,n,s,"end"),q(),void k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0)}if((e.ctrlKey||e.metaKey)&&"/"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=t.value,o=r.lastIndexOf("\n",n-1)+1;let i=r.indexOf("\n",s);-1===i&&(i=r.length);const a=r.slice(o,i).split("\n"),l=a.every(function(e){return e.trim().startsWith("--")}),c=a.map(function(e){return l?e.replace(/^(\s*)--\s?/,"$1"):e?"-- "+e:e}).join("\n");return t.setRangeText(c,o,i,"preserve"),t.setSelectionRange(o,o+c.length),q(),void k.record(t.value,{start:o,end:o+c.length},0)}if("Tab"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=t.value;if(n!==s||e.shiftKey){const o=r.lastIndexOf("\n",n-1)+1;let i=r.indexOf("\n",s);-1===i&&(i=r.length);const a=r.slice(o,i).split("\n").map(function(t){return e.shiftKey?t.replace(/^( |\t| )/,""):" "+t}).join("\n");t.setRangeText(a,o,i,"preserve"),t.setSelectionRange(o,o+a.length),q(),k.record(t.value,{start:o,end:o+a.length},0)}else t.setRangeText(" ",n,s,"end"),q(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0);return}})),x&&x.addEventListener("click",()=>C("csv")),b&&b.addEventListener("click",()=>C("json"));const A=window.Utils?window.Utils.isJson:function(e){return!1},H=window.Utils?window.Utils.isHexBlob:function(e){return!1};if(f){let e=null;f.addEventListener("click",function(e){const t=e.target.closest(".js-query-inspect"),n=e.target.closest(".js-query-cell");if(!t&&(!n||e.detail<2&&!e.altKey))return;const s=t?t.closest(".js-query-cell"):n;if(!s)return;const r=s.dataset.col||"Result",o=s.dataset.value||"",i="true"===s.dataset.blob,a="true"===s.dataset.json;window.Inspector&&window.Inspector.open&&window.Inspector.open({col:r,value:o,isNull:!1,isBlob:i,isJson:a,readonly:!0})}),f.addEventListener("mouseover",function(t){const n=t.target.closest(".js-query-cell");n&&(clearTimeout(e),e=setTimeout(function(){const e=n.dataset.value||"";if(!e||e.length<30)return;const t=n.dataset.col||"Result",s="true"===n.dataset.blob,r="true"===n.dataset.json;window.Inspector&&window.Inspector.peek&&window.Inspector.peek(n,{col:t,value:e,isBlob:s,isJson:r,readonly:!0})},450))}),f.addEventListener("mouseout",function(t){clearTimeout(e)})}function D(e,t){m.innerHTML="",f.innerHTML='<tr><td class="px-4 py-8 text-center"><p class="text-2xl font-extrabold text-primary">'+L(e)+'</p><p class="mt-1 text-sm text-base-content/45">'+L(t)+"</p></td></tr>"}function W(e){g&&(g.textContent=e)}async function J(){const t=S().trim();if(!t)return void M("Enter a SQL statement first.","warning");if(function(e){var t=e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim();return!!/\b(drop\s+(table|view|index|trigger)|truncate\b)/i.test(t)||!(!/\bdelete\s+from\b/i.test(t)||/\bwhere\b/i.test(t))}(t)){if(!await UI.confirm({title:"Execute Destructive SQL",message:"This SQL statement contains potentially destructive operations (e.g. DROP, TRUNCATE, or unrestricted DELETE) that may permanently erase database schemas or data.",detailsHtml:'<pre class="font-mono text-xs text-base-content/85 whitespace-pre-wrap break-all">'+L(t.length>350?t.slice(0,350)+"…":t)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}I=null,j(),u.innerHTML="",p.classList.add("hidden"),N(!1);const n=a?a.innerHTML:"";a&&(a.disabled=!0,a.innerHTML='<span class="loading loading-spinner loading-xs"></span> Running…');const s=performance.now();try{const n=await Api.post("/api/query/"+e,{sql:t}),i=Math.max(1,Math.round(performance.now()-s));p.classList.remove("hidden"),p.classList.remove("js-result-slide"),p.offsetWidth,p.classList.add("js-result-slide"),"count"===n.kind?(D(String(n.count),"count"),W("Count query · "+i+"ms")):"select"===n.kind?(r=n.columns,o=n.rows,m.innerHTML="<tr>"+r.map(e=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+L(e)+"</span></th>").join("")+"</tr>",o.length?f.innerHTML=o.map(e=>'<tr class="hover:bg-primary/[0.04]">'+r.map(t=>{const n=e[t];if(null==n)return'<td class="align-middle"><span class="chip chip-null">NULL</span></td>';const s=String(n),r=A(s),o=H(s),i=s.length>140?s.slice(0,140)+"…":s;return r?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-json="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':o?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-blob="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'"><div class="flex items-center justify-between gap-1 group/cell"><span class="truncate font-mono text-[13px]" title="'+L(s)+'">'+L(i)+"</span>"+(s.length>25?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-query-inspect shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></td>"}).join("")+"</tr>").join(""):f.innerHTML='<tr><td colspan="'+Math.max(1,r.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',W(n.rows.length+" row(s) · "+i+"ms"),N(!0)):(D(n.message||"Statement executed.","write"),W(null!=n.changes?n.changes+" row(s) affected · "+i+"ms":i+"ms")),setTimeout(function(){p.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}catch(e){p.classList.add("hidden"),function(e){if(!u)return;const t=e&&e.details,n=e&&e.message||t&&t.message||"Query failed.";if(!t||!t.snippet&&!t.cause&&!t.suggestion)return void M(n,"error");I=t.line||null,j();let s='<div class="sql-error-card space-y-3">';s+='<div class="flex flex-wrap items-center justify-between gap-2 border-b border-error/20 pb-2.5">',s+='<div class="flex items-center gap-2">',s+='<span class="grid h-6 w-6 place-items-center rounded-lg bg-error/15 text-error font-bold">✕</span>',s+='<span class="font-bold text-sm text-error">'+L(n)+"</span>",s+="</div>",t.line&&(s+='<div class="flex items-center gap-1.5">',s+='<span class="badge badge-sm badge-error font-mono font-bold">Line '+t.line+(t.column?", Col "+t.column:"")+"</span>",s+='<button type="button" class="btn btn-xs btn-outline btn-error js-jump-err-line" data-line="'+t.line+'" data-col="'+(t.column||1)+'">Jump to line</button>',s+="</div>"),s+="</div>",t.snippet&&(s+="<div>",s+='<p class="text-[11px] font-bold uppercase tracking-wider text-base-content/45 mb-1.5">Code Snippet</p>',s+='<pre class="sql-error-snippet">'+L(t.snippet)+"</pre>",s+="</div>"),(t.cause||t.suggestion)&&(s+='<div class="grid grid-cols-1 md:grid-cols-2 gap-2.5 pt-1">',t.cause&&(s+='<div class="rounded-lg border border-base-300 bg-base-100/90 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-base-content/80 mb-1">',s+="<span>💡</span><span>Possible Cause</span>",s+="</div>",s+='<p class="text-xs text-base-content/70 leading-relaxed">'+L(t.cause)+"</p>",s+="</div>"),t.suggestion&&(s+='<div class="rounded-lg border border-primary/30 bg-primary/5 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-primary mb-1">',s+="<span>🔧</span><span>Suggested Fix</span>",s+="</div>",s+='<p class="text-xs text-base-content/80 font-medium leading-relaxed">'+L(t.suggestion)+"</p>",s+="</div>"),s+="</div>"),s+="</div>",u.innerHTML=s;const r=u.querySelector(".js-jump-err-line");r&&r.addEventListener("click",function(){B(parseInt(r.dataset.line,10),parseInt(r.dataset.col,10))})}(e),setTimeout(function(){u.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}finally{a&&(a.disabled=!1,a.innerHTML=n)}var r,o}function K(){return"admindb_saved_queries_"+(decodeURIComponent(e)||"default")}function F(){try{const e=localStorage.getItem(K());if(e){const t=JSON.parse(e);if(Array.isArray(t))return t}}catch(e){console.warn("Failed to read saved queries from localStorage:",e)}return[]}function Q(e){try{localStorage.setItem(K(),JSON.stringify(e))}catch(e){console.warn("Failed to save queries to localStorage:",e)}}function P(e){E=F(),E.sort(function(e,t){return(e.name||"").localeCompare(t.name||"",void 0,{sensitivity:"base"})}),v&&(v.innerHTML='<option value="">— select a saved query —</option>'+E.map(function(t){return'<option value="'+L(t.id)+'"'+(e&&t.name===e?" selected":"")+">"+L(t.name)+"</option>"}).join("")),w&&(w.disabled=!v||!v.value)}function z(){const e=S().trim(),t=y?y.value.trim():"";if(e)if(t)try{const n=F(),s=n.findIndex(function(e){return(e.name||"").trim().toLowerCase()===t.toLowerCase()}),r=(new Date).toISOString();if(s>=0)n[s]={id:n[s].id,name:t,sql:e,updated_at:r};else{const s="sq_"+Date.now()+"_"+Math.random().toString(36).substring(2,7);n.push({id:s,name:t,sql:e,created_at:r})}Q(n),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),M('Query "'+t+'" saved to browser storage.',"success"),P(t)}catch(e){M("Failed to save query: "+e.message,"error")}else M("Give the query a name.","warning");else M("Write some SQL before saving.","warning")}a&&a.addEventListener("click",J),h.forEach(e=>e.addEventListener("click",z)),v&&v.addEventListener("change",()=>{const e=v.value;if(w.disabled=!e,!e)return;const n=E.find(t=>String(t.id)===String(e));n&&(R(n.sql),y&&(y.value=n.name),I=null,M('Loaded "'+n.name+'".',"info"),t.focus())}),w&&w.addEventListener("click",async function(){if(!v)return;const e=v.value;if(!e)return;const t=E.find(function(t){return String(t.id)===String(e)}),n=t?t.name:"Saved query";if(await UI.confirm({title:"Delete Saved Query",message:'Are you sure you want to delete the saved query "'+n+'" from browser storage?',item:n,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{Q(F().filter(function(t){return String(t.id)!==String(e)})),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),M("Query deleted.","info"),P(),y&&y.value===n&&(y.value="")}catch(e){M("Failed to delete query: "+e.message,"error")}});const G=S();G?R(G,!0):k.record("",{start:0,end:0},0),q(),P()}();
@@ -1 +1 @@
1
- import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export type ListSavedQueriesResponseData=SavedQuery[];export type ListSavedQueriesResponse=ApiResponse<ListSavedQueriesResponseData>;export interface SaveQueryRequestBody{name:string;sql:string;}export interface SaveQueryResponseData{message:string;}export type SaveQueryResponse=ApiResponse<SaveQueryResponseData>;export interface DeleteSavedQueryResponseData{message:string;}export type DeleteSavedQueryResponse=ApiResponse<DeleteSavedQueryResponseData>;export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
1
+ import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
@@ -1 +1 @@
1
- export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const isInternalTable:(name:string)=>boolean;export declare const encodePk:(values:unknown[])=>string;export declare const decodePk:(encoded:string)=>string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare const truncate:(str:string,maxLength:number)=>string;
1
+ export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const encodePk:(values:unknown[])=>string;export declare const decodePk:(encoded:string)=>string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare const truncate:(str:string,maxLength:number)=>string;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.truncate = exports.decodePk = exports.encodePk = exports.isInternalTable = exports.isPostgresConnectionString = exports.errorMessage = void 0;
6
+ exports.truncate = exports.decodePk = exports.encodePk = exports.isPostgresConnectionString = exports.errorMessage = void 0;
7
7
  exports.sanitizeConnectionString = sanitizeConnectionString;
8
8
  exports.isPathWithinRoot = isPathWithinRoot;
9
9
  exports.parseFilters = parseFilters;
@@ -99,8 +99,6 @@ function filtersToQS(filters) {
99
99
  }
100
100
  return Object.keys(out).length ? `f=${encodeURIComponent(JSON.stringify(out))}` : '';
101
101
  }
102
- const isInternalTable = (name) => name.startsWith('_');
103
- exports.isInternalTable = isInternalTable;
104
102
  const encodePk = (values) => values.map((v) => encodeURIComponent(v == null ? '' : String(v))).join(',');
105
103
  exports.encodePk = encodePk;
106
104
  const decodePk = (encoded) => String(encoded)
@@ -0,0 +1 @@
1
+ import{Transform} from 'node:stream';export declare function createJsonStream():Transform;export declare function createCsvStream(columns:string[]):Transform;export declare function parseCsvStreamBatched(stream:AsyncIterable<string|Buffer>,onBatch:(rows:string[][])=>Promise<void>,batchSize?:number):Promise<number>;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createJsonStream = createJsonStream;
4
+ exports.createCsvStream = createCsvStream;
5
+ exports.parseCsvStreamBatched = parseCsvStreamBatched;
6
+ const node_stream_1 = require("node:stream");
7
+ const csv_1 = require("./csv");
8
+ function createJsonStream() {
9
+ let isFirst = true;
10
+ return new node_stream_1.Transform({
11
+ writableObjectMode: true,
12
+ readableObjectMode: false,
13
+ transform(chunk, _encoding, callback) {
14
+ try {
15
+ const str = JSON.stringify(chunk, null, 2);
16
+ if (isFirst) {
17
+ isFirst = false;
18
+ this.push(`[\n ${str.replace(/\n/g, '\n ')}`);
19
+ }
20
+ else {
21
+ this.push(`,\n ${str.replace(/\n/g, '\n ')}`);
22
+ }
23
+ callback();
24
+ }
25
+ catch (err) {
26
+ callback(err);
27
+ }
28
+ },
29
+ flush(callback) {
30
+ if (isFirst) {
31
+ this.push('[]');
32
+ }
33
+ else {
34
+ this.push('\n]');
35
+ }
36
+ callback();
37
+ },
38
+ });
39
+ }
40
+ function createCsvStream(columns) {
41
+ let headerSent = false;
42
+ return new node_stream_1.Transform({
43
+ writableObjectMode: true,
44
+ readableObjectMode: false,
45
+ transform(chunk, _encoding, callback) {
46
+ try {
47
+ if (!headerSent) {
48
+ headerSent = true;
49
+ const header = columns.map(csv_1.csvEscape).join(',');
50
+ this.push(`${header}\r\n`);
51
+ }
52
+ const line = columns.map((c) => (0, csv_1.csvEscape)(chunk[c])).join(',');
53
+ this.push(`${line}\r\n`);
54
+ callback();
55
+ }
56
+ catch (err) {
57
+ callback(err);
58
+ }
59
+ },
60
+ flush(callback) {
61
+ if (!headerSent) {
62
+ this.push(`${columns.map(csv_1.csvEscape).join(',')}\r\n`);
63
+ }
64
+ callback();
65
+ },
66
+ });
67
+ }
68
+ async function parseCsvStreamBatched(stream, onBatch, batchSize = 500) {
69
+ let buffer = '';
70
+ let inQuotes = false;
71
+ let row = [];
72
+ let field = '';
73
+ let totalRows = 0;
74
+ let batch = [];
75
+ for await (const chunk of stream) {
76
+ const text = typeof chunk === 'string' ? chunk : chunk.toString('utf-8');
77
+ buffer += text;
78
+ for (let i = 0; i < buffer.length; i++) {
79
+ const ch = buffer[i];
80
+ if (inQuotes) {
81
+ if (ch === '"') {
82
+ if (buffer[i + 1] === '"') {
83
+ field += '"';
84
+ i++;
85
+ }
86
+ else {
87
+ inQuotes = false;
88
+ }
89
+ }
90
+ else {
91
+ field += ch;
92
+ }
93
+ }
94
+ else if (ch === '"') {
95
+ inQuotes = true;
96
+ }
97
+ else if (ch === ',') {
98
+ row.push(field);
99
+ field = '';
100
+ }
101
+ else if (ch === '\n') {
102
+ row.push(field);
103
+ field = '';
104
+ if (row.length > 1 || (row[0] !== undefined && row[0] !== '')) {
105
+ batch.push(row);
106
+ totalRows++;
107
+ if (batch.length >= batchSize) {
108
+ await onBatch(batch);
109
+ batch = [];
110
+ }
111
+ }
112
+ row = [];
113
+ }
114
+ else if (ch !== '\r') {
115
+ field += ch;
116
+ }
117
+ }
118
+ buffer = '';
119
+ }
120
+ if (field || row.length > 0) {
121
+ row.push(field);
122
+ if (row.length > 1 || (row[0] !== undefined && row[0] !== '')) {
123
+ batch.push(row);
124
+ totalRows++;
125
+ }
126
+ }
127
+ if (batch.length > 0) {
128
+ await onBatch(batch);
129
+ }
130
+ return totalRows;
131
+ }