admindb 2.2.1 → 2.3.1
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.
- package/README.md +368 -364
- package/dist/app.js +25 -16
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +18 -30
- package/dist/auth/crypto.js +11 -21
- package/dist/auth/middleware.js +9 -16
- package/dist/auth/routes.js +9 -15
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +20 -29
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +15 -18
- package/dist/cli/runner.js +13 -13
- package/dist/data/chain.d.ts +1 -1
- package/dist/data/chain.js +40 -617
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +43 -1
- package/dist/data/detector.js +45 -21
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +483 -142
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +8 -0
- package/dist/data/prng.d.ts +1 -0
- package/dist/data/prng.js +73 -0
- package/dist/data/registry.d.ts +1 -0
- package/dist/data/registry.js +805 -0
- package/dist/data/schema-graph.d.ts +1 -0
- package/dist/data/schema-graph.js +210 -0
- package/dist/data/strategies.d.ts +1 -1
- package/dist/data/strategies.js +44 -283
- package/dist/data/templates.d.ts +1 -0
- package/dist/data/templates.js +134 -0
- package/dist/data/types.d.ts +1 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.js +9 -14
- package/dist/db/export.js +32 -55
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +15 -38
- package/dist/db/introspection.js +4 -7
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +48 -53
- package/dist/db/migrations.js +7 -14
- package/dist/db/postgres.js +30 -78
- package/dist/index.d.ts +1 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +3 -8
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +75 -25
- package/dist/routes/api/import-export.js +1 -2
- package/dist/routes/api/query.js +2 -6
- package/dist/routes/api/rows.js +4 -8
- package/dist/routes/api/seed.js +35 -21
- package/dist/routes/api/tables.js +15 -30
- package/dist/routes/databases.js +8 -26
- package/dist/routes/pages.js +67 -18
- package/dist/serverless.js +31 -44
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +35 -109
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +45 -85
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +11 -17
- package/dist/utils/datatype.js +46 -60
- package/dist/utils/icons.js +8 -8
- package/dist/views/layouts/main.hbs +1 -87
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -1241
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -220
- package/dist/views/pages/info.hbs +1 -107
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -129
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +256 -414
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -129
- package/docs/API.md +149 -149
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +80 -79
package/dist/public/js/browse.js
CHANGED
|
@@ -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(s.table),l=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let a=null,i={};function r(e){return a?a.columns.find(t=>t.name===e):null}function c(e){return a?a.foreignKeys.find(t=>t.from===e):null}function d(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function u(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 p(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function m(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=c(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,r=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,m="DATE"===l,f="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),b=m||f||h,g=s.filters[e.name],v=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(g),y=function(e){return"string"==typeof e?e:null}(g);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",p(n,"","(any)"),p(n,"__NULL__","(not set / NULL)"),(i[e.name]||[]).forEach(e=>p(n,String(e.value),""===e.label?String(e.value):e.label));let s=d(v,"eq");return null==s&&y&&y.startsWith("=")&&(s=y.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",p(n,"","(any)"),p(n,"1","true"),p(n,"0","false");let s=d(v,"eq");return null==s&&y&&y.startsWith("=")&&(s=y.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(b||r){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(b)s.type=o.type=m?"date":f?"time":"datetime-local",(f||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=b?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=b?"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}}(v),i=d(v,"gte")??d(v,"gt")??a.min,r=d(v,"lte")??d(v,"lt")??a.max;return null!=i&&(s.value=b?u(i,m,f):String(i)),null!=r&&(o.value=b?u(r,m,f):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const w=document.createElement("input");w.type="text",w.className="filter-input field-input !py-1.5 !text-[13px]",w.dataset.column=e.name,w.dataset.kind="text",w.placeholder="BLOB"===l?"0x… or text":"filter…",w.autocomplete="off",w.spellcheck=!1;let x=y;if(null==x){const e=d(v,"prefix"),t=d(v,"eq"),n=d(v,"like");null!=e?x=String(e)+"*":null!=t?x="="+String(t):null!=n&&(x=String(n))}return null!=x&&(w.value=x),t.appendChild(w),t}function f(t){let n="";const s=window.location.pathname.match(/\/tables\/([^/]+)(?:\/|$)/);if(s&&(n=decodeURIComponent(s[1])),!n)return;const o=document.getElementById("page-size"),l=new URLSearchParams({page:"1",size:o?o.value:"50"});Object.keys(t).length&&l.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+encodeURIComponent(n)+"?"+l.toString()}function h(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&a&&(a.columns.forEach(e=>n.appendChild(m(e))),e.addEventListener("submit",e=>{e.preventDefault(),f(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",()=>f({}))}let b=null;function g(){b&&(b.classList.remove("cell-focused"),b=null)}function v(e){e&&e!==b&&!e.classList.contains("js-editing")&&(g(),b=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function y(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;w(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,E.delete(n+"::"+s),L(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function w(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,a=e.dataset.origValue,i="true"===e.dataset.origNull,c=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":a)+")":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=r(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),m="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),f="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(m){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=f?'<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="'+l(c)+'">'+l(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="'+l(c)+'">'+l(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 x(e){return"true"===e.dataset.null?null:e.dataset.value??""}const E=new Map;function k(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;if(!r(o))return;let l,i=!1;if("checkbox"===n.type)l=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(l="",i=!0):l=e}var d;void 0===t.dataset.origValue&&(t.dataset.origValue=t.dataset.value,t.dataset.origNull="true"===t.dataset.null?"true":"false"),w(t,i?null:l),E.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:l,setNull:i,isPk:(d=o,!!a&&-1!==a.primaryKey.indexOf(d)),isFk:!!c(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),L()}function L(){if(!N||!T)return;const e=E.size;T.textContent=String(e),N.classList.toggle("hidden",0===e)}function I(){if(!E.size||t)return;const e=new Map;for(const t of E.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()];j&&(j.disabled=!0),Api.post("/api/tables/"+o+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),j&&(j.disabled=!1)})}async function S(){const e=E.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 E.values())w(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;E.clear(),L(),UI.showToast("Pending edits discarded.","info")}}function A(){if(E.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="";E.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+l(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+l(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="'+l(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">'+E.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">×</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 '+E.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=E.get(t);n&&(y(n.td),e.closest("tr").remove(),E.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),I()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await S()})}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 N=null,T=null,j=null;function C(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(!r(e.dataset.col))return;v(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=x(e);const o=function(e){const t=e.dataset.col,n=r(t);if(!n)return null;const s=c(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,d="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),m="DATETIME"===o||o.includes("TIMESTAMP"),f=a||d||m,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,b="BLOB"===o||"BYTEA"===o||"BINARY"===o,g="UUID"===o,v=o.includes("JSON"),y=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),w="INTERVAL"===o,E="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,k="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=x(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",p(S,"","(not set / NULL)"),(i[t]||[]).forEach(e=>{const t=p(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(f)S=document.createElement("input"),S.type=a?"date":d?"time":"datetime-local",(d||m)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=u(String(L),a,d)),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 g?(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))):y?(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))):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="e.g. '1 day'",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="(x, y)",I||(S.value=String(L))):k?(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=b?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":v?'{"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?k(n,e,o):function(e){w(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?U("down"):"right"===s?U("right"):"left"===s&&U("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 U(e){if(!b){const e=Array.prototype.slice.call(document.querySelectorAll("#main-table tbody tr td.cell-editable"));return void(e.length&&v(e[0]))}const t=b.closest("tr");if(!t)return;const n=Array.prototype.slice.call(t.querySelectorAll("td.cell-editable")),s=n.indexOf(b);if("right"===e)if(s<n.length-1)v(n[s+1]);else{const e=t.nextElementSibling;if(e){const t=Array.prototype.slice.call(e.querySelectorAll("td.cell-editable"));t.length&&v(t[0])}}else if("left"===e)if(s>0)v(n[s-1]);else{const e=t.previousElementSibling;if(e){const t=Array.prototype.slice.call(e.querySelectorAll("td.cell-editable"));t.length&&v(t[t.length-1])}}else if("down"===e){const e=t.nextElementSibling;if(e){const t=Array.prototype.slice.call(e.querySelectorAll("td.cell-editable"));t[s]&&v(t[s])}}else if("up"===e){const e=t.previousElementSibling;if(e){const t=Array.prototype.slice.call(e.querySelectorAll("td.cell-editable"));t[s]&&v(t[s])}}}function B(){N=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}(),T=document.getElementById("changes-count"),j=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");j&&j.addEventListener("click",I),e&&e.addEventListener("click",S),n&&n.addEventListener("click",A),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&&y(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&v(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>C(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&&b)if("ArrowRight"===e.key)e.preventDefault(),U("right");else if("ArrowLeft"===e.key)e.preventDefault(),U("left");else if("ArrowDown"===e.key)e.preventDefault(),U("down");else if("ArrowUp"===e.key)e.preventDefault(),U("up");else if("Tab"===e.key)e.preventDefault(),U(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||C(b);else if("Escape"===e.key)g();else if(" "!==e.key||t){if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=x(b);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=r(b.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=b.closest("tr"),n=x(b),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";k(t,b,{value:s,type:"checkbox",checked:"1"===s})}}})}function R(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function M(e){const t=R();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/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"}),l=document.createElement("a");l.href=URL.createObjectURL(s),l.download=n.filename,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(l.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function O(){const e=R();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const a=n&&n.references||[],i=a.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">'+a.map(e=>'<li><code class="font-mono font-bold">'+l(e.table)+"."+l(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:i,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function D(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let W=null;function q(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),W&&(D(W,!1),W.setAttribute("aria-expanded","false"),W.classList.remove("opacity-50"),W=null)}function _(t,n){q();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 a='<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+'">'+l(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+l(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+l(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+l(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?a+'<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">'+l(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="'+l(s)+'">'+l(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>':a+'<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)),a=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+a>window.innerHeight-8&&(c=Math.max(8,i.top-a-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",W=t;const d=e=>{e&&e.target&&s.contains(e.target)||q()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||q()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&q()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}!async function(){try{a=await Api.get("/api/tables/"+o+"/info")}catch(e){a=null}try{i=await Api.get("/api/tables/"+o+"/fk-options")}catch(e){i={}}h(),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+"/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)})}),B(),document.addEventListener("click",e=>{const n=e.target.closest(".js-inspect-btn");if(!n)return;e.stopPropagation(),e.preventDefault();const o=n.closest("td"),l=n.closest("tr");if(!o||!l)return;const a=o.dataset.col,i="true"===o.dataset.null,c=o.dataset.value,d=l.dataset.pk,u=l.dataset.table||s.table,p="true"===o.dataset.blob,m="true"===o.dataset.json,f=o.dataset.type||(r(a)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({table:u,pk:d,col:a,colType:f,value:c,isNull:i,isBlob:p,isJson:m,readonly:t,onSave:e=>{t||k(l,o,{value:e,type:"text"})}})}),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=R().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",()=>M("csv")),a&&a.addEventListener("click",()=>M("json")),o&&o.addEventListener("click",O)}(),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/"+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"),o=document.getElementById("import-csv-file"),l=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")),o&&o.addEventListener("change",()=>{const e=o.files&&o.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),l&&s&&l.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[1]):"";if(!n)return UI.showError("Could not determine the table.");l.disabled=!0;try{const t=await Api.post("/api/tables/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{l.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void q();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+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"),D(e,!0),_(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(s.table),l=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let a=null,i={};function r(e){return a?a.columns.find(t=>t.name===e):null}function c(e){return!!a&&-1!==a.primaryKey.indexOf(e)}function d(e){return a?a.foreignKeys.find(t=>t.from===e):null}function u(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function p(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 f(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function m(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=d(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,r=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,m="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||m||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",f(n,"","(any)"),f(n,"__NULL__","(not set / NULL)"),(i[e.name]||[]).forEach(e=>f(n,String(e.value),""===e.label?String(e.value):e.label));let s=u(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",f(n,"","(any)"),f(n,"1","true"),f(n,"0","false");let s=u(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||r){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":m?"time":"datetime-local",(m||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=u(y,"gte")??u(y,"gt")??a.min,r=u(y,"lte")??u(y,"lt")??a.max;return null!=i&&(s.value=g?p(i,c,m):String(i)),null!=r&&(o.value=g?p(r,c,m):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=u(y,"prefix"),t=u(y,"eq"),n=u(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 h(t){let n="";const s=window.location.pathname.match(/\/tables\/([^/]+)(?:\/|$)/);if(s&&(n=decodeURIComponent(s[1])),!n)return;const o=document.getElementById("page-size"),l=new URLSearchParams({page:"1",size:o?o.value:"50"});Object.keys(t).length&&l.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+encodeURIComponent(n)+"?"+l.toString()}function g(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&a&&(a.columns.forEach(e=>n.appendChild(m(e))),e.addEventListener("submit",e=>{e.preventDefault(),h(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",()=>h({}))}let b=null;function y(){b&&(b.classList.remove("cell-focused"),b=null)}function v(e){e&&e!==b&&!e.classList.contains("js-editing")&&(y(),b=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function x(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;w(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,k.delete(n+"::"+s),I(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function w(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,a=e.dataset.origValue,i="true"===e.dataset.origNull,c=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":a)+")":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=r(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="'+l(c)+'">'+l(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="'+l(c)+'">'+l(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 E(e){return"true"===e.dataset.null?null:e.dataset.value??""}const k=new Map;function L(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=r(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 u=(l.type||"").toUpperCase(),p="BOOLEAN"===u||"BOOL"===u,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"),k.delete(s+"::"+o),w(t,i?null:a),void I();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),w(t,i?null:a),k.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:c(o),isFk:!!d(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),I()}function I(){if(!N||!j)return;const e=k.size;j.textContent=String(e),N.classList.toggle("hidden",0===e)}function S(){if(!k.size||t)return;const e=new Map;for(const t of k.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()];C&&(C.disabled=!0),Api.post("/api/tables/"+o+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),C&&(C.disabled=!1)})}async function T(){const e=k.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 k.values())w(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;k.clear(),I(),UI.showToast("Pending edits discarded.","info")}}function A(){if(k.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="";k.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':l(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+l(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+l(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="'+l(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">'+k.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">×</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 '+k.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=k.get(t);n&&(x(n.td),e.closest("tr").remove(),k.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),S()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await T()})}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 N=null,j=null,C=null;function B(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(!r(e.dataset.col))return;v(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=E(e);const o=function(e){const t=e.dataset.col,n=r(t);if(!n)return null;const s=d(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,c="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),u="DATETIME"===o||o.includes("TIMESTAMP"),m=a||c||u,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,k="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=E(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",f(S,"","(not set / NULL)"),(i[t]||[]).forEach(e=>{const t=f(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(m)S=document.createElement("input"),S.type=a?"date":c?"time":"datetime-local",(c||u)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=p(String(L),a,c)),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))):k?(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?L(n,e,o):function(e){w(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?U("down"):"right"===s?U("right"):"left"===s&&U("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 U(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!b){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&v(n[0])}return}const n=b.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(b);if("right"===e)if(o>=0&&o<s.length-1)v(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&v(n[0])}}else if("left"===e)if(o>0)v(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&v(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=b.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)v(s);else{const n=t(e);(n[o]||n[0])&&v(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=b.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)v(s);else{const n=t(e);(n[o]||n[0])&&v(n[o]||n[0])}}}}function M(){N=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}(),j=document.getElementById("changes-count"),C=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");C&&C.addEventListener("click",S),e&&e.addEventListener("click",T),n&&n.addEventListener("click",A),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&&x(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&v(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>B(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&&b)if("ArrowRight"===e.key)e.preventDefault(),U("right");else if("ArrowLeft"===e.key)e.preventDefault(),U("left");else if("ArrowDown"===e.key)e.preventDefault(),U("down");else if("ArrowUp"===e.key)e.preventDefault(),U("up");else if("Tab"===e.key)e.preventDefault(),U(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||B(b);else if("Escape"===e.key)y();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),V(b);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=E(b);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=r(b.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=b.closest("tr"),n=E(b),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";L(t,b,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),V(b)}})}function R(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function q(e){const t=R();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/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"}),l=document.createElement("a");l.href=URL.createObjectURL(s),l.download=n.filename,document.body.appendChild(l),l.click(),l.remove(),URL.revokeObjectURL(l.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function O(){const e=R();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const a=n&&n.references||[],i=a.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">'+a.map(e=>'<li><code class="font-mono font-bold">'+l(e.table)+"."+l(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:i,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function D(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let W=null;function P(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),W&&(D(W,!1),W.setAttribute("aria-expanded","false"),W.classList.remove("opacity-50"),W=null)}function _(t,n){P();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 a='<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+'">'+l(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+l(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+l(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+l(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?a+'<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">'+l(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="'+l(s)+'">'+l(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>':a+'<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)),a=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+a>window.innerHeight-8&&(c=Math.max(8,i.top-a-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",W=t;const d=e=>{e&&e.target&&s.contains(e.target)||P()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||P()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&P()};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 V(e){if(!e)return;const n=e.closest("tr");if(!n)return;const o=e.dataset.col,l="true"===e.dataset.null,a=e.dataset.value,i=n.dataset.pk,c=n.dataset.table||s.table,d="true"===e.dataset.blob,u="true"===e.dataset.json,p="true"===e.dataset.image,f="true"===e.dataset.audio,m="true"===e.dataset.video,h="true"===e.dataset.pdf,g=e.querySelector("img")&&e.querySelector("img").src||"",b=e.dataset.type||(r(o)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({table:c,pk:i,col:o,colType:b,value:a,isNull:l,isBlob:d,isJson:u,isImage:p,isAudio:f,isVideo:m,isPdf:h,blobUrl:g,readonly:t,onSave:s=>{if(!t){L(n,e,{value:s,type:"text"})}}})}function z(){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"),a=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),d=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(!a)return;a.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:"),d=i?t.slice(4):"",u=!i&&c(t),p=i?null:r(t),f=p?p.type:"",m=i?d: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: "+l(d):l(t))+'">'+l(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">'+l(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"),a.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),a.appendChild(h)})}let E=null;d&&d.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!c(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{a=await Api.get("/api/tables/"+o+"/info")}catch(e){a=null}try{i=await Api.get("/api/tables/"+o+"/fk-options")}catch(e){i={}}z(),g(),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+"/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&&V(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),V(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,c="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||c||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||(r(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:c,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&L(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=R().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",()=>q("csv")),a&&a.addEventListener("click",()=>q("json")),o&&o.addEventListener("click",O)}(),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/"+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"),o=document.getElementById("import-csv-file"),l=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")),o&&o.addEventListener("change",()=>{const e=o.files&&o.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),l&&s&&l.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[1]):"";if(!n)return UI.showError("Could not determine the table.");l.disabled=!0;try{const t=await Api.post("/api/tables/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{l.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void P();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+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"),D(e,!0),_(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},t=window.Utils?window.Utils.formatBytes:function(e){return e+" B"},n=window.Utils?window.Utils.isJson:function(e){return!1};function s(t,n,a){var o=document.createElement("div");o.className="json-tree-node font-mono text-[13px] leading-relaxed my-0.5";var i=void 0!==n?'<span class="json-key text-primary font-semibold select-text">'+e(n)+'</span><span class="text-base-content/40 mr-1.5">:</span>':"";if(null===t)return o.innerHTML=i+'<span class="chip chip-null text-xs font-bold">null</span>',o;if("boolean"==typeof t)return o.innerHTML=i+'<span class="text-secondary font-bold select-text">'+t+"</span>",o;if("number"==typeof t)return o.innerHTML=i+'<span class="text-accent font-mono font-medium select-text">'+t+"</span>",o;if("string"==typeof t)return o.innerHTML=i+'<span class="text-success select-text break-all">"'+e(t)+'"</span>',o;if(Array.isArray(t)){var r=a||t.length<=10,l=document.createElement("div");l.className="flex items-center gap-1 cursor-pointer hover:bg-base-200/50 rounded px-1 -mx-1",l.innerHTML='<button type="button" class="json-toggle text-base-content/40 hover:text-base-content text-[11px] w-4 text-center">'+(r?"▼":"▶")+"</button>"+i+'<span class="text-base-content/60 font-semibold">[</span><span class="text-xs text-base-content/40 tabular-nums">'+t.length+" item"+(1===t.length?"":"s")+'</span><span class="text-base-content/60 font-semibold">'+(r?"":" ... ]")+"</span>";var c=document.createElement("div");if(c.className="pl-5 border-l border-base-300/60 ml-2 space-y-0.5 "+(r?"":"hidden"),t.forEach(function(e,t){c.appendChild(s(e,String(t),!1))}),r){var d=document.createElement("div");d.className="text-base-content/60 font-semibold pl-1",d.textContent="]",c.appendChild(d)}return l.addEventListener("click",function(){var e=!c.classList.contains("hidden");c.classList.toggle("hidden",e),l.querySelector(".json-toggle").textContent=e?"▶":"▼"}),o.appendChild(l),o.appendChild(c),o}if("object"==typeof t){var p=Object.keys(t),b=a||p.length<=10,u=document.createElement("div");u.className="flex items-center gap-1 cursor-pointer hover:bg-base-200/50 rounded px-1 -mx-1",u.innerHTML='<button type="button" class="json-toggle text-base-content/40 hover:text-base-content text-[11px] w-4 text-center">'+(b?"▼":"▶")+"</button>"+i+'<span class="text-base-content/60 font-semibold">{</span><span class="text-xs text-base-content/40 tabular-nums">'+p.length+" key"+(1===p.length?"":"s")+'</span><span class="text-base-content/60 font-semibold">'+(b?"":" ... }")+"</span>";var m=document.createElement("div");if(m.className="pl-5 border-l border-base-300/60 ml-2 space-y-0.5 "+(b?"":"hidden"),p.forEach(function(e){m.appendChild(s(t[e],e,!1))}),b){var x=document.createElement("div");x.className="text-base-content/60 font-semibold pl-1",x.textContent="}",m.appendChild(x)}return u.addEventListener("click",function(){var e=!m.classList.contains("hidden");m.classList.toggle("hidden",e),u.querySelector(".json-toggle").textContent=e?"▶":"▼"}),o.appendChild(u),o.appendChild(m),o}return o.innerHTML=i+'<span class="text-base-content select-text">'+e(String(t))+"</span>",o}window.Inspector={open:function(a){var o,i=a.table||"",r=a.pk||"",l=a.col||"",c=(a.colType||"").toUpperCase(),d=a.value,p=a.isNull||null==d,b=!!a.readonly,u=a.onSave,m=p?"":String(d),x=a.isBlob||c.includes("BLOB")||(o=m,!(!window.Utils||!window.Utils.isHexBlob(o))||"string"==typeof o&&(/^0x[0-9a-f]{4,}$/i.test(o.trim())||/^\\x[0-9a-f]{4,}$/i.test(o.trim()))),f=a.isJson||n(m),v=f?"json":x?"blob":"text",h=document.createElement("div");h.className="fixed inset-0 z-[120] flex items-center justify-center bg-black/70 backdrop-blur-xs p-3 md:p-6 opacity-0 transition-opacity duration-200",h.setAttribute("role","dialog"),h.setAttribute("aria-modal","true");var g=document.createElement("div");g.className="app-card flex flex-col w-full max-w-4xl max-h-[92vh] overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 bg-base-100";var y=document.createElement("div");y.className="flex items-center justify-between gap-3 border-b border-base-200 px-5 py-3.5 bg-base-200/30",y.innerHTML='<div class="flex items-center gap-3 min-w-0"> <span class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-primary/10 text-primary font-mono font-bold text-xs">'+(x?"🗃️":f?"{ }":"📝")+' </span> <div class="min-w-0"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content truncate">'+e(l)+'</h3> <span class="chip chip-type text-[10px]">'+e(c||(x?"BLOB":f?"JSON":"TEXT"))+"</span>"+(p?'<span class="chip chip-null">NULL</span>':"")+' </div> <p class="text-xs text-base-content/50 truncate">Table <span class="font-mono font-semibold">'+e(i)+"</span>"+(r?' · PK <span class="font-mono">'+e(r)+"</span>":"")+'</p> </div></div><div class="flex items-center gap-2"> <button type="button" class="btn btn-ghost btn-xs btn-circle js-inspector-close text-base-content/40 hover:text-base-content" aria-label="Close">×</button></div>',g.appendChild(y);var w=document.createElement("div");w.className="flex items-center justify-between border-b border-base-200 bg-base-200/20 px-5 py-2",w.innerHTML='<div class="flex gap-1" id="inspector-tabs">'+(f||!x&&m.length>0?'<button type="button" data-tab="json" class="btn btn-xs '+("json"===v?"btn-primary":"btn-ghost")+' gap-1">🧩 JSON Tree & Editor</button>':"")+(x?'<button type="button" data-tab="blob" class="btn btn-xs '+("blob"===v?"btn-primary":"btn-ghost")+' gap-1">🖼️ Media & Hex Dump</button>':"")+'<button type="button" data-tab="text" class="btn btn-xs '+("text"===v?"btn-primary":"btn-ghost")+' gap-1">📝 Raw Text / Code</button></div><div id="tab-meta" class="text-xs text-base-content/50 font-mono"></div>',g.appendChild(w);var L=document.createElement("div");L.className="flex-1 overflow-y-auto p-5 space-y-4 min-h-[350px]",g.appendChild(L);var C=document.createElement("div");C.className="flex flex-wrap items-center justify-between gap-3 border-t border-base-200 bg-base-200/30 px-5 py-3.5",C.innerHTML='<div class="flex items-center gap-2" id="inspector-left-actions"></div><div class="flex items-center gap-2"> <button type="button" class="btn btn-ghost btn-sm js-inspector-close">Close</button>'+(b?"":' <button type="button" id="inspector-save-btn" class="btn btn-primary btn-sm gap-1.5">Save Changes</button>')+"</div>",g.appendChild(C),h.appendChild(g),document.body.appendChild(h),requestAnimationFrame(function(){h.classList.remove("opacity-0"),h.classList.add("opacity-100"),g.classList.remove("scale-95"),g.classList.add("scale-100")});var N=g.querySelector("#tab-meta"),E=g.querySelector("#inspector-left-actions"),T=g.querySelector("#inspector-save-btn"),S=m;function j(){L.innerHTML='<div class="py-12 text-center text-sm text-base-content/40 font-mono">Loading BLOB data...</div>',E.innerHTML="",N.textContent="";var t=(window.APP&&window.APP.basePath||"")+"/api/tables/"+encodeURIComponent(i)+"/row/"+encodeURIComponent(r)+"/blob/"+encodeURIComponent(l);Api.get(t+"/meta").then(function(n){var s=n.data||{};N.textContent=(s.mime||"BLOB")+" · "+(s.sizeFormatted||"0 B"),L.innerHTML="";var a=document.createElement("div");if(a.className="space-y-4",s.isImage){var o=document.createElement("div");o.className="flex flex-col items-center justify-center p-4 rounded-xl border border-base-300 bg-base-200/40 min-h-[220px]",o.innerHTML='<div class="relative group max-w-full max-h-[300px] overflow-hidden rounded-lg shadow-sm"> <img src="'+t+'" alt="'+e(l)+'" class="max-h-[280px] max-w-full object-contain rounded-lg transition-transform" id="blob-preview-img" /></div><div class="mt-3 flex items-center gap-3 text-xs text-base-content/60 font-mono"> <span>Format: <b>'+e(s.ext.toUpperCase())+"</b></span> <span>Size: <b>"+e(s.sizeFormatted)+'</b></span> <a href="'+t+'" target="_blank" class="btn btn-ghost btn-xs text-primary">Open in new tab ↗</a></div>',a.appendChild(o)}else if(s.isAudio){var i=document.createElement("div");i.className="flex flex-col items-center justify-center p-6 rounded-xl border border-base-300 bg-base-200/40",i.innerHTML='<audio controls class="w-full max-w-md mb-2"><source src="'+t+'" type="'+e(s.mime)+'"></audio><div class="text-xs text-base-content/50 font-mono">'+e(s.mime)+" · "+e(s.sizeFormatted)+"</div>",a.appendChild(i)}var r=document.createElement("div");r.className="space-y-2";var c=document.createElement("div");c.className="flex items-center justify-between",c.innerHTML='<h4 class="text-xs font-semibold uppercase tracking-wider text-base-content/50 font-mono">Hex Dump (Offset | Hex | ASCII)</h4><button type="button" id="btn-copy-hex" class="btn btn-ghost btn-xs">Copy Hex</button>',r.appendChild(c);var d=document.createElement("div");d.className="overflow-x-auto rounded-xl border border-base-300 bg-neutral p-3 font-mono text-xs leading-relaxed text-neutral-content max-h-[300px] select-text";var p=s.hexDump;if(p&&p.lines&&p.lines.length){var x=p.lines.map(function(t){return'<div class="grid grid-cols-[80px_1fr_150px] gap-3 hover:bg-white/[0.06] px-1 rounded"><span class="text-primary/70 select-none">'+t.offset+'</span><span class="text-accent/90">'+e(t.hex)+'</span><span class="text-neutral-content/70 border-l border-white/10 pl-2">'+e(t.ascii)+"</span></div>"}).join("");d.innerHTML=x}else d.innerHTML='<div class="text-center text-neutral-content/40 py-4">No binary content</div>';r.appendChild(d),a.appendChild(r),L.appendChild(a),c.querySelector("#btn-copy-hex").addEventListener("click",function(){m.startsWith("0x")||m.startsWith("0X")?navigator.clipboard.writeText(m).then(function(){UI.showToast("Hex data copied.","success")}):UI.showToast("Hex copied.","success")}),E.innerHTML='<a href="'+t+'?download=1" class="btn btn-outline btn-sm gap-1.5"> <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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg> Download File</a>'+(b?"":'<label class="btn btn-outline btn-sm gap-1.5 cursor-pointer"> <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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /></svg> Replace File <input type="file" id="blob-file-upload-input" class="hidden" /></label>');var f=E.querySelector("#blob-file-upload-input");f&&f.addEventListener("change",function(e){var n=e.target.files&&e.target.files[0];if(n){var s=new FileReader;s.onload=function(){var e=s.result;Api.put(t,{data:e,format:"base64"}).then(function(){UI.showToast("BLOB file uploaded successfully.","success"),j(),u&&u("0x...")}).catch(function(e){UI.showError(e.message)})},s.readAsDataURL(n)}})}).catch(function(t){L.innerHTML='<div class="alert alert-error text-xs p-3">Failed to load BLOB metadata: '+e(t.message)+"</div>"})}function H(n){w.querySelectorAll("#inspector-tabs button").forEach(function(e){var t=e.dataset.tab===n;e.className="btn btn-xs "+(t?"btn-primary":"btn-ghost")+" gap-1"}),"json"===n?function(){L.innerHTML="",E.innerHTML="",N.textContent="";var n=!1,a=null;try{a=JSON.parse(S),n=!0}catch(e){n=!1}if(n){var o="Size: "+t(new Blob([S]).size);N.textContent=o;var i=document.createElement("div");i.className="space-y-3";var r=document.createElement("div");r.className="flex flex-wrap items-center justify-between gap-2 pb-2 border-b border-base-200",r.innerHTML='<div class="flex items-center gap-1.5"> <button type="button" id="btn-view-tree" class="btn btn-xs btn-outline active">Tree View</button> <button type="button" id="btn-view-code" class="btn btn-xs btn-outline">Code Editor</button></div><div class="flex items-center gap-1.5"> <button type="button" id="btn-prettify-json" class="btn btn-ghost btn-xs">Beautify</button> <button type="button" id="btn-minify-json" class="btn btn-ghost btn-xs">Minify</button> <button type="button" id="btn-copy-json" class="btn btn-ghost btn-xs gap-1">Copy JSON</button></div>',i.appendChild(r);var l=document.createElement("div");l.className="rounded-xl border border-base-300 bg-base-200/30 p-4 max-h-[420px] overflow-y-auto",l.appendChild(s(a,void 0,!0)),i.appendChild(l);var c=document.createElement("div");c.className="hidden space-y-2",c.innerHTML='<textarea id="json-editor-ta" class="field-input font-mono text-[13px] h-96 w-full resize-none p-3.5" spellcheck="false">'+e(JSON.stringify(a,null,2))+'</textarea><div id="json-validation-msg" class="text-xs font-medium text-success">✓ Valid JSON</div>',i.appendChild(c),L.appendChild(i);var d=r.querySelector("#btn-view-tree"),p=r.querySelector("#btn-view-code"),b=c.querySelector("#json-editor-ta"),u=c.querySelector("#json-validation-msg");x("tree"),d.addEventListener("click",function(){x("tree")}),p.addEventListener("click",function(){x("code")}),r.querySelector("#btn-prettify-json").addEventListener("click",function(){var e=b.value||S;try{var t=JSON.parse(e);S=JSON.stringify(t,null,2),b.value=S,l.innerHTML="",l.appendChild(s(t,void 0,!0)),u.className="text-xs font-medium text-success",u.textContent="✓ Beautified valid JSON",x("code"),T&&(T.disabled=!1),UI.showToast("JSON beautified.","success")}catch(e){u.className="text-xs font-medium text-error",u.textContent="❌ Syntax Error: "+e.message,UI.showToast("Invalid JSON: "+e.message,"error")}}),r.querySelector("#btn-minify-json").addEventListener("click",function(){var e=b.value||S;try{var t=JSON.parse(e);S=JSON.stringify(t),b.value=S,l.innerHTML="",l.appendChild(s(t,void 0,!0)),u.className="text-xs font-medium text-success",u.textContent="✓ Minified valid JSON",x("code"),T&&(T.disabled=!1),UI.showToast("JSON minified.","success")}catch(e){u.className="text-xs font-medium text-error",u.textContent="❌ Syntax Error: "+e.message,UI.showToast("Invalid JSON: "+e.message,"error")}}),r.querySelector("#btn-copy-json").addEventListener("click",function(){var e=b.value||S;navigator.clipboard.writeText(e).then(function(){UI.showToast("JSON copied to clipboard.","success")})}),b.addEventListener("input",function(){S=b.value;try{JSON.parse(S),u.className="text-xs font-medium text-success",u.textContent="✓ Valid JSON",T&&(T.disabled=!1)}catch(e){u.className="text-xs font-medium text-error",u.textContent="❌ Syntax Error: "+e.message,T&&(T.disabled=!0)}})}else{L.innerHTML='<div class="alert alert-warning text-xs p-3"><span>Content is not valid JSON. You can edit raw text or format it.</span></div><textarea id="json-raw-input" class="field-input font-mono text-[13px] h-72 w-full resize-none p-3.5" spellcheck="false">'+e(S)+"</textarea>";var m=L.querySelector("#json-raw-input");m.addEventListener("input",function(){S=m.value})}function x(e){"tree"===e?(d.className="btn btn-xs btn-primary",p.className="btn btn-xs btn-outline",l.classList.remove("hidden"),c.classList.add("hidden")):(p.className="btn btn-xs btn-primary",d.className="btn btn-xs btn-outline",l.classList.add("hidden"),c.classList.remove("hidden"))}}():"blob"===n?j():function(){L.innerHTML="",E.innerHTML="",N.textContent="Length: "+S.length+" chars";var e=document.createElement("div");e.className="space-y-3";var t=document.createElement("div");t.className="flex items-center justify-between pb-2 border-b border-base-200",t.innerHTML='<span class="text-xs text-base-content/50 font-mono">Lines: '+S.split("\n").length+'</span><button type="button" id="btn-copy-text" class="btn btn-ghost btn-xs gap-1">Copy Text</button>',e.appendChild(t);var n=document.createElement("textarea");n.className="field-input font-mono text-[13px] h-80 w-full resize-none p-3.5",n.spellcheck=!1,n.value=S,b&&(n.readOnly=!0),n.addEventListener("input",function(){S=n.value,N.textContent="Length: "+S.length+" chars"}),e.appendChild(n),L.appendChild(e),t.querySelector("#btn-copy-text").addEventListener("click",function(){navigator.clipboard.writeText(S).then(function(){UI.showToast("Text copied to clipboard.","success")})})}()}function M(){h.classList.remove("opacity-100"),h.classList.add("opacity-0"),g.classList.remove("scale-100"),g.classList.add("scale-95"),setTimeout(function(){h.remove()},180)}w.querySelectorAll("#inspector-tabs button").forEach(function(e){e.addEventListener("click",function(){H(e.dataset.tab)})}),H(v),g.querySelectorAll(".js-inspector-close").forEach(function(e){e.addEventListener("click",M)}),h.addEventListener("click",function(e){e.target===h&&M()}),T&&T.addEventListener("click",function(){u&&u(S),M()})}}}();
|
|
1
|
+
!function(){"use strict";const e=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e).replace(/[&<>"']/g,function(e){return{"&":"&","<":"<",">":">",'"':""","'":"'"}[e]})},t=window.Utils?window.Utils.formatBytes:function(e){if(!e||0===e)return"0 B";var t=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,t)).toFixed(2))+" "+["B","KB","MB","GB"][t]},n=window.Utils?window.Utils.isJson:function(e){if("string"!=typeof e)return!1;var t=e.trim();if(!t||!t.startsWith("{")&&!t.startsWith("["))return!1;try{return JSON.parse(t),!0}catch(e){return!1}};function s(e){return window.Utils&&window.Utils.isHexBlob?window.Utils.isHexBlob(e):"string"==typeof e&&(/^0x[0-9a-f]{4,}$/i.test(e.trim())||/^\\x[0-9a-f]{4,}$/i.test(e.trim()))}const o='<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="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg>',a='<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="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2"/></svg>',r='<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="M4 6h16M4 12h16M4 18h7"/></svg>',i='<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="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>',l='<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="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>',c='<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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>',d='<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="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/></svg>',p='<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="M6 18L18 6M6 6l12 12"/></svg>',u='<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"><rect x="3" y="3" width="18" height="18" rx="2"/><path stroke-linecap="round" d="M15 3v18"/></svg>',b='<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"><rect x="3" y="3" width="18" height="18" rx="2"/><rect x="7" y="7" width="10" height="10" rx="1"/></svg>',m='<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="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5v-4m0 0h-4m4 0l-5-5"/></svg>',x='<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>',f='<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="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>';function v(t,n,s,o,a){var r=document.createElement("div");r.className="json-tree-row flex items-start gap-1 font-mono text-[13px] leading-relaxed py-0.5 px-2 rounded-lg transition-colors hover:bg-base-200/50 group/row select-text";var i=void 0!==s?void 0!==n?"number"==typeof n||/^\d+$/.test(n)?s+"["+n+"]":s?s+"."+n:n:s:void 0!==n?n:"$";r.dataset.path=i,r.addEventListener("mouseenter",function(e){e.stopPropagation(),a&&a(i)});var l=void 0!==n?'<span class="json-key text-sky-500 dark:text-sky-400 font-semibold select-text">'+h(e(String(n)),o)+'</span><span class="text-base-content/30 mx-1 select-none">:</span>':"",c='<div class="opacity-0 group-hover/row:opacity-100 transition-opacity flex items-center gap-1 ml-auto shrink-0 select-none pl-3"> <button type="button" class="btn btn-ghost btn-xs h-5 min-h-0 px-1.5 text-[10px] font-mono text-base-content/50 hover:text-primary json-copy-path" data-path="'+e(i)+'" title="Copy JSON path">path</button></div>';if(null===t)return r.innerHTML='<div class="flex items-center min-w-0">'+l+'<span class="chip chip-null text-[11px] font-semibold">null</span></div>'+c,r;if("boolean"==typeof t)return r.innerHTML='<div class="flex items-center min-w-0">'+l+'<span class="text-purple-600 dark:text-purple-400 font-bold select-text">'+t+"</span></div>"+c,r;if("number"==typeof t)return r.innerHTML='<div class="flex items-center min-w-0">'+l+'<span class="text-amber-600 dark:text-amber-400 font-medium select-text">'+t+"</span></div>"+c,r;if("string"==typeof t){var d=h(e(t),o);return r.innerHTML='<div class="flex items-start min-w-0 break-all">'+l+'<span class="text-emerald-600 dark:text-emerald-400 select-text">"'+d+'"</span></div>'+c,r}var p=Array.isArray(t),u=p?t:Object.keys(t),b=p?t.length:u.length,m=document.createElement("div");m.className="json-tree-branch font-mono text-[13px] my-0.5";var x=document.createElement("div");x.className="json-tree-header flex items-center justify-between gap-1 py-0.5 px-2 rounded-lg transition-colors hover:bg-base-200/50 cursor-pointer select-none group/header",x.innerHTML='<div class="flex items-center gap-1.5 min-w-0"> <span class="json-toggle text-base-content/40 group-hover/header:text-base-content transition-transform text-[10px] w-3.5 text-center shrink-0">▼</span> '+l+' <span class="text-base-content/70 font-bold">'+(p?"[":"{")+'</span> <span class="text-[11px] text-base-content/40 font-sans tabular-nums ml-0.5">'+b+" "+(1===b?p?"item":"key":p?"items":"keys")+'</span> <span class="text-base-content/70 font-bold json-collapsed-trailer hidden">'+(p?" ... ]":" ... }")+'</span></div><div class="opacity-0 group-hover/header:opacity-100 transition-opacity flex items-center gap-1 shrink-0"> <button type="button" class="btn btn-ghost btn-xs h-5 min-h-0 px-1.5 text-[10px] font-mono text-base-content/50 hover:text-primary json-copy-path" data-path="'+e(i)+'" title="Copy JSON path">path</button></div>',x.addEventListener("mouseenter",function(e){e.stopPropagation(),a&&a(i)});var f=document.createElement("div");f.className="json-tree-body pl-4 ml-2 border-l border-base-300/80 dark:border-base-content/10 space-y-0.5",p?t.forEach(function(e,t){f.appendChild(v(e,t,i,o,a))}):u.forEach(function(e){f.appendChild(v(t[e],e,i,o,a))});var w=document.createElement("div");return w.className="json-tree-closing pl-2 text-base-content/70 font-bold select-none text-[13px]",w.textContent=p?"]":"}",f.appendChild(w),x.addEventListener("click",function(e){if(!e.target.closest(".json-copy-path")){var t=f.classList.contains("hidden");f.classList.toggle("hidden",!t),x.querySelector(".json-toggle").textContent=t?"▼":"▶";var n=x.querySelector(".json-collapsed-trailer");n&&n.classList.toggle("hidden",t)}}),m.appendChild(x),m.appendChild(f),m}function h(e,t){if(!t||"string"!=typeof t||!t.trim())return e;var n=t.trim(),s=new RegExp("("+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+")","gi");return e.replace(s,'<mark class="bg-warning/30 text-warning-content rounded px-0.5 font-semibold">$1</mark>')}function w(e,t){t=t||8192;for(var n=Math.min(e.length,t),s=[],o=0;o<n;o+=16){for(var a=("00000000"+o.toString(16)).slice(-8),r=[],i=[],l=0;l<16;l++)if(o+l<n){var c=e[o+l];r.push(("00"+c.toString(16)).slice(-2)),i.push(c>=32&&c<=126?String.fromCharCode(c):".")}else r.push(" "),i.push(" ");var d=r.slice(0,8).join(" ")+" "+r.slice(8).join(" ");s.push({offset:a,hex:d,ascii:i.join("")})}return{lines:s,totalBytes:e.length,truncated:e.length>t}}function g(e){e=e.replace(/^0x/i,"").replace(/^\\x/i,"").replace(/\s+/g,"");for(var t=new Uint8Array(Math.floor(e.length/2)),n=0;n<e.length-1;n+=2)t[n/2]=parseInt(e.substring(n,n+2),16)||0;return t}function y(h){var y=(h=h||{}).table||"",k=h.pk||"",L=h.col||h.column||"Value",C=(h.colType||h.type||"").toUpperCase(),j=h.value,S=h.isNull||null==j,M=!!h.readonly,E=h.onSave,T=S?"":String(j),N=h.isBlob||C.includes("BLOB")||s(T)||h.isImage||h.isAudio||h.isVideo||h.isPdf,H=h.isJson||n(T)||C.includes("JSON"),q=H?"json":N?"blob":"text",U="drawer"===h.mode||"drawer"===window.localStorage.getItem("admindb_inspector_mode"),I=!1,B=document.createElement("div");B.className="fixed inset-0 z-[120] transition-opacity duration-200 opacity-0 "+(U?"bg-black/40 backdrop-blur-xs flex justify-end pointer-events-auto":"bg-black/65 backdrop-blur-xs p-3 sm:p-5 flex items-center justify-center"),B.setAttribute("role","dialog"),B.setAttribute("aria-modal","true");var O=document.createElement("div");O.className="app-card flex flex-col overflow-hidden shadow-2xl border border-base-300 transition-all duration-200 bg-base-100 "+(U?"w-full max-w-xl h-full rounded-none sm:rounded-l-2xl border-r-0 transform translate-x-full":"w-full max-w-4xl max-h-[90vh] rounded-2xl transform scale-95");var A=document.createElement("div");A.className="flex items-center justify-between gap-3 border-b border-base-200/80 px-5 py-3.5 bg-base-200/40 select-none",A.innerHTML='<div class="flex items-center gap-3 min-w-0"> <div class="grid h-9 w-9 shrink-0 place-items-center rounded-xl bg-primary/10 text-primary border border-primary/20" id="inspector-header-icon">'+(N?a:H?o:r)+' </div> <div class="min-w-0"> <div class="flex items-center gap-2 flex-wrap"> <span class="font-mono font-bold text-base text-base-content tracking-tight">'+e(L)+'</span> <span class="chip chip-type text-[10px]">'+e(C||(N?"BLOB":H?"JSON":"TEXT"))+"</span>"+(S?'<span class="chip chip-null text-[10px]">NULL</span>':"")+' </div> <div class="flex items-center gap-1.5 text-xs text-base-content/50 truncate font-mono mt-0.5">'+(y?"<span>"+e(y)+"</span>":"")+(k?'<span class="text-base-content/30">/</span><span class="text-base-content/70 font-semibold">PK '+e(k)+"</span>":"")+' </div> </div></div><div class="flex items-center gap-1 shrink-0"> <button type="button" id="btn-toggle-drawer" class="btn btn-ghost btn-xs btn-square text-base-content/50 hover:text-base-content" title="'+(U?"Switch to Center Modal":"Dock as Side Drawer")+'">'+(U?b:u)+' </button> <button type="button" id="btn-toggle-fullscreen" class="btn btn-ghost btn-xs btn-square text-base-content/50 hover:text-base-content" title="Toggle Fullscreen">'+m+'</button> <div class="h-4 w-px bg-base-300 mx-1"></div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-inspector-close text-base-content/40 hover:text-base-content" aria-label="Close">'+p+"</button></div>",O.appendChild(A);var J=document.createElement("div");J.className="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 bg-base-200/20 px-4 py-2 select-none",J.innerHTML='<div class="flex items-center gap-1.5" id="inspector-tabs"> <button type="button" data-tab="json" class="btn btn-xs '+("json"===q?"btn-primary":"btn-ghost")+' gap-1.5 font-medium">'+o+"JSON Tree</button>"+(N||s(T)?' <button type="button" data-tab="blob" class="btn btn-xs '+("blob"===q?"btn-primary":"btn-ghost")+' gap-1.5 font-medium">'+a+"Media & Hex</button>":"")+' <button type="button" data-tab="text" class="btn btn-xs '+("text"===q?"btn-primary":"btn-ghost")+' gap-1.5 font-medium">'+r+'Raw Code</button></div><div class="flex items-center gap-2" id="inspector-top-actions"></div>',O.appendChild(J);var z=document.createElement("div");z.className="flex-1 overflow-y-auto p-4 space-y-3 min-h-[360px] bg-base-100",O.appendChild(z);var P=document.createElement("div");P.className="flex flex-wrap items-center justify-between gap-3 border-t border-base-200 bg-base-200/30 px-4 py-2.5 select-none",P.innerHTML='<div class="flex items-center gap-3 text-xs font-mono text-base-content/50 min-w-0" id="inspector-status-bar"> <span id="tab-meta" class="truncate"></span> <span id="breadcrumb-path" class="text-primary/70 font-semibold truncate hidden"></span></div><div class="flex items-center gap-2 shrink-0"> <div id="inspector-left-actions" class="flex items-center gap-1.5"></div> <button type="button" class="btn btn-ghost btn-sm text-xs js-inspector-close">Close</button>'+(M?"":' <button type="button" id="inspector-save-btn" class="btn btn-primary btn-sm gap-1.5 text-xs font-semibold">'+f+"Save Changes</button>")+"</div>",O.appendChild(P),B.appendChild(O),document.body.appendChild(B),requestAnimationFrame(function(){B.classList.remove("opacity-0"),B.classList.add("opacity-100"),U?O.classList.remove("translate-x-full"):(O.classList.remove("scale-95"),O.classList.add("scale-100"))});var R=O.querySelector("#tab-meta"),V=O.querySelector("#breadcrumb-path"),D=O.querySelector("#inspector-top-actions"),F=O.querySelector("#inspector-left-actions"),_=O.querySelector("#inspector-save-btn"),$=O.querySelector("#inspector-header-icon"),W=T;function K(){I?O.className="app-card flex flex-col w-screen h-screen rounded-none overflow-hidden shadow-2xl border-0 bg-base-100 fixed inset-0 z-[130]":U?(O.className="app-card flex flex-col w-full max-w-xl h-full rounded-none sm:rounded-l-2xl border-r-0 overflow-hidden shadow-2xl border border-base-300 bg-base-100",B.className="fixed inset-0 z-[120] bg-black/40 backdrop-blur-xs flex justify-end pointer-events-auto transition-opacity duration-200 opacity-100"):(O.className="app-card flex flex-col w-full max-w-4xl max-h-[90vh] rounded-2xl overflow-hidden shadow-2xl border border-base-300 bg-base-100",B.className="fixed inset-0 z-[120] bg-black/65 backdrop-blur-xs p-3 sm:p-5 flex items-center justify-center transition-opacity duration-200 opacity-100");var e=O.querySelector("#btn-toggle-drawer");e&&(e.innerHTML=U?b:u)}function Z(){z.innerHTML='<div class="py-16 text-center text-sm text-base-content/40 font-mono flex flex-col items-center justify-center gap-2"><span class="loading loading-spinner loading-md text-primary"></span><span>Loading binary content & metadata…</span></div>',D.innerHTML="",F.innerHTML="",R.textContent="",V.textContent="",V.classList.add("hidden"),$&&($.innerHTML=a);var n=(window.APP&&window.APP.basePath||"")+"/api/tables/"+encodeURIComponent(y)+"/row/"+encodeURIComponent(k)+"/blob/"+encodeURIComponent(L);(y&&k&&window.Api?Api.get(n+"/meta"):Promise.resolve({data:{mime:"application/octet-stream",ext:"bin",size:Math.floor(T.length/2),sizeFormatted:t(Math.floor(T.length/2)),hexDump:w(g(T))}})).then(function(t){var s=t.data||{};R.textContent=(s.mime||"BLOB")+" · "+(s.sizeFormatted||"0 B"),z.innerHTML="";var o=document.createElement("div");if(o.className="space-y-4",s.isImage){var a=document.createElement("div");a.className="flex flex-col items-center justify-center p-4 rounded-2xl border border-base-300 bg-base-200/40 relative overflow-hidden",a.innerHTML='<div class="flex items-center justify-between w-full mb-3 pb-2 border-b border-base-200/60 px-2"> <span class="text-xs font-mono font-semibold text-base-content/70">Image Preview ('+e(s.ext.toUpperCase())+')</span> <div class="flex items-center gap-1"> <button type="button" id="btn-img-zoom-in" class="btn btn-ghost btn-xs font-mono" title="Zoom In">+</button> <button type="button" id="btn-img-zoom-out" class="btn btn-ghost btn-xs font-mono" title="Zoom Out">-</button> <button type="button" id="btn-img-rotate" class="btn btn-ghost btn-xs" title="Rotate 90°">Rotate</button> <button type="button" id="btn-img-reset" class="btn btn-ghost btn-xs" title="Reset View">Reset</button> </div></div><div class="relative flex items-center justify-center max-w-full max-h-[340px] overflow-auto rounded-xl p-3 bg-[radial-gradient(#8881_1px,transparent_1px)] [background-size:16px_16px] border border-base-300/60"> <img src="'+n+'" alt="'+e(L)+'" class="max-h-[300px] max-w-full object-contain rounded-lg shadow-sm transition-transform duration-150" id="blob-preview-img" /></div>',o.appendChild(a);var r=1,i=0,l=a.querySelector("#blob-preview-img");function C(){l.style.transform="scale("+r+") rotate("+i+"deg)"}a.querySelector("#btn-img-zoom-in").addEventListener("click",function(){r=Math.min(r+.25,4),C()}),a.querySelector("#btn-img-zoom-out").addEventListener("click",function(){r=Math.max(r-.25,.25),C()}),a.querySelector("#btn-img-rotate").addEventListener("click",function(){i=(i+90)%360,C()}),a.querySelector("#btn-img-reset").addEventListener("click",function(){r=1,i=0,C()})}else if(s.isAudio){var p=document.createElement("div");p.className="flex flex-col items-center justify-center p-6 rounded-2xl border border-base-300 bg-base-200/40 space-y-3",p.innerHTML='<h4 class="text-sm font-semibold text-base-content">Audio Stream · '+e(s.ext.toUpperCase())+'</h4><audio controls class="w-full max-w-md shadow-xs rounded-xl" preload="metadata"> <source src="'+n+'" type="'+e(s.mime)+'"></audio>',o.appendChild(p)}else if(s.isVideo){var u=document.createElement("div");u.className="flex flex-col items-center justify-center p-4 rounded-2xl border border-base-300 bg-base-200/40 space-y-3",u.innerHTML='<video controls class="max-h-[300px] max-w-full rounded-xl shadow-md bg-black" preload="metadata"> <source src="'+n+'" type="'+e(s.mime)+'"></video>',o.appendChild(u)}else if(s.isPdf){var b=document.createElement("div");b.className="space-y-3 rounded-2xl border border-base-300 bg-base-200/40 p-4",b.innerHTML='<iframe src="'+n+'#toolbar=0" class="w-full h-80 rounded-xl border border-base-300 bg-white" title="PDF"></iframe>',o.appendChild(b)}var m=document.createElement("div");m.className="space-y-2";var x=document.createElement("div");x.className="flex flex-wrap items-center justify-between gap-2",x.innerHTML='<div class="flex items-center gap-2"> <span class="text-xs font-semibold uppercase tracking-wider text-base-content/60 font-mono">Hex Dump</span> <input type="text" id="hex-search-input" placeholder="Search hex/ASCII…" class="field-input !py-0.5 !px-2 !text-xs !w-36 font-mono" /></div><div class="flex items-center gap-1.5"> <button type="button" id="btn-copy-hex-raw" class="btn btn-ghost btn-xs font-mono">Copy Hex</button> <button type="button" id="btn-copy-ascii" class="btn btn-ghost btn-xs font-mono">Copy ASCII</button></div>',m.appendChild(x);var f=document.createElement("div");f.className="overflow-x-auto rounded-xl border border-base-300 bg-neutral p-3.5 font-mono text-xs leading-relaxed text-neutral-content max-h-[320px] select-text shadow-inner";var v=s.hexDump;if(v&&v.lines&&v.lines.length){var h=v.lines.map(function(t,n){return'<div class="hex-line grid grid-cols-[80px_1fr_160px] gap-3 hover:bg-white/[0.08] px-1 rounded transition-colors" data-line-idx="'+n+'"><span class="text-primary/70 select-none font-bold">'+t.offset+'</span><span class="text-accent/90 hex-bytes">'+e(t.hex)+'</span><span class="text-neutral-content/75 border-l border-white/15 pl-2.5 ascii-text truncate">'+e(t.ascii)+"</span></div>"}).join("");f.innerHTML=h}else f.innerHTML='<div class="text-center text-neutral-content/40 py-6">No binary content available</div>';m.appendChild(f),o.appendChild(m),z.appendChild(o);var w=x.querySelector("#hex-search-input");if(w.addEventListener("input",function(){var e=w.value.trim().toLowerCase();f.querySelectorAll(".hex-line").forEach(function(t){e?t.textContent.toLowerCase().includes(e)?(t.classList.remove("hidden"),t.classList.add("bg-warning/20")):(t.classList.remove("bg-warning/20"),t.classList.add("hidden")):t.classList.remove("bg-warning/20","hidden")})}),y&&k){F.innerHTML='<a href="'+n+'?download=1" class="btn btn-outline btn-xs gap-1 font-medium">'+c+" Download</a>"+(M?"":'<label class="btn btn-outline btn-xs gap-1 cursor-pointer font-medium hover:border-primary">'+d+' Replace <input type="file" id="blob-file-upload-input" class="hidden" /></label>');var g=F.querySelector("#blob-file-upload-input");g&&g.addEventListener("change",function(e){var t=e.target.files&&e.target.files[0];if(t){var s=new FileReader;s.onload=function(){Api.put(n,{data:s.result,format:"base64"}).then(function(){window.UI&&window.UI.showToast&&window.UI.showToast("BLOB updated successfully.","success"),Z(),E&&E("0x...")}).catch(function(e){window.UI&&window.UI.showError&&window.UI.showError(e.message)})},s.readAsDataURL(t)}})}}).catch(function(t){z.innerHTML='<div class="alert alert-error text-xs p-3">Failed to load binary metadata: '+e(t.message)+"</div>"})}function G(n){J.querySelectorAll("#inspector-tabs button").forEach(function(e){var t=e.dataset.tab===n;e.className="btn btn-xs "+(t?"btn-primary":"btn-ghost")+" gap-1.5 font-medium"}),"json"===n?function(){z.innerHTML="",D.innerHTML="",F.innerHTML="",R.textContent="",V.textContent="",V.classList.add("hidden"),$&&($.innerHTML=o);var n=null,s=!1;try{n=JSON.parse(W),s=!0}catch(e){s=!1}if(s){var a=new Blob([W]).size;R.textContent=t(a),D.innerHTML='<div class="flex items-center gap-1.5"> <div class="relative flex items-center"> <input type="text" id="json-tree-search" placeholder="Filter keys/values…" class="field-input !py-1 !pl-7 !pr-2 !text-xs !w-40 font-mono" /> <span class="absolute left-2 text-base-content/40 pointer-events-none">'+i+'</span> </div> <div class="join border border-base-300 rounded-lg overflow-hidden bg-base-200/50"> <button type="button" id="btn-view-tree" class="btn btn-xs btn-primary join-item px-2.5 font-medium">Tree</button> <button type="button" id="btn-view-code" class="btn btn-xs btn-ghost join-item px-2.5 font-medium">Editor</button> </div> <div class="dropdown dropdown-end"> <button type="button" tabindex="0" class="btn btn-ghost btn-xs btn-square text-base-content/60" title="More Tools">•••</button> <ul tabindex="0" class="dropdown-content menu p-1.5 shadow-xl bg-base-100 border border-base-300 rounded-xl w-44 z-50 text-xs mt-1 space-y-0.5"> <li><button type="button" id="btn-expand-all" class="flex items-center gap-2">Expand All</button></li> <li><button type="button" id="btn-collapse-all" class="flex items-center gap-2">Collapse All</button></li> <li class="border-t border-base-200 my-1"></li> <li><button type="button" id="btn-prettify-json" class="flex items-center gap-2">Prettify (2 spaces)</button></li> <li><button type="button" id="btn-minify-json" class="flex items-center gap-2">Minify</button></li> <li><button type="button" id="btn-copy-json" class="flex items-center gap-2">Copy JSON</button></li> </ul> </div></div>';var r=document.createElement("div");r.className="rounded-xl border border-base-300 bg-base-200/30 p-3 max-h-[440px] overflow-y-auto";var l=v(n,void 0,"","",function(e){V.textContent=e,V.classList.remove("hidden")});r.appendChild(l),z.appendChild(r);var c=document.createElement("div");c.className="hidden space-y-2",c.innerHTML='<textarea id="json-editor-ta" class="field-input font-mono text-[13px] h-96 w-full resize-none p-3.5 leading-relaxed bg-base-200/20" spellcheck="false">'+e(JSON.stringify(n,null,2))+'</textarea><div class="flex items-center justify-between text-xs"> <div id="json-validation-msg" class="font-medium text-success flex items-center gap-1.5">'+x+' Valid JSON</div> <div class="text-base-content/40 font-mono" id="json-char-stats"></div></div>',z.appendChild(c);var d=D.querySelector("#btn-view-tree"),p=D.querySelector("#btn-view-code"),u=D.querySelector("#json-tree-search"),b=c.querySelector("#json-editor-ta"),m=c.querySelector("#json-validation-msg"),f=c.querySelector("#json-char-stats");g(),d.addEventListener("click",function(){y("tree")}),p.addEventListener("click",function(){y("code")});var h=null;u.addEventListener("input",function(){clearTimeout(h),h=setTimeout(function(){var e=u.value.trim();r.innerHTML="";var t=v(n,void 0,"",e,function(e){V.textContent=e,V.classList.remove("hidden")});r.appendChild(t)},120)}),D.querySelector("#btn-expand-all").addEventListener("click",function(){r.querySelectorAll(".json-tree-body").forEach(function(e){e.classList.remove("hidden")}),r.querySelectorAll(".json-toggle").forEach(function(e){e.textContent="▼"}),r.querySelectorAll(".json-collapsed-trailer").forEach(function(e){e.classList.add("hidden")})}),D.querySelector("#btn-collapse-all").addEventListener("click",function(){r.querySelectorAll(".json-tree-body").forEach(function(e){e.classList.add("hidden")}),r.querySelectorAll(".json-toggle").forEach(function(e){e.textContent="▶"}),r.querySelectorAll(".json-collapsed-trailer").forEach(function(e){e.classList.remove("hidden")})}),D.querySelector("#btn-prettify-json").addEventListener("click",function(){var e=b.value||W;try{var t=JSON.parse(e);n=t,W=JSON.stringify(t,null,2),b.value=W,r.innerHTML="",r.appendChild(v(t,void 0,"","",function(e){V.textContent=e,V.classList.remove("hidden")})),m.className="font-medium text-success flex items-center gap-1.5",m.innerHTML=x+" Beautified valid JSON",g(),_&&(_.disabled=!1),window.UI&&window.UI.showToast&&window.UI.showToast("JSON beautified.","success")}catch(e){m.className="font-medium text-error flex items-center gap-1.5",m.textContent="Syntax Error: "+e.message}}),D.querySelector("#btn-minify-json").addEventListener("click",function(){var e=b.value||W;try{var t=JSON.parse(e);n=t,W=JSON.stringify(t),b.value=W,r.innerHTML="",r.appendChild(v(t,void 0,"","",function(e){V.textContent=e,V.classList.remove("hidden")})),m.className="font-medium text-success flex items-center gap-1.5",m.innerHTML=x+" Minified valid JSON",g(),_&&(_.disabled=!1),window.UI&&window.UI.showToast&&window.UI.showToast("JSON minified.","success")}catch(e){m.className="font-medium text-error flex items-center gap-1.5",m.textContent="Syntax Error: "+e.message}}),D.querySelector("#btn-copy-json").addEventListener("click",function(){var e=b.value||W;navigator.clipboard.writeText(e).then(function(){window.UI&&window.UI.showToast&&window.UI.showToast("JSON copied to clipboard.","success")})}),b.addEventListener("input",function(){W=b.value,g();try{n=JSON.parse(W),m.className="font-medium text-success flex items-center gap-1.5",m.innerHTML=x+" Valid JSON",r.innerHTML="",r.appendChild(v(n,void 0,"","",function(e){V.textContent=e,V.classList.remove("hidden")})),_&&(_.disabled=!1)}catch(e){m.className="font-medium text-error flex items-center gap-1.5",m.textContent="Syntax Error: "+e.message,_&&(_.disabled=!0)}})}else{z.innerHTML='<div class="alert alert-warning text-xs p-3 rounded-xl"> <span>Content is not valid JSON. You can edit it in Raw Code tab.</span></div><textarea id="json-raw-input" class="field-input font-mono text-[13px] h-80 w-full resize-none p-3 leading-relaxed" spellcheck="false">'+e(W)+"</textarea>";var w=z.querySelector("#json-raw-input");w.addEventListener("input",function(){W=w.value;try{JSON.parse(W),_&&(_.disabled=!1)}catch(e){_&&(_.disabled=!0)}})}function g(){f&&(f.textContent=b.value.length+" chars · "+b.value.split("\n").length+" lines")}function y(e){"tree"===e?(d.className="btn btn-xs btn-primary join-item px-2.5 font-medium",p.className="btn btn-xs btn-ghost join-item px-2.5 font-medium",r.classList.remove("hidden"),c.classList.add("hidden")):(p.className="btn btn-xs btn-primary join-item px-2.5 font-medium",d.className="btn btn-xs btn-ghost join-item px-2.5 font-medium",r.classList.add("hidden"),c.classList.remove("hidden"))}}():"blob"===n?Z():function(){z.innerHTML="",D.innerHTML="",F.innerHTML="",V.textContent="",V.classList.add("hidden"),$&&($.innerHTML=r);var e=!0;function n(){var e=W.split("\n").length,n=W.trim()?W.trim().split(/\s+/).length:0,s=W.length;R.textContent=s+" chars · "+n+" words · "+e+" lines ("+t(new Blob([W]).size)+")"}n(),D.innerHTML='<div class="flex items-center gap-2"> <label class="flex items-center gap-1.5 text-xs text-base-content/60 cursor-pointer"> <input type="checkbox" id="text-word-wrap-toggle" class="checkbox checkbox-xs checkbox-primary" checked /> <span>Word Wrap</span> </label> <button type="button" id="btn-copy-raw-text" class="btn btn-ghost btn-xs gap-1 font-mono">'+l+"Copy</button></div>";var s=document.createElement("textarea");s.className="field-input font-mono text-[13px] h-96 w-full resize-none p-3.5 leading-relaxed "+(e?"whitespace-pre-wrap":"whitespace-pre overflow-x-auto"),s.spellcheck=!1,s.value=W,M&&(s.readOnly=!0),s.addEventListener("input",function(){W=s.value,n(),_&&(_.disabled=!1)}),z.appendChild(s),D.querySelector("#text-word-wrap-toggle").addEventListener("change",function(t){e=t.target.checked,s.className="field-input font-mono text-[13px] h-96 w-full resize-none p-3.5 leading-relaxed "+(e?"whitespace-pre-wrap":"whitespace-pre overflow-x-auto")}),D.querySelector("#btn-copy-raw-text").addEventListener("click",function(){navigator.clipboard.writeText(W).then(function(){window.UI&&window.UI.showToast&&window.UI.showToast("Text copied to clipboard.","success")})})}()}function X(){B.classList.remove("opacity-100"),B.classList.add("opacity-0"),U?O.classList.add("translate-x-full"):(O.classList.remove("scale-100"),O.classList.add("scale-95")),setTimeout(function(){B.remove(),document.removeEventListener("keydown",Y)},180)}function Y(e){"Escape"===e.key?(e.preventDefault(),X()):(e.ctrlKey||e.metaKey)&&"s"===e.key&&(e.preventDefault(),_&&!_.disabled&&E&&(E(W),X()))}O.querySelector("#btn-toggle-drawer").addEventListener("click",function(){U=!U,I=!1,window.localStorage.setItem("admindb_inspector_mode",U?"drawer":"modal"),K()}),O.querySelector("#btn-toggle-fullscreen").addEventListener("click",function(){I=!I,K()}),O.addEventListener("click",function(e){var t=e.target.closest(".json-copy-path");if(t){var n=t.dataset.path;n&&navigator.clipboard.writeText(n).then(function(){window.UI&&window.UI.showToast&&window.UI.showToast("Copied path: "+n,"info")})}}),J.querySelectorAll("#inspector-tabs button").forEach(function(e){e.addEventListener("click",function(){G(e.dataset.tab)})}),G(q),O.querySelectorAll(".js-inspector-close").forEach(function(e){e.addEventListener("click",X)}),B.addEventListener("click",function(e){e.target===B&&X()}),document.addEventListener("keydown",Y),_&&_.addEventListener("click",function(){E&&E(W),X()})}var k=null;function L(){clearTimeout(null),k&&(k.remove(),k=null)}document.addEventListener("click",function(e){k&&!k.contains(e.target)&&L()}),window.Inspector={open:y,peek:function(t,s){L();var o=(s=s||{}).value,a=s.col||s.column||"Value",r=!!s.isBlob,i=!!s.isImage,c=!!s.isAudio,d=s.blobUrl||"",u=!!s.isJson||n(String(o)),b=null==o?"":String(o),m=document.createElement("div");m.className="fixed z-[110] w-80 max-w-sm rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md shadow-2xl p-3.5 space-y-2.5 transform transition-all duration-150 animate-in fade-in zoom-in-95";var x=document.createElement("div");x.className="flex items-center justify-between gap-2 border-b border-base-200/80 pb-2 select-none",x.innerHTML='<div class="flex items-center gap-1.5 min-w-0"> <span class="text-xs font-bold font-mono text-primary truncate">'+e(a)+"</span>"+(r?'<span class="chip chip-type text-[9px]">BLOB</span>':u?'<span class="chip chip-type text-[9px]">JSON</span>':"")+'</div><div class="flex items-center gap-1"> <button type="button" class="btn btn-ghost btn-xs btn-circle js-peek-copy text-base-content/50 hover:text-base-content" title="Copy value">'+l+'</button> <button type="button" class="btn btn-ghost btn-xs btn-circle js-peek-open text-base-content/50 hover:text-base-content" title="Open in Inspector">↗</button> <button type="button" class="btn btn-ghost btn-xs btn-circle js-peek-close text-base-content/40 hover:text-base-content">'+p+"</button></div>",m.appendChild(x);var f=document.createElement("div");if(f.className="text-xs max-h-48 overflow-y-auto font-mono select-text",i&&d)f.innerHTML='<div class="flex items-center justify-center bg-base-200/40 rounded-xl p-2"><img src="'+d+'" class="max-h-36 max-w-full rounded object-contain shadow-xs" alt="" /></div>';else if(c&&d)f.innerHTML='<div class="flex flex-col items-center justify-center p-2 bg-base-200/40 rounded-xl"><audio controls class="w-full max-w-xs" src="'+d+'"></audio></div>';else if(u)try{var v=JSON.parse(b);f.innerHTML='<pre class="text-[11px] leading-relaxed text-base-content/85 whitespace-pre-wrap break-all bg-base-200/40 p-2.5 rounded-xl border border-base-300/40">'+e(JSON.stringify(v,null,2))+"</pre>"}catch(t){f.innerHTML='<pre class="text-[11px] leading-relaxed text-base-content/80 whitespace-pre-wrap break-all">'+e(b)+"</pre>"}else f.innerHTML='<pre class="text-[11px] leading-relaxed text-base-content/80 whitespace-pre-wrap break-all bg-base-200/30 p-2.5 rounded-xl border border-base-300/40">'+e(b)+"</pre>";m.appendChild(f),document.body.appendChild(m),k=m;var h=t.getBoundingClientRect(),w=h.bottom+6,g=h.left;g+320>window.innerWidth&&(g=window.innerWidth-330),g<10&&(g=10),w+200>window.innerHeight&&(w=h.top-210),w<10&&(w=10),m.style.top=w+"px",m.style.left=g+"px",m.querySelector(".js-peek-copy").addEventListener("click",function(e){e.stopPropagation(),navigator.clipboard.writeText(b).then(function(){window.UI&&window.UI.showToast&&window.UI.showToast("Copied to clipboard.","info")})}),m.querySelector(".js-peek-open").addEventListener("click",function(e){e.stopPropagation(),L(),y(s)}),m.querySelector(".js-peek-close").addEventListener("click",function(e){e.stopPropagation(),L()}),m.addEventListener("mouseleave",function(){L()})},closePeek:L}}();
|
package/dist/public/js/query.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const t=document.getElementById("query-sql"),e=document.getElementById("query-gutter"),n=document.getElementById("editor-cursor-pos"),s=document.getElementById("editor-stats"),r=document.getElementById("run-query"),i=document.getElementById("format-query"),o=document.getElementById("copy-query"),a=document.getElementById("clear-query"),l=document.getElementById("query-message"),c=document.getElementById("query-result"),d=document.getElementById("query-result-head"),u=document.getElementById("query-result-body"),p=document.getElementById("result-summary"),m=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),f=document.getElementById("save-query-name"),g=document.getElementById("saved-query-select"),h=document.getElementById("delete-query"),x=document.getElementById("export-csv"),w=document.getElementById("export-json");let y=[],b=null,v=!1;const E=window.Utils?window.Utils.escapeHtml:function(t){return String(null==t?"":t)},L=window.Utils?window.Utils.highlightSql:function(t){return E(t)};const T=new class{constructor(t=100){this.stack=[],this.index=-1,this.limit=t,this.lastRecordTime=0}record(t,e,n=250){const s=Date.now(),r=this.index>=0?this.stack[this.index]:null;if(r&&r.text===t)r.selection=e;else{if(r&&s-this.lastRecordTime<n&&1===Math.abs(t.length-r.text.length)&&!/\s|[;(),]/.test(t.slice(-1)))return r.text=t,r.selection=e,void(this.lastRecordTime=s);this.stack=this.stack.slice(0,this.index+1),this.stack.push({text:t,selection:e}),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 I(){if(!t)return"";return(t.innerText||t.textContent||"").replace(/\r\n/g,"\n").replace(/\r/g,"\n")}function k(t){const e=window.getSelection();if(!e||!e.rangeCount)return{start:0,end:0};const n=e.getRangeAt(0),s=n.cloneRange();s.selectNodeContents(t),s.setEnd(n.startContainer,n.startOffset);const r=s.toString().length;return{start:r,end:r+n.toString().length}}function q(t,e){const n=window.getSelection();if(!n)return;let s=0;const r=document.createRange();r.setStart(t,0),r.collapse(!0);const i=[t];let o,a=!1,l=!1;for(;!l&&(o=i.pop());)if(3===o.nodeType){const t=s+o.length;!a&&e.start>=s&&e.start<=t&&(r.setStart(o,e.start-s),a=!0),a&&e.end>=s&&e.end<=t&&(r.setEnd(o,e.end-s),l=!0),s=t}else{let t=o.childNodes.length;for(;t--;)i.push(o.childNodes[t])}n.removeAllRanges(),n.addRange(r)}function S(e,n=!0){if(t&&(v=!0,t.innerHTML=e?L(e):"",v=!1,M(),O(),n)){const t=(e||"").length;T.record(e||"",{start:t,end:t},0)}}function U(e){if(!t)return;const n=I(),s=k(t),r=n.slice(0,s.start)+e+n.slice(s.end),i=s.start+e.length;v=!0,t.innerHTML=L(r),q(t,{start:i,end:i}),v=!1,M(),O(),T.record(r,{start:i,end:i},0)}function M(){if(!t||!e)return;const n=I().split("\n"),s=Math.max(1,n.length);let r="";for(let t=1;t<=s;t++){const e=null!=b&&t===b;r+='<div class="sql-gutter-line'+(e?" has-error":"")+'" data-line="'+t+'" title="'+(e?"Error on line "+t:"Line "+t)+'">'+t+"</div>"}e.innerHTML=r,e.scrollTop=t.scrollTop}function O(){if(!t||!n)return;const e=I(),r=k(t),i=e.slice(0,r.start).split("\n"),o=i.length,a=i[i.length-1].length+1;if(n.textContent="Ln "+o+", Col "+a,s){const t=e.length,n=e.split("\n").length,i=Math.abs(r.end-r.start);s.textContent=i>0?t+" chars ("+i+" selected) · "+n+" lines":t+" chars · "+n+" lines"}}function j(e,n){if(!t)return;const s=I().split("\n"),r=Math.max(1,Math.min(s.length,e||1));let i=0;for(let t=0;t<r-1;t++)i+=s[t].length+1;n&&n>1&&(i+=Math.min(s[r-1].length,n-1)),t.focus(),q(t,{start:i,end:i});t.scrollTop=Math.max(0,21*(r-3)),M(),O()}function B(t,e){if(!l)return;if(!t)return void(l.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"};l.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border px-4 py-3 text-sm font-medium '+(n[e]||n.info)+'"><span>'+E(t)+"</span></div>"}function R(t){x&&(x.disabled=!t),w&&(w.disabled=!t)}async function N(t){const e=I().trim();if(e)try{const n=await fetch(Api.url("/api/query/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:e,format:t})});if(!n.ok){let t="Export failed ("+n.status+").";try{const e=await n.json();e&&e.error&&(t=e.error)}catch(t){}throw new Error(t)}const s=await n.blob(),r=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(s,"query-"+r+"."+t),window.UI&&UI.showToast&&UI.showToast("Exported as "+t.toUpperCase()+".","success")}catch(t){B(t.message,"error")}}i&&i.addEventListener("click",function(){const t=I();t.trim()&&(S(function(t){if(!t)return"";let e=t.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(t){const n=new RegExp("\\b"+t.replace(" ","\\s+")+"\\b","gi");e=e.replace(n,"\n"+t.toUpperCase())}),e.split("\n").map(function(t){return t.trim()}).filter(function(t){return t.length>0}).map(function(t){const e=t.toUpperCase();return e.startsWith("WHERE")||e.startsWith("AND")||e.startsWith("OR")||e.startsWith("JOIN")||e.startsWith("LEFT")||e.startsWith("RIGHT")||e.startsWith("INNER")||e.startsWith("ON")||e.startsWith("SET")||e.startsWith("VALUES")||e.startsWith("HAVING")||e.startsWith("ORDER")||e.startsWith("GROUP")||e.startsWith("LIMIT")||e.startsWith("OFFSET")?" "+t:t}).join("\n")}(t)),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),o&&o.addEventListener("click",function(){const t=I();t.trim()&&navigator.clipboard.writeText(t).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),a&&a.addEventListener("click",function(){S(""),b=null,B("","info"),t.focus()}),document.querySelectorAll(".js-snippet").forEach(function(e){e.addEventListener("click",function(n){n.preventDefault();const s=e.getAttribute("data-snippet");s&&(S(s.replace(/\\n/g,"\n")),b=null,B("","info"),t.focus())})}),e&&e.addEventListener("click",function(t){const e=t.target.closest(".sql-gutter-line");if(!e)return;const n=parseInt(e.dataset.line,10);n&&j(n,1)}),t&&(t.addEventListener("input",function(){b=null;const e=I(),n=k(t);T.record(e,n,0),function(){if(!t||v)return;v=!0;const e=k(t),n=I();n?(t.innerHTML=L(n),q(t,e)):t.innerHTML="",v=!1,M(),O()}()}),t.addEventListener("paste",function(t){t.preventDefault();const e=(t.clipboardData||window.clipboardData).getData("text/plain");e&&U(e)}),t.addEventListener("scroll",function(){e&&(e.scrollTop=t.scrollTop)}),t.addEventListener("click",function(){O()}),t.addEventListener("keyup",function(){O()}),t.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()&&!e.shiftKey)return e.preventDefault(),void function(){const e=T.undo();e&&(v=!0,t.innerHTML=L(e.text),q(t,e.selection||{start:0,end:0}),v=!1,b=null,M(),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=T.redo();e&&(v=!0,t.innerHTML=L(e.text),q(t,e.selection||{start:0,end:0}),v=!1,b=null,M(),O())}();if((e.ctrlKey||e.metaKey)&&"Enter"===e.key)return e.preventDefault(),void W();if("Enter"===e.key){e.preventDefault();const n=I(),s=k(t),r=(n.slice(0,s.start).split("\n").pop()||"").match(/^(\s+)/);return void U("\n"+(r?r[1]:""))}if((e.ctrlKey||e.metaKey)&&"/"===e.key){e.preventDefault();const n=k(t),s=I(),r=s.lastIndexOf("\n",n.start-1)+1;let i=s.indexOf("\n",n.end);-1===i&&(i=s.length);const o=s.slice(r,i).split("\n"),a=o.every(function(t){return t.trim().startsWith("--")}),l=o.map(function(t){return a?t.replace(/^(\s*)--\s?/,"$1"):t?"-- "+t:t}).join("\n");return S(s.slice(0,r)+l+s.slice(i)),void q(t,{start:r,end:r+l.length})}if("Tab"===e.key){e.preventDefault();const n=k(t),s=I();if(n.start===n.end)e.shiftKey||U(" ");else{const r=s.lastIndexOf("\n",n.start-1)+1;let i=s.indexOf("\n",n.end);-1===i&&(i=s.length);const o=s.slice(r,i).split("\n").map(function(t){return e.shiftKey?t.replace(/^( |\t| )/,""):" "+t}).join("\n");S(s.slice(0,r)+o+s.slice(i)),q(t,{start:r,end:r+o.length})}return}})),x&&x.addEventListener("click",()=>N("csv")),w&&w.addEventListener("click",()=>N("json"));const C=window.Utils?window.Utils.isJson:function(t){return!1},A=window.Utils?window.Utils.isHexBlob:function(t){return!1};function H(t,e){d.innerHTML="",u.innerHTML='<tr><td class="px-4 py-8 text-center"><p class="text-2xl font-extrabold text-primary">'+E(t)+'</p><p class="mt-1 text-sm text-base-content/45">'+E(e)+"</p></td></tr>"}function D(t){p&&(p.textContent=t)}async function W(){const t=I().trim();if(!t)return void B("Enter a SQL statement first.","warning");if(function(t){var e=t.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim();return!!/\b(drop\s+(table|view|index|trigger)|truncate\b)/i.test(e)||!(!/\bdelete\s+from\b/i.test(e)||/\bwhere\b/i.test(e))}(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">'+E(t.length>350?t.slice(0,350)+"…":t)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}b=null,M(),l.innerHTML="",c.classList.add("hidden"),R(!1);const e=performance.now();try{const r=await Api.post("/api/query",{sql:t}),i=Math.max(1,Math.round(performance.now()-e));c.classList.remove("hidden"),"count"===r.kind?(H(String(r.count),"count"),D("Count query · "+i+"ms")):"select"===r.kind?(n=r.columns,s=r.rows,d.innerHTML="<tr>"+n.map(t=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+E(t)+"</span></th>").join("")+"</tr>",s.length?u.innerHTML=s.map(t=>'<tr class="hover:bg-primary/[0.04]">'+n.map(e=>{const n=t[e];if(null==n)return'<td class="align-middle"><span class="chip chip-null">NULL</span></td>';const s=String(n),r=C(s),i=A(s),o=s.length>140?s.slice(0,140)+"…":s;return r?'<td class="max-w-xs align-middle js-query-cell" data-col="'+E(e)+'" data-value="'+E(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">'+E(o)+'</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>':i?'<td class="max-w-xs align-middle js-query-cell" data-col="'+E(e)+'" data-value="'+E(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">'+E(o)+'</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="'+E(e)+'" data-value="'+E(s)+'"><div class="flex items-center justify-between gap-1 group/cell"><span class="truncate font-mono text-[13px]" title="'+E(s)+'">'+E(o)+"</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(""):u.innerHTML='<tr><td colspan="'+Math.max(1,n.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',D(r.rows.length+" row(s) · "+i+"ms"),R(!0)):(H(r.message||"Statement executed.","write"),D(null!=r.changes?r.changes+" row(s) affected · "+i+"ms":i+"ms"))}catch(t){c.classList.add("hidden"),function(t){if(!l)return;const e=t&&t.details,n=t&&t.message||e&&e.message||"Query failed.";if(!e||!e.snippet&&!e.cause&&!e.suggestion)return void B(n,"error");b=e.line||null,M();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">'+E(n)+"</span>",s+="</div>",e.line&&(s+='<div class="flex items-center gap-1.5">',s+='<span class="badge badge-sm badge-error font-mono font-bold">Line '+e.line+(e.column?", Col "+e.column:"")+"</span>",s+='<button type="button" class="btn btn-xs btn-outline btn-error js-jump-err-line" data-line="'+e.line+'" data-col="'+(e.column||1)+'">Jump to line</button>',s+="</div>"),s+="</div>",e.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">'+E(e.snippet)+"</pre>",s+="</div>"),(e.cause||e.suggestion)&&(s+='<div class="grid grid-cols-1 md:grid-cols-2 gap-2.5 pt-1">',e.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">'+E(e.cause)+"</p>",s+="</div>"),e.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">'+E(e.suggestion)+"</p>",s+="</div>"),s+="</div>"),s+="</div>",l.innerHTML=s;const r=l.querySelector(".js-jump-err-line");r&&r.addEventListener("click",function(){j(parseInt(r.dataset.line,10),parseInt(r.dataset.col,10))})}(t)}var n,s}async function K(t){try{y=await Api.get("/api/queries")}catch(t){y=[]}g.innerHTML='<option value="">— select a saved query —</option>'+y.map(e=>'<option value="'+e.id+'"'+(t&&e.name===t?" selected":"")+">"+E(e.name)+"</option>").join(""),h.disabled=!0}async function J(){const t=I().trim(),e=f.value.trim();if(t)if(e)try{await Api.post("/api/queries",{name:e,sql:t}),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),await K(e)}catch(t){B(t.message,"error")}else B("Give the query a name.","warning");else B("Write some SQL before saving.","warning")}u&&u.addEventListener("click",function(t){const e=t.target.closest(".js-query-inspect"),n=t.target.closest(".js-query-cell");if(!e&&(!n||t.detail<2))return;const s=e?e.closest(".js-query-cell"):n;if(!s)return;const r=s.dataset.col||"result",i=s.dataset.value||"",o="true"===s.dataset.blob,a="true"===s.dataset.json;window.Inspector&&window.Inspector.open&&window.Inspector.open({col:r,value:i,isNull:!1,isBlob:o,isJson:a,readonly:!0})}),r&&r.addEventListener("click",W),m.forEach(t=>t.addEventListener("click",J)),g&&g.addEventListener("change",()=>{const t=g.value;if(h.disabled=!t,!t)return;const e=y.find(e=>String(e.id)===String(t));e&&(S(e.sql),f.value=e.name,b=null,B('Loaded "'+e.name+'".',"info"))}),h&&h.addEventListener("click",async function(){const t=g.value;if(!t)return;const e=y.find(e=>String(e.id)===String(t)),n=e?e.name:"Saved query";if(await UI.confirm({title:"Delete Saved Query",message:'Are you sure you want to delete the saved query "'+n+'"?',item:n,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{await Api.del("/api/queries/"+t),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),await K()}catch(t){B(t.message,"error")}});const Q=I();Q?S(Q,!0):T.record("",{start:0,end:0},0),M(),O()}();
|
|
1
|
+
!function(){"use strict";const e=document.getElementById("query-sql"),t=document.getElementById("query-code"),n=document.getElementById("query-highlight"),s=document.getElementById("query-gutter"),r=document.getElementById("editor-cursor-pos"),o=document.getElementById("editor-stats"),i=document.getElementById("run-query"),a=document.getElementById("format-query"),l=document.getElementById("copy-query"),c=document.getElementById("clear-query"),d=document.getElementById("query-message"),u=document.getElementById("query-result"),p=document.getElementById("query-result-head"),m=document.getElementById("query-result-body"),f=document.getElementById("result-summary"),g=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),h=document.getElementById("save-query-name"),y=document.getElementById("saved-query-select"),x=document.getElementById("delete-query"),w=document.getElementById("export-csv"),v=document.getElementById("export-json");let b=[],E=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 I=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 k(){return e?e.value:""}function q(){if(!e||!t)return;const n=e.value;t.innerHTML=T(n)+(n.endsWith("\n")?" ":""),R(),j(),S()}function S(){e&&(n&&(n.scrollTop=e.scrollTop,n.scrollLeft=e.scrollLeft),s&&(s.scrollTop=e.scrollTop))}function U(t,n=!0){if(e&&(e.value=t||"",q(),n)){const e=(t||"").length;I.record(t||"",{start:e,end:e},0)}}function R(){if(!e||!s)return;const t=e.value.split("\n"),n=Math.max(1,t.length);let r="";for(let e=1;e<=n;e++){const t=null!=E&&e===E;r+='<div class="sql-gutter-line'+(t?" has-error":"")+'" data-line="'+e+'" title="'+(t?"Error on line "+e:"Line "+e)+'">'+e+"</div>"}s.innerHTML=r,s.scrollTop=e.scrollTop}function j(){if(!e||!r)return;const t=e.value,n=e.selectionStart||0,s=e.selectionEnd||0,i=t.slice(0,n).split("\n"),a=i.length,l=i[i.length-1].length+1;if(r.textContent="Ln "+a+", Col "+l,o){const e=t.length,r=t.split("\n").length,i=Math.abs(s-n);o.textContent=i>0?e+" chars ("+i+" selected) · "+r+" lines":e+" chars · "+r+" lines"}}function B(t,n){if(!e)return;const s=e.value.split("\n"),r=Math.max(1,Math.min(s.length,t||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)),e.focus(),e.setSelectionRange(o,o);e.scrollTop=Math.max(0,21*(r-3)),S(),j()}function M(e,t){if(!d)return;if(!e)return void(d.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"};d.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 O(e){w&&(w.disabled=!e),v&&(v.disabled=!e)}async function N(e){const t=k().trim();if(t)try{const n=await fetch(Api.url("/api/query/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:t,format:e})});if(!n.ok){let e="Export failed ("+n.status+").";try{const t=await n.json();t&&t.error&&(e=t.error)}catch(e){}throw new Error(e)}const s=await n.blob(),r=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(s,"query-"+r+"."+e),window.UI&&UI.showToast&&UI.showToast("Exported as "+e.toUpperCase()+".","success")}catch(e){M(e.message,"error")}}a&&a.addEventListener("click",function(){const t=k();t.trim()&&(U(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")}(t)),e.focus(),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),l&&l.addEventListener("click",function(){const e=k();e.trim()&&navigator.clipboard.writeText(e).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),c&&c.addEventListener("click",function(){U(""),E=null,M("","info"),e.focus()}),document.querySelectorAll(".js-snippet").forEach(function(t){t.addEventListener("click",function(n){n.preventDefault();const s=t.getAttribute("data-snippet");s&&(U(s.replace(/\\n/g,"\n")),E=null,M("","info"),e.focus())})}),s&&s.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)}),e&&(e.addEventListener("input",function(){E=null,q(),I.record(e.value,{start:e.selectionStart,end:e.selectionEnd},250)}),e.addEventListener("scroll",S),e.addEventListener("click",j),e.addEventListener("keyup",j),e.addEventListener("select",j),e.addEventListener("keydown",function(t){if((t.ctrlKey||t.metaKey)&&"z"===t.key.toLowerCase()&&!t.shiftKey)return t.preventDefault(),void function(){const t=I.undo();t&&(e.value=t.text,q(),t.selection&&e.setSelectionRange(t.selection.start,t.selection.end),e.focus(),E=null,R(),j())}();if((t.ctrlKey||t.metaKey)&&"y"===t.key.toLowerCase()||(t.ctrlKey||t.metaKey)&&t.shiftKey&&"z"===t.key.toLowerCase())return t.preventDefault(),void function(){const t=I.redo();t&&(e.value=t.text,q(),t.selection&&e.setSelectionRange(t.selection.start,t.selection.end),e.focus(),E=null,R(),j())}();if((t.ctrlKey||t.metaKey)&&"Enter"===t.key)return t.preventDefault(),void W();if("Enter"===t.key){t.preventDefault();const n=e.selectionStart,s=e.selectionEnd,r=(e.value.slice(0,n).split("\n").pop()||"").match(/^(\s+)/),o="\n"+(r?r[1]:"");return e.setRangeText(o,n,s,"end"),q(),void I.record(e.value,{start:e.selectionStart,end:e.selectionEnd},0)}if((t.ctrlKey||t.metaKey)&&"/"===t.key){t.preventDefault();const n=e.selectionStart,s=e.selectionEnd,r=e.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 e.setRangeText(c,o,i,"preserve"),e.setSelectionRange(o,o+c.length),q(),void I.record(e.value,{start:o,end:o+c.length},0)}if("Tab"===t.key){t.preventDefault();const n=e.selectionStart,s=e.selectionEnd,r=e.value;if(n!==s||t.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(e){return t.shiftKey?e.replace(/^( |\t| )/,""):" "+e}).join("\n");e.setRangeText(a,o,i,"preserve"),e.setSelectionRange(o,o+a.length),q(),I.record(e.value,{start:o,end:o+a.length},0)}else e.setRangeText(" ",n,s,"end"),q(),I.record(e.value,{start:e.selectionStart,end:e.selectionEnd},0);return}})),w&&w.addEventListener("click",()=>N("csv")),v&&v.addEventListener("click",()=>N("json"));const C=window.Utils?window.Utils.isJson:function(e){return!1},A=window.Utils?window.Utils.isHexBlob:function(e){return!1};if(m){let e=null;m.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})}),m.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))}),m.addEventListener("mouseout",function(t){clearTimeout(e)})}function H(e,t){p.innerHTML="",m.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 D(e){f&&(f.textContent=e)}async function W(){const e=k().trim();if(!e)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))}(e)){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(e.length>350?e.slice(0,350)+"…":e)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}E=null,R(),d.innerHTML="",u.classList.add("hidden"),O(!1);const t=i?i.innerHTML:"";i&&(i.disabled=!0,i.innerHTML='<span class="loading loading-spinner loading-xs"></span> Running…');const n=performance.now();try{const t=await Api.post("/api/query",{sql:e}),o=Math.max(1,Math.round(performance.now()-n));u.classList.remove("hidden"),u.classList.remove("js-result-slide"),u.offsetWidth,u.classList.add("js-result-slide"),"count"===t.kind?(H(String(t.count),"count"),D("Count query · "+o+"ms")):"select"===t.kind?(s=t.columns,r=t.rows,p.innerHTML="<tr>"+s.map(e=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+L(e)+"</span></th>").join("")+"</tr>",r.length?m.innerHTML=r.map(e=>'<tr class="hover:bg-primary/[0.04]">'+s.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=C(s),o=A(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(""):m.innerHTML='<tr><td colspan="'+Math.max(1,s.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',D(t.rows.length+" row(s) · "+o+"ms"),O(!0)):(H(t.message||"Statement executed.","write"),D(null!=t.changes?t.changes+" row(s) affected · "+o+"ms":o+"ms")),setTimeout(function(){u.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}catch(e){u.classList.add("hidden"),function(e){if(!d)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");E=t.line||null,R();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>",d.innerHTML=s;const r=d.querySelector(".js-jump-err-line");r&&r.addEventListener("click",function(){B(parseInt(r.dataset.line,10),parseInt(r.dataset.col,10))})}(e),setTimeout(function(){d.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}finally{i&&(i.disabled=!1,i.innerHTML=t)}var s,r}async function K(e){try{b=await Api.get("/api/queries")}catch(e){b=[]}y.innerHTML='<option value="">— select a saved query —</option>'+b.map(t=>'<option value="'+t.id+'"'+(e&&t.name===e?" selected":"")+">"+L(t.name)+"</option>").join(""),x.disabled=!0}async function J(){const e=k().trim(),t=h.value.trim();if(e)if(t)try{await Api.post("/api/queries",{name:t,sql:e}),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),await K(t)}catch(e){M(e.message,"error")}else M("Give the query a name.","warning");else M("Write some SQL before saving.","warning")}i&&i.addEventListener("click",W),g.forEach(e=>e.addEventListener("click",J)),y&&y.addEventListener("change",()=>{const t=y.value;if(x.disabled=!t,!t)return;const n=b.find(e=>String(e.id)===String(t));n&&(U(n.sql),h.value=n.name,E=null,M('Loaded "'+n.name+'".',"info"),e.focus())}),x&&x.addEventListener("click",async function(){const e=y.value;if(!e)return;const t=b.find(t=>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+'"?',item:n,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{await Api.del("/api/queries/"+e),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),await K()}catch(e){M(e.message,"error")}});const Q=k();Q?U(Q,!0):I.record("",{start:0,end:0},0),q()}();
|