admindb 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/dist/app.js +48 -25
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.js +2 -0
- package/dist/core/diff/data-differ.d.ts +1 -0
- package/dist/core/diff/data-differ.js +243 -0
- package/dist/core/diff/patch-generator.d.ts +1 -0
- package/dist/core/diff/patch-generator.js +77 -0
- package/dist/core/diff/schema-differ.d.ts +1 -0
- package/dist/core/diff/schema-differ.js +229 -0
- package/dist/core/errors.d.ts +1 -0
- package/dist/core/errors.js +39 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/result.d.ts +1 -0
- package/dist/core/result.js +10 -0
- package/dist/core/router.d.ts +1 -0
- package/dist/core/router.js +37 -0
- package/dist/core/transfer/dialect-mapper.d.ts +1 -0
- package/dist/core/transfer/dialect-mapper.js +303 -0
- package/dist/core/transfer/masking.d.ts +1 -0
- package/dist/core/transfer/masking.js +49 -0
- package/dist/core/transfer/sync-engine.d.ts +1 -0
- package/dist/core/transfer/sync-engine.js +234 -0
- package/dist/core/transfer/topological-sort.d.ts +1 -0
- package/dist/core/transfer/topological-sort.js +55 -0
- package/dist/data/chain.js +1 -1
- package/dist/data/engine.js +1 -1
- package/dist/db/database.d.ts +1 -1
- package/dist/db/database.js +9 -31
- package/dist/db/dialects/index.d.ts +1 -0
- package/dist/db/dialects/index.js +58 -0
- package/dist/db/dialects/postgres/ddl.d.ts +1 -0
- package/dist/db/dialects/postgres/ddl.js +107 -0
- package/dist/db/dialects/postgres/dialect.d.ts +1 -0
- package/dist/db/dialects/postgres/dialect.js +28 -0
- package/dist/db/dialects/postgres/introspector.d.ts +1 -0
- package/dist/db/dialects/postgres/introspector.js +265 -0
- package/dist/db/dialects/postgres/types.d.ts +1 -0
- package/dist/db/dialects/postgres/types.js +83 -0
- package/dist/db/dialects/sqlite/ddl.d.ts +1 -0
- package/dist/db/dialects/sqlite/ddl.js +244 -0
- package/dist/db/dialects/sqlite/dialect.d.ts +1 -0
- package/dist/db/dialects/sqlite/dialect.js +25 -0
- package/dist/db/dialects/sqlite/introspector.d.ts +1 -0
- package/dist/db/dialects/sqlite/introspector.js +217 -0
- package/dist/db/dialects/sqlite/types.d.ts +1 -0
- package/dist/db/dialects/sqlite/types.js +69 -0
- package/dist/db/dialects/types.d.ts +1 -0
- package/dist/db/dialects/types.js +15 -0
- package/dist/db/export.js +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +101 -21
- package/dist/db/postgres.d.ts +1 -1
- package/dist/db/postgres.js +20 -284
- package/dist/db/types.d.ts +1 -1
- package/dist/db/types.js +0 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/dist/modules/databases/databases.routes.d.ts +1 -0
- package/dist/modules/databases/databases.routes.js +150 -0
- package/dist/modules/databases/databases.service.d.ts +1 -0
- package/dist/modules/databases/databases.service.js +134 -0
- package/dist/modules/databases/index.d.ts +1 -0
- package/dist/{routes → modules/databases}/index.js +2 -3
- package/dist/modules/erd/erd.routes.d.ts +1 -0
- package/dist/modules/erd/erd.routes.js +32 -0
- package/dist/modules/erd/erd.service.d.ts +1 -0
- package/dist/modules/erd/erd.service.js +39 -0
- package/dist/modules/erd/index.d.ts +1 -0
- package/dist/modules/erd/index.js +18 -0
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/index.js +70 -0
- package/dist/modules/query/index.d.ts +1 -0
- package/dist/modules/query/index.js +18 -0
- package/dist/modules/query/query.routes.d.ts +1 -0
- package/dist/modules/query/query.routes.js +54 -0
- package/dist/modules/query/query.service.d.ts +1 -0
- package/dist/modules/query/query.service.js +95 -0
- package/dist/modules/rows/blob.helper.d.ts +1 -0
- package/dist/modules/rows/blob.helper.js +40 -0
- package/dist/modules/rows/display.helper.d.ts +1 -0
- package/dist/modules/rows/display.helper.js +73 -0
- package/dist/modules/rows/index.d.ts +1 -0
- package/dist/modules/rows/index.js +20 -0
- package/dist/modules/rows/rows.routes.d.ts +1 -0
- package/dist/modules/rows/rows.routes.js +555 -0
- package/dist/modules/rows/rows.service.d.ts +1 -0
- package/dist/modules/rows/rows.service.js +433 -0
- package/dist/modules/schema/index.d.ts +1 -0
- package/dist/modules/schema/index.js +18 -0
- package/dist/modules/schema/schema.routes.d.ts +1 -0
- package/dist/modules/schema/schema.routes.js +171 -0
- package/dist/modules/schema/schema.service.d.ts +1 -0
- package/dist/modules/schema/schema.service.js +58 -0
- package/dist/modules/search/index.d.ts +1 -0
- package/dist/modules/search/index.js +19 -0
- package/dist/modules/search/search.routes.d.ts +1 -0
- package/dist/modules/search/search.routes.js +38 -0
- package/dist/modules/search/search.service.d.ts +1 -0
- package/dist/modules/search/search.service.js +458 -0
- package/dist/modules/search/search.types.d.ts +1 -0
- package/dist/modules/search/search.types.js +2 -0
- package/dist/modules/seed/index.d.ts +1 -0
- package/dist/modules/seed/index.js +18 -0
- package/dist/modules/seed/seed.routes.d.ts +1 -0
- package/dist/modules/seed/seed.routes.js +212 -0
- package/dist/modules/seed/seed.service.d.ts +1 -0
- package/dist/{routes/api/helpers.js → modules/seed/seed.service.js} +86 -109
- package/dist/modules/sync/index.d.ts +1 -0
- package/dist/modules/sync/index.js +18 -0
- package/dist/modules/sync/sync.routes.d.ts +1 -0
- package/dist/modules/sync/sync.routes.js +135 -0
- package/dist/modules/sync/sync.service.d.ts +1 -0
- package/dist/modules/sync/sync.service.js +41 -0
- package/dist/modules/tables/index.d.ts +1 -0
- package/dist/modules/tables/index.js +18 -0
- package/dist/modules/tables/tables.routes.d.ts +1 -0
- package/dist/modules/tables/tables.routes.js +194 -0
- package/dist/modules/tables/tables.service.d.ts +1 -0
- package/dist/modules/tables/tables.service.js +218 -0
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/search.js +1 -0
- package/dist/public/js/sync.js +1 -0
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +1 -3
- package/dist/utils/icons.js +3 -0
- package/dist/utils/stream.d.ts +1 -0
- package/dist/utils/stream.js +131 -0
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/error.hbs +10 -1
- package/dist/views/pages/home.hbs +1 -1
- package/dist/views/pages/query.hbs +1 -1
- package/dist/views/pages/seed-select.hbs +1 -252
- package/dist/views/pages/seed.hbs +1 -307
- package/dist/views/pages/sync.hbs +409 -0
- package/dist/views/pages/table.hbs +1 -1
- package/dist/views/partials/navbar.hbs +1 -1
- package/dist/views/partials/sidebar.hbs +1 -1
- package/docs/API.md +4 -17
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +10 -9
- package/dist/db/introspection.d.ts +0 -1
- package/dist/db/introspection.js +0 -164
- package/dist/routes/api/erd.d.ts +0 -1
- package/dist/routes/api/erd.js +0 -45
- package/dist/routes/api/helpers.d.ts +0 -1
- package/dist/routes/api/import-export.d.ts +0 -1
- package/dist/routes/api/import-export.js +0 -84
- package/dist/routes/api/index.d.ts +0 -1
- package/dist/routes/api/index.js +0 -55
- package/dist/routes/api/query.d.ts +0 -1
- package/dist/routes/api/query.js +0 -77
- package/dist/routes/api/rows.d.ts +0 -1
- package/dist/routes/api/rows.js +0 -373
- package/dist/routes/api/seed.d.ts +0 -1
- package/dist/routes/api/seed.js +0 -180
- package/dist/routes/api/tables.d.ts +0 -1
- package/dist/routes/api/tables.js +0 -230
- package/dist/routes/databases.d.ts +0 -1
- package/dist/routes/databases.js +0 -212
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/pages.d.ts +0 -1
- package/dist/routes/pages.js +0 -540
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(window.APP&&window.APP.dbId||s&&s.dbId||""),l=encodeURIComponent(s.table),a=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let i=null,r={};function c(e){return i?i.columns.find(t=>t.name===e):null}function d(e){return!!i&&-1!==i.primaryKey.indexOf(e)}function u(e){return i?i.foreignKeys.find(t=>t.from===e):null}function p(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function f(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function m(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function h(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=u(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,d="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||d||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",m(n,"","(any)"),m(n,"__NULL__","(not set / NULL)"),(r[e.name]||[]).forEach(e=>m(n,String(e.value),""===e.label?String(e.value):e.label));let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",m(n,"","(any)"),m(n,"1","true"),m(n,"0","false");let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||i){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":d?"time":"datetime-local",(d||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=p(y,"gte")??p(y,"gt")??a.min,r=p(y,"lte")??p(y,"lt")??a.max;return null!=i&&(s.value=g?f(i,c,d):String(i)),null!=r&&(o.value=g?f(r,c,d):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=p(y,"prefix"),t=p(y,"eq"),n=p(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function g(t){let n=s.table;if(!n){const e=window.location.pathname.match(/\/tables\/[^/]+\/([^/]+)(?:\/|$)/);e&&(n=decodeURIComponent(e[1]))}if(!n)return;const l=document.getElementById("page-size"),a=new URLSearchParams({page:"1",size:l?l.value:"50"});Object.keys(t).length&&a.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+o+"/"+encodeURIComponent(n)+"?"+a.toString()}function b(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&i&&(i.columns.forEach(e=>n.appendChild(h(e))),e.addEventListener("submit",e=>{e.preventDefault(),g(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>g({}))}let y=null;function v(){y&&(y.classList.remove("cell-focused"),y=null)}function x(e){e&&e!==y&&!e.classList.contains("js-editing")&&(v(),y=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function w(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;E(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,L.delete(n+"::"+s),S(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function E(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,l=e.dataset.origValue,i="true"===e.dataset.origNull,r=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":l)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=c(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+a(r)+'">'+a(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+a(r)+'">'+a(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function k(e){return"true"===e.dataset.null?null:e.dataset.value??""}const L=new Map;function I(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=c(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const r=(l.type||"").toUpperCase(),p="BOOLEAN"===r||"BOOL"===r,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),L.delete(s+"::"+o),E(t,i?null:a),void S();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),E(t,i?null:a),L.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:d(o),isFk:!!u(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),S()}function S(){if(!j||!C)return;const e=L.size;C.textContent=String(e),j.classList.toggle("hidden",0===e)}function T(){if(!L.size||t)return;const e=new Map;for(const t of L.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];B&&(B.disabled=!0),Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),B&&(B.disabled=!1)})}async function A(){const e=L.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of L.values())E(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;L.clear(),S(),UI.showToast("Pending edits discarded.","info")}}function N(){if(L.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";L.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+a(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+a(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+a(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+L.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">×</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+L.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=L.get(t);n&&(w(n.td),e.closest("tr").remove(),L.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),T()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await A()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let j=null,C=null,B=null;function U(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!c(e.dataset.col))return;x(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=k(e);const o=function(e){const t=e.dataset.col,n=c(t);if(!n)return null;const s=u(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,i="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),d="DATETIME"===o||o.includes("TIMESTAMP"),p=a||i||d,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,E="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=k(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",m(S,"","(not set / NULL)"),(r[t]||[]).forEach(e=>{const t=m(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(p)S=document.createElement("input"),S.type=a?"date":i?"time":"datetime-local",(i||d)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=f(String(L),a,i)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):E?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?I(n,e,o):function(e){E(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?R("down"):"right"===s?R("right"):"left"===s&&R("left"))};e._finish=a,o.addEventListener("keydown",e=>{"Enter"===e.key?(e.preventDefault(),a(!0,"down")):"Tab"===e.key?(e.preventDefault(),a(!0,e.shiftKey?"left":"right")):"Escape"===e.key&&(e.preventDefault(),a(!1))}),"SELECT"===o.tagName||"checkbox"===o.type?o.addEventListener("change",()=>a(!0)):o.addEventListener("blur",()=>a(!0)),o.focus(),"function"==typeof o.select&&o.select()}function R(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!y){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&x(n[0])}return}const n=y.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(y);if("right"===e)if(o>=0&&o<s.length-1)x(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&x(n[0])}}else if("left"===e)if(o>0)x(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&x(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}}function M(){j=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),C=document.getElementById("changes-count"),B=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");B&&B.addEventListener("click",T),e&&e.addEventListener("click",A),n&&n.addEventListener("click",N),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&w(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&x(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>U(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&y)if("ArrowRight"===e.key)e.preventDefault(),R("right");else if("ArrowLeft"===e.key)e.preventDefault(),R("left");else if("ArrowDown"===e.key)e.preventDefault(),R("down");else if("ArrowUp"===e.key)e.preventDefault(),R("up");else if("Tab"===e.key)e.preventDefault(),R(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||U(y);else if("Escape"===e.key)v();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),z(y);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=k(y);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}}else{const t=c(y.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=y.closest("tr"),n=k(y),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";I(t,y,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),z(y)}})}function q(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}async function O(e){const t=q();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(s),a.download=n.filename,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(a.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function D(){const e=q();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const i=n&&n.references||[],r=i.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+i.map(e=>'<li><code class="font-mono font-bold">'+a(e.table)+"."+a(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:r,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function W(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let P=null;function _(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),P&&(W(P,!1),P.setAttribute("aria-expanded","false"),P.classList.remove("opacity-50"),P=null)}function V(t,n){_();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let l='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+a(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+a(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+a(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+a(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?l+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+a(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+a(s)+'">'+a(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':l+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),l=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+l>window.innerHeight-8&&(c=Math.max(8,i.top-l-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",P=t;const d=e=>{e&&e.target&&s.contains(e.target)||_()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||_()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&_()};document.addEventListener("keydown",p),s._cleanup=()=>{window.removeEventListener("scroll",d,!0),window.removeEventListener("resize",d),document.removeEventListener("click",u,!0),document.removeEventListener("keydown",p)}}function z(e){if(!e)return;const n=e.closest("tr");if(!n)return;const l=e.dataset.col,a="true"===e.dataset.null,i=e.dataset.value,r=n.dataset.pk,d=n.dataset.table||s.table,u="true"===e.dataset.blob,p="true"===e.dataset.json,f="true"===e.dataset.image,m="true"===e.dataset.audio,h="true"===e.dataset.video,g="true"===e.dataset.pdf,b=e.querySelector("img")&&e.querySelector("img").src||"",y=e.dataset.type||(c(l)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({dbId:o,table:d,pk:r,col:l,colType:y,value:i,isNull:a,isBlob:u,isJson:p,isImage:f,isAudio:m,isVideo:h,isPdf:g,blobUrl:b,readonly:t,onSave:s=>{if(!t){I(n,e,{value:s,type:"text"})}}})}function H(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),l=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),r=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!l)return;l.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),r=i?t.slice(4):"",u=!i&&d(t),p=i?null:c(t),f=p?p.type:"",m=i?r:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+a(r):a(t))+'">'+a(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+a(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),l.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),l.appendChild(h)})}let E=null;r&&r.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!d(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{i=await Api.get("/api/tables/"+o+"/"+l+"/info")}catch(e){i=null}try{r=await Api.get("/api/tables/"+o+"/"+l+"/fk-options")}catch(e){r={}}H(),b(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/"+l+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),M(),function(){let e=null;document.addEventListener("click",e=>{const t=e.target.closest(".js-inspect-btn"),n=e.target.closest("td.cell-editable");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&z(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),z(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,r="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||r||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(c(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:r,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&I(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear"),r=Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"));function c(){const l=q().length;s&&(s.textContent=String(l)),e.classList.toggle("hidden",0===l),n&&(n.checked=l>0&&l===r.length),o&&(o.disabled=t||0===l)}r.forEach(e=>e.addEventListener("change",c)),n&&n.addEventListener("change",()=>{r.forEach(e=>{e.checked=n.checked}),c()}),i&&i.addEventListener("click",()=>{r.forEach(e=>{e.checked=!1}),c()}),l&&l.addEventListener("click",()=>O("csv")),a&&a.addEventListener("click",()=>O("json")),o&&o.addEventListener("click",D)}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+o+"/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.table,s=t.value,o=t.dataset.orderby||"",l=t.dataset.orderdir||"asc",a=t.dataset.filters||"";let i=e+"/tables/"+encodeURIComponent(n)+"?page=1&size="+encodeURIComponent(s)+"&orderBy="+encodeURIComponent(o)+"&orderDir="+encodeURIComponent(l);a&&(i+="&"+a),window.location.href=i})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void _();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),W(e,!0),V(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
|
1
|
+
!function(){"use strict";const e=window.APP&&window.APP.basePath||"",t=!(!window.APP||!window.APP.readonly),n=document.getElementById("browse-config"),s=n?JSON.parse(n.textContent):{table:"",filters:{},pkCols:[],hasPk:!1,readonly:t},o=encodeURIComponent(window.APP&&window.APP.dbId||s&&s.dbId||""),l=encodeURIComponent(s.table),a=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)};let i=null,r={};function c(e){return i?i.columns.find(t=>t.name===e):null}function d(e){return!!i&&-1!==i.primaryKey.indexOf(e)}function u(e){return i?i.foreignKeys.find(t=>t.from===e):null}function p(e,t){const n=e.find(e=>e.op===t);return n?n.value??null:null}function f(e,t,n){if(null==e||""===e)return"";const s=String(e);if(t){const e=s.match(/^(\d{4}-\d{2}-\d{2})/);return e?e[1]:""}if(n){const e=s.match(/(\d{2}:\d{2}(?::\d{2})?)/);return e?e[1]:""}const o=s.match(/^(\d{4}-\d{2}-\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);return o?o[1]+"T"+(o[2]?o[2]+":"+o[3]+(o[4]?":"+o[4]:""):"00:00:00"):""}function m(e,t,n){const s=document.createElement("option");return s.value=t,s.textContent=n,e.appendChild(s),s}function h(e){const t=document.createElement("label");t.className="block";const n=document.createElement("span");n.className="field-label !mb-1 font-mono",n.textContent=e.name,t.appendChild(n);const o=u(e.name),l=(e.type||"").toUpperCase(),a="BOOLEAN"===l||"BOOL"===l,i=l.startsWith("INT")||"REAL"===l||"NUMERIC"===l||l.startsWith("DECIMAL")||"FLOAT"===l||"DOUBLE"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL")||"MONEY"===l,c="DATE"===l,d="TIME"===l||"TIMETZ"===l||l.startsWith("TIME ")||l.startsWith("TIME("),h="DATETIME"===l||l.includes("TIMESTAMP"),g=c||d||h,b=s.filters[e.name],y=function(e){return null==e?[]:Array.isArray(e)?e.filter(e=>e&&"object"==typeof e&&"string"==typeof e.op):"object"==typeof e?[e]:[]}(b),v=function(e){return"string"==typeof e?e:null}(b);if(o){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="fk",m(n,"","(any)"),m(n,"__NULL__","(not set / NULL)"),(r[e.name]||[]).forEach(e=>m(n,String(e.value),""===e.label?String(e.value):e.label));let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"__NULL__"===s?n.value="__NULL__":null!=s&&[...n.options].some(e=>e.value===String(s))&&(n.value=String(s)),t.appendChild(n),t}if(a){const n=document.createElement("select");n.className="filter-input field-select !py-1.5 !text-[13px]",n.dataset.column=e.name,n.dataset.kind="bool",m(n,"","(any)"),m(n,"1","true"),m(n,"0","false");let s=p(y,"eq");return null==s&&v&&v.startsWith("=")&&(s=v.slice(1)),"1"!==s&&"0"!==s&&"true"!==s&&"false"!==s||(n.value="true"===s||"1"===s?"1":"0"),t.appendChild(n),t}if(g||i){const n=document.createElement("div");n.className="flex items-center gap-1.5";const s=document.createElement("input"),o=document.createElement("input");if(g)s.type=o.type=c?"date":d?"time":"datetime-local",(d||h)&&(s.step=o.step="1");else{s.type=o.type="number";const e=l.startsWith("INTEGER")||"INT"===l||l.startsWith("BIGINT")||l.startsWith("SMALLINT")||l.startsWith("SERIAL");s.step=o.step=e?"1":"any"}s.className="filter-input field-input !py-1.5 !text-[13px]",o.className="filter-input field-input !py-1.5 !text-[13px]",s.dataset.column=e.name,s.dataset.bound="min",s.placeholder=g?"from":"min",o.dataset.column=e.name,o.dataset.bound="max",o.placeholder=g?"to":"max";const a=function(e){const t=e.find(e=>"between"===e.op);return t?{min:t.value??null,max:t.max??null}:{min:null,max:null}}(y),i=p(y,"gte")??p(y,"gt")??a.min,r=p(y,"lte")??p(y,"lt")??a.max;return null!=i&&(s.value=g?f(i,c,d):String(i)),null!=r&&(o.value=g?f(r,c,d):String(r)),n.appendChild(s),n.appendChild(o),t.appendChild(n),t}const x=document.createElement("input");x.type="text",x.className="filter-input field-input !py-1.5 !text-[13px]",x.dataset.column=e.name,x.dataset.kind="text",x.placeholder="BLOB"===l?"0x… or text":"filter…",x.autocomplete="off",x.spellcheck=!1;let w=v;if(null==w){const e=p(y,"prefix"),t=p(y,"eq"),n=p(y,"like");null!=e?w=String(e)+"*":null!=t?w="="+String(t):null!=n&&(w=String(n))}return null!=w&&(x.value=w),t.appendChild(x),t}function g(t){let n=s.table;if(!n){const e=window.location.pathname.match(/\/tables\/[^/]+\/([^/]+)(?:\/|$)/);e&&(n=decodeURIComponent(e[1]))}if(!n)return;const l=document.getElementById("page-size"),a=new URLSearchParams({page:"1",size:l?l.value:"50"});Object.keys(t).length&&a.set("f",JSON.stringify(t)),window.location.href=e+"/tables/"+o+"/"+encodeURIComponent(n)+"?"+a.toString()}function b(){const e=document.getElementById("filter-form"),t=document.getElementById("clear-filter"),n=document.getElementById("filter-fields");e&&n&&i&&(i.columns.forEach(e=>n.appendChild(h(e))),e.addEventListener("submit",e=>{e.preventDefault(),g(function(){const e={},t={};document.querySelectorAll("#filter-fields .filter-input").forEach(e=>{const n=e.dataset.column;n&&(t[n]=t[n]||[]).push(e)});for(const n of Object.keys(t)){const s=t[n],o=s[0],l=o.dataset.kind||"";if("fk"===l){const t=o.value;if(""===t)continue;e[n]="__NULL__"===t?{op:"null"}:{op:"eq",value:t}}else if("bool"===l){const t=o.value;if(""===t)continue;e[n]={op:"eq",value:t}}else if(2===s.length&&o.dataset.bound){const t=s.find(e=>"min"===e.dataset.bound),o=s.find(e=>"max"===e.dataset.bound),l=t&&""!==t.value?t.value:null,a=o&&""!==o.value?o.value:null;null!=l&&null!=a?e[n]={op:"between",value:l,max:a}:null!=l?e[n]={op:"gte",value:l}:null!=a&&(e[n]={op:"lte",value:a})}else{const t=(o.value||"").trim();t&&(e[n]=t)}}return e}())})),t&&t.addEventListener("click",()=>g({}))}let y=null;function v(){y&&(y.classList.remove("cell-focused"),y=null)}function x(e){e&&e!==y&&!e.classList.contains("js-editing")&&(v(),y=e,e.classList.add("cell-focused"),e.scrollIntoView({block:"nearest",inline:"nearest"}))}function w(e){const t=e.closest("tr"),n=t&&t.dataset.pk,s=e.dataset.col;if(!n||!s)return;const o=e.dataset.origValue;E(e,"true"===e.dataset.origNull?null:o),e.classList.remove("cell-dirty"),delete e.dataset.origValue,delete e.dataset.origNull,L.delete(n+"::"+s),S(),UI.showToast('Reverted edit in column "'+s+'".',"info")}function E(e,t){const n=null==t,s=n?"":String(t);e.dataset.null=String(n),e.dataset.value=s,e.classList.remove("js-editing","opacity-60");const o=void 0!==e.dataset.origValue,l=e.dataset.origValue,i="true"===e.dataset.origNull,r=o?"Staged edit: "+(n?"NULL":s)+" (Saved: "+(i?"NULL":l)+")":s,d=o?'<button type="button" class="cell-revert-btn js-revert-cell" title="Revert this field to saved value">↺</button>':"";if(n)return void(e.innerHTML='<div class="flex items-center justify-between gap-1"><span class="chip chip-null js-cell-value">NULL</span>'+d+"</div>");const u=c(e.dataset.col),p=(u?.type||e.dataset.type||"").toUpperCase(),f="true"===e.dataset.blob||p.includes("BLOB")||/^0x[0-9a-f]{8,}$/i.test(s),m="true"===e.dataset.json||s.startsWith("{")&&s.endsWith("}")||s.startsWith("[")&&s.endsWith("]");if(f){const t=s.startsWith("0x")?Math.floor((s.length-2)/2):s.length;e.innerHTML='<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB · '+t+' B</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>"}else e.innerHTML=m?'<div class="flex items-center justify-between gap-1 js-cell-value"><div class="flex items-center gap-1.5 min-w-0"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[140px] truncate font-mono text-[12px] text-base-content/70" title="'+a(r)+'">'+a(s)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover:opacity-100 transition-opacity js-inspect-btn" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div>'+d+"</div>":'<div class="flex items-center justify-between gap-1 group/cell"><span class="js-cell-value block max-w-xs truncate font-mono text-[13px]" title="'+a(r)+'">'+a(s.length>120?s.slice(0,120)+"…":s)+'</span><div class="flex items-center gap-0.5 shrink-0">'+d+(s.length>30?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-inspect-btn shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></div>"}function k(e){return"true"===e.dataset.null?null:e.dataset.value??""}const L=new Map;function I(e,t,n){const s=e.dataset.pk,o=t.dataset.col;if(!s||!o)return;const l=c(o);if(!l)return;let a,i=!1;if("checkbox"===n.type)a=n.checked?"1":"0";else{const e=void 0===n.value?"":String(n.value);""===e||"__NULL__"===e?(a="",i=!0):a=e}const r=(l.type||"").toUpperCase(),p="BOOLEAN"===r||"BOOL"===r,f=void 0!==t.dataset.origValue?"true"===t.dataset.origNull:"true"===t.dataset.null,m=void 0!==t.dataset.origValue?t.dataset.origValue??"":t.dataset.value??"";let h=!1;if(i&&f)h=!0;else if(!i&&!f)if(p){h=("1"===m||"true"===m||1===m||!0===m)===("1"===a||"true"===a)}else h=String(m)===String(a);if(h)return delete t.dataset.origValue,delete t.dataset.origNull,t.classList.remove("cell-dirty"),L.delete(s+"::"+o),E(t,i?null:a),void S();void 0===t.dataset.origValue&&(t.dataset.origValue=m,t.dataset.origNull=f?"true":"false"),E(t,i?null:a),L.set(s+"::"+o,{td:t,pk:s,col:o,rawValue:a,setNull:i,isPk:d(o),isFk:!!u(o),origValue:t.dataset.origValue,origNull:"true"===t.dataset.origNull}),t.classList.add("cell-dirty"),S()}function S(){if(!j||!C)return;const e=L.size;C.textContent=String(e),j.classList.toggle("hidden",0===e)}function T(){if(!L.size||t)return;const e=new Map;for(const t of L.values()){let n=e.get(t.pk);n||(n={id:t.pk,values:{},nulls:[]},e.set(t.pk,n)),t.setNull?n.nulls.push(t.col):n.values[t.col]=t.rawValue}const n=[...e.values()];B&&(B.disabled=!0),Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-update",{updates:n}).then(e=>{UI.showToast(e.message||"Changes applied.","success"),window.location.reload()}).catch(e=>{UI.showError(e.message),B&&(B.disabled=!1)})}async function A(){const e=L.size;if(!e)return;if(await UI.confirm({title:"Discard Changes",message:"You have "+e+" pending inline edit(s). Are you sure you want to discard all staged changes and revert to the saved values?",confirmText:"Discard "+e+" change(s)",danger:!0})){for(const e of L.values())E(e.td,e.origNull?null:e.origValue),e.td.classList.remove("cell-dirty"),delete e.td.dataset.origValue,delete e.td.dataset.origNull;L.clear(),S(),UI.showToast("Pending edits discarded.","info")}}function N(){if(L.size){var e=document.createElement("div");e.className="fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-xs p-4 opacity-0 transition-opacity duration-200";var t=document.createElement("div");t.className="app-card w-full max-w-3xl overflow-hidden shadow-2xl border border-base-300 transform scale-95 transition-transform duration-200 max-h-[85vh] flex flex-col";var n="";L.forEach((e,t)=>{var s=e.origNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.origValue),o=e.setNull?'<span class="chip chip-null text-[10px]">NULL</span>':a(e.rawValue);n+='<tr class="hover:bg-base-200/50"><td class="font-mono text-xs font-semibold">'+a(e.pk)+'</td><td class="font-mono text-xs text-primary font-medium">'+a(e.col)+'</td><td class="font-mono text-xs text-error line-through opacity-80 max-w-xs truncate">'+s+'</td><td class="font-mono text-xs text-success font-medium max-w-xs truncate">'+o+'</td><td class="text-right"><button type="button" class="btn btn-ghost btn-xs text-warning js-modal-revert-item" data-key="'+a(t)+'">Revert</button></td></tr>'}),t.innerHTML='<div class="flex items-center justify-between border-b border-base-200 px-6 py-4"> <div class="flex items-center gap-2"> <h3 class="text-base font-bold text-base-content">Staged Changes Diff</h3> <span class="chip bg-warning/15 text-warning font-mono">'+L.size+' edit(s)</span> </div> <button type="button" class="btn btn-ghost btn-xs btn-circle js-diff-close text-base-content/40 hover:text-base-content">×</button></div><div class="flex-1 overflow-y-auto p-4"> <table class="table table-sm"> <thead><tr class="text-xs uppercase text-base-content/50"><th>Row PK</th><th>Column</th><th>Original Value</th><th>Staged Value</th><th class="text-right">Action</th></tr></thead> <tbody id="diff-rows-body">'+n+'</tbody> </table></div><div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-6 py-4"> <button type="button" id="diff-modal-discard" class="btn btn-ghost btn-sm text-error">Discard All</button> <div class="flex gap-2"> <button type="button" class="btn btn-ghost btn-sm js-diff-close">Cancel</button> <button type="button" id="diff-modal-apply" class="btn btn-primary btn-sm gap-1"> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg> Apply '+L.size+" Change(s) </button> </div></div>",e.appendChild(t),document.body.appendChild(e),requestAnimationFrame(()=>{e.classList.remove("opacity-0"),e.classList.add("opacity-100"),t.classList.remove("scale-95"),t.classList.add("scale-100")}),t.querySelectorAll(".js-diff-close").forEach(e=>e.addEventListener("click",s)),e.addEventListener("click",t=>{t.target===e&&s()}),t.querySelectorAll(".js-modal-revert-item").forEach(e=>{e.addEventListener("click",()=>{const t=e.dataset.key,n=L.get(t);n&&(w(n.td),e.closest("tr").remove(),L.size||s())})}),t.querySelector("#diff-modal-apply").addEventListener("click",()=>{s(),T()}),t.querySelector("#diff-modal-discard").addEventListener("click",async()=>{s(),await A()})}function s(){e.classList.remove("opacity-100"),e.classList.add("opacity-0"),t.classList.remove("scale-100"),t.classList.add("scale-95"),setTimeout(()=>e.remove(),180)}}let j=null,C=null,B=null;function U(e){if(t)return;if(e.classList.contains("js-editing"))return;const n=e.closest("tr");if(!n||!n.dataset.pk||!n.dataset.table)return;if(!c(e.dataset.col))return;x(e);const s=document.querySelector(".js-editing");s&&s._finish&&s._finish(!0),e._origNull="true"===e.dataset.null,e._origValue=k(e);const o=function(e){const t=e.dataset.col,n=c(t);if(!n)return null;const s=u(t),o=(n.type||"").toUpperCase(),l="BOOLEAN"===o||"BOOL"===o,a="DATE"===o,i="TIME"===o||"TIMETZ"===o||o.startsWith("TIME ")||o.startsWith("TIME("),d="DATETIME"===o||o.includes("TIMESTAMP"),p=a||i||d,h=o.startsWith("INT")||"REAL"===o||"NUMERIC"===o||o.startsWith("DECIMAL")||"FLOAT"===o||"DOUBLE"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL")||"MONEY"===o,g="BLOB"===o||"BYTEA"===o||"BINARY"===o,b="UUID"===o,y=o.includes("JSON"),v=o.endsWith("[]")||o.startsWith("_")||o.includes("ARRAY"),x="INTERVAL"===o,w="POINT"===o||"GEOMETRY"===o||"BOX"===o||"CIRCLE"===o||"POLYGON"===o,E="BIT"===o||"VARBIT"===o||o.startsWith("BIT(")||o.startsWith("VARBIT("),L=k(e),I=null===L||""===L;let S;if(l)S=document.createElement("input"),S.type="checkbox",S.className="checkbox checkbox-primary checkbox-sm js-inline-ctrl",S.checked=1===L||!0===L||"1"===L||"true"===L;else if(s)S=document.createElement("select"),S.className="field-select !w-64 !py-1.5 !text-[13px] js-inline-ctrl",m(S,"","(not set / NULL)"),(r[t]||[]).forEach(e=>{const t=m(S,String(e.value),""===e.label?String(e.value):e.label);I||String(L)!==String(e.value)||(t.selected=!0)});else if(p)S=document.createElement("input"),S.type=a?"date":i?"time":"datetime-local",(i||d)&&(S.step="1"),S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=f(String(L),a,i)),setTimeout(()=>{if("function"==typeof S.showPicker)try{S.showPicker()}catch(e){}},50);else if(h){S=document.createElement("input"),S.type="number";const e=o.startsWith("INTEGER")||"INT"===o||o.startsWith("BIGINT")||o.startsWith("SMALLINT")||o.startsWith("SERIAL");S.step=e?"1":"any",S.className="field-input !w-48 !py-1.5 !text-[13px] js-inline-ctrl font-mono",I||(S.value=String(L))}else b?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="00000000-0000-0000-0000-000000000000",I||(S.value=String(L))):v?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="{item1, item2}",I||(S.value=String(L))):x?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="e.g. '1 day'",I||(S.value=String(L))):w?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="(x, y)",I||(S.value=String(L))):E?(S=document.createElement("input"),S.type="text",S.className="field-input font-mono !w-64 !py-1.5 !text-[12px] js-inline-ctrl",S.placeholder="101010",I||(S.value=String(L))):(S=document.createElement("input"),S.type="text",S.className="field-input !w-64 !py-1.5 !text-[13px] js-inline-ctrl",S.placeholder=g?"BYTEA"===o?"\\x… or 0x… hex":"0x… hex or text":y?'{"key":"value"}':"",I||(S.value=String(L)));return S}(e);if(!o)return;e.classList.add("js-editing"),e.innerHTML="",e.appendChild(o);let l=!1;const a=(t,s)=>{l||(l=!0,t?I(n,e,o):function(e){E(e,e._origNull?null:e._origValue),e.classList.remove("js-editing")}(e),"down"===s?M("down"):"right"===s?M("right"):"left"===s&&M("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 M(e){const t=e=>Array.prototype.slice.call(e.querySelectorAll("td.cell-editable")).filter(e=>"none"!==e.style.display&&!e.classList.contains("hidden"));if(!y){const e=document.querySelector("#main-table tbody tr");if(e){const n=t(e);n.length&&x(n[0])}return}const n=y.closest("tr");if(!n)return;const s=t(n),o=s.indexOf(y);if("right"===e)if(o>=0&&o<s.length-1)x(s[o+1]);else{const e=n.nextElementSibling;if(e){const n=t(e);n.length&&x(n[0])}}else if("left"===e)if(o>0)x(s[o-1]);else{const e=n.previousElementSibling;if(e){const n=t(e);n.length&&x(n[n.length-1])}}else if("down"===e){const e=n.nextElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}else if("up"===e){const e=n.previousElementSibling;if(e){const n=y.dataset.col,s=n?e.querySelector('td[data-col="'+CSS.escape(n)+'"]'):null;if(s&&"none"!==s.style.display)x(s);else{const n=t(e);(n[o]||n[0])&&x(n[o]||n[0])}}}}function R(){j=function(){const e=document.createElement("div");return e.id="changes-bar",e.className="fixed bottom-5 left-1/2 z-50 flex -translate-x-1/2 items-center gap-3 rounded-2xl border border-base-300 bg-base-100/95 backdrop-blur-md px-4 py-2.5 shadow-2xl transition-all duration-200 hidden",e.innerHTML='<div class="flex items-center gap-2"><span class="relative flex h-2.5 w-2.5"><span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-warning opacity-75"></span><span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-warning"></span></span><span class="text-sm font-medium text-base-content"><b id="changes-count" class="tabular-nums text-warning font-bold">0</b> pending edit(s)</span></div><div class="h-4 w-px bg-base-300"></div><div class="flex items-center gap-1.5"><button type="button" id="changes-review" class="btn btn-ghost btn-sm gap-1.5 text-xs font-medium"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>Review Diff</button><button type="button" id="changes-apply" class="btn btn-primary btn-sm gap-1 text-xs font-semibold"><svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>Apply</button><button type="button" id="changes-discard" class="btn btn-ghost btn-sm text-xs text-base-content/60 hover:text-error">Discard</button></div>',document.body.appendChild(e),e}(),C=document.getElementById("changes-count"),B=document.getElementById("changes-apply");const e=document.getElementById("changes-discard"),n=document.getElementById("changes-review");B&&B.addEventListener("click",T),e&&e.addEventListener("click",A),n&&n.addEventListener("click",N),document.addEventListener("click",e=>{const t=e.target.closest(".js-revert-cell");if(t){e.stopPropagation(),e.preventDefault();const n=t.closest("td");return void(n&&w(n))}const n=e.target.closest("td.cell-editable");n&&!n.classList.contains("js-editing")&&x(n)}),t||document.querySelectorAll(".js-inline-cell").forEach(e=>{e.addEventListener("dblclick",()=>U(e))}),document.addEventListener("keydown",e=>{if(document.querySelector(".js-editing"))return;const n=document.activeElement?document.activeElement.tagName:"";if("INPUT"!==n&&"TEXTAREA"!==n&&"SELECT"!==n&&y)if("ArrowRight"===e.key)e.preventDefault(),M("right");else if("ArrowLeft"===e.key)e.preventDefault(),M("left");else if("ArrowDown"===e.key)e.preventDefault(),M("down");else if("ArrowUp"===e.key)e.preventDefault(),M("up");else if("Tab"===e.key)e.preventDefault(),M(e.shiftKey?"left":"right");else if("Enter"===e.key)e.preventDefault(),t||U(y);else if("Escape"===e.key)v();else if(" "!==e.key||t){if("i"===e.key||"I"===e.key)e.preventDefault(),K(y);else if((e.ctrlKey||e.metaKey)&&"c"===e.key.toLowerCase()){const e=k(y);null!=e&&navigator.clipboard.writeText(String(e)).then(()=>{UI.showToast("Cell value copied.","info")})}else if("x"===e.key||"X"===e.key||e.shiftKey&&" "===e.key){const t=y?y.closest("tr"):null;if(t){const n=t.querySelector(".row-checkbox");n&&(e.preventDefault(),n.checked=!n.checked,n.dispatchEvent(new Event("change",{bubbles:!0})))}}}else{const t=c(y.dataset.col);if(t&&"BOOLEAN"===(t.type||"").toUpperCase()){e.preventDefault();const t=y.closest("tr"),n=k(y),s=1===n||"1"===n||!0===n||"true"===n?"0":"1";I(t,y,{value:s,type:"checkbox",checked:"1"===s})}else e.preventDefault(),K(y)}})}function q(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox"))}function O(){return Array.prototype.slice.call(document.querySelectorAll(".row-checkbox:checked")).map(e=>e.value).filter(Boolean)}let D=null;async function W(e){const t=O();if(t.length)try{const n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-export",{ids:t,format:e}),s=new Blob([n.content],{type:"json"===e?"application/json;charset=utf-8":"text/csv;charset=utf-8"}),a=document.createElement("a");a.href=URL.createObjectURL(s),a.download=n.filename,document.body.appendChild(a),a.click(),a.remove(),URL.revokeObjectURL(a.href),UI.showToast("Exported "+n.rowCount+" row(s).","success")}catch(e){UI.showError(e.message)}}async function P(){const e=O();if(!e.length||t)return;let n;try{n=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-impact",{ids:e})}catch(e){return void UI.showError(e.message)}const i=n&&n.references||[],r=i.length?'<p class="mb-2 font-medium text-warning">⚠ Other tables reference these records:</p><ul class="mb-2 list-disc space-y-1 pl-5 text-xs">'+i.map(e=>'<li><code class="font-mono font-bold">'+a(e.table)+"."+a(e.from)+'</code> — <b class="tabular-nums">'+e.count+"</b> row(s) point at them</li>").join("")+'</ul><p class="text-[11px] text-base-content/55">Depending on foreign key rules, those rows may cascade-delete or block this action.</p>':'<p class="text-xs text-base-content/60">No other tables reference these rows — safe to delete.</p>';if(await UI.confirm({title:"Delete Selected Rows",message:"Delete "+e.length+' selected row(s) from table "'+s.table+'"? This action cannot be undone.',item:s.table,itemType:"Table",detailsHtml:r,confirmText:"Delete "+e.length+" row(s)",danger:!0}))try{const t=await Api.post("/api/tables/"+o+"/"+l+"/rows/bulk-delete",{ids:e,confirmImpact:!0});UI.showToast(t.message||"Rows deleted.","success"),window.location.reload()}catch(e){UI.showError(e.message)}}function _(e,t){const n=e.querySelector(".js-ref-chevron");n&&n.classList.toggle("rotate-180",t)}let V=null;function z(){const e=document.querySelector(".js-ref-pop");e&&(e._cleanup&&e._cleanup(),e.remove()),V&&(_(V,!1),V.setAttribute("aria-expanded","false"),V.classList.remove("opacity-50"),V=null)}function H(t,n){z();const s=document.createElement("div");s.className="js-ref-pop app-card js-ref-anim z-50 min-w-[280px] max-w-[460px] overflow-hidden shadow-lift",s.innerHTML=n.length?n.map(t=>function(e,t){const n=t+"/tables/"+encodeURIComponent(e.table),s=e.count??e.total??(e.rows?e.rows.length:0),o=e.columns||(e.rows&&e.rows.length>0?Object.keys(e.rows[0]).filter(e=>"_rowid_"!==e):[]);let l='<div class="flex items-center justify-between gap-2 border-b border-base-200 px-3 py-2"><span class="min-w-0 truncate"><a class="link link-primary font-mono text-xs font-semibold" href="'+n+'">'+a(e.table)+'</a><span class="ml-1.5 text-xs text-base-content/45">via <code class="font-mono">'+a(e.from)+"</code>"+(void 0!==e.value?' = <code class="font-mono">'+a(String(e.value))+"</code>":"")+'</span></span><span class="chip '+(s?"chip-pk":"chip-type")+'">'+a(s+" row"+(1===s?"":"s"))+"</span></div>";return e.rows&&e.rows.length&&o.length?l+'<div class="max-h-64 overflow-auto"><table class="w-full"><thead><tr class="bg-base-200/40"><tr>'+o.map(function(e){return'<th class="whitespace-nowrap px-2.5 py-1.5 text-left text-[10px] font-semibold uppercase tracking-wide text-base-content/50"><span class="font-mono normal-case">'+a(e)+"</span></th>"}).join("")+"</tr></tr></thead><tbody>"+e.rows.map(function(e){return'<tr class="border-t border-base-200/60 hover:bg-primary/[0.04]">'+o.map(function(t){const n=e[t];if(null==n)return'<td class="px-2.5 py-1.5 align-middle"><span class="text-[11px] text-base-content/40">NULL</span></td>';const s=String(n),o=s.length>120?s.slice(0,120)+"…":s;return'<td class="max-w-[180px] truncate px-2.5 py-1.5 align-middle font-mono text-[11px] text-base-content/80" title="'+a(s)+'">'+a(o)+"</td>"}).join("")+"</tr>"}).join("")+'</tbody></table></div><div class="flex justify-end border-t border-base-200 px-3 py-1.5"><a class="btn btn-ghost btn-xs text-base-content/60 hover:text-primary" href="'+n+'">Open table</a></div>':l+'<div class="px-3 py-3 text-xs text-base-content/45">No matching rows.</div>'}(t,e)).join('<div class="h-px bg-base-200"></div>'):'<div class="px-4 py-3 text-sm text-base-content/45">No related rows for this record.</div>',s.style.visibility="hidden",document.body.appendChild(s);const o=Math.min(460,Math.max(280,s.offsetWidth)),l=s.offsetHeight,i=t.getBoundingClientRect();let r=i.left;r+o>window.innerWidth-8&&(r=Math.max(8,window.innerWidth-o-8));let c=i.bottom+6;c+l>window.innerHeight-8&&(c=Math.max(8,i.top-l-6)),s.style.position="fixed",s.style.left=r+"px",s.style.top=c+"px",s.style.visibility="visible",V=t;const d=e=>{e&&e.target&&s.contains(e.target)||z()};window.addEventListener("scroll",d,!0),window.addEventListener("resize",d);const u=e=>{s.contains(e.target)||e.target===t||t.contains(e.target)||z()};document.addEventListener("click",u,!0);const p=e=>{"Escape"===e.key&&z()};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 K(e){if(!e)return;const n=e.closest("tr");if(!n)return;const l=e.dataset.col,a="true"===e.dataset.null,i=e.dataset.value,r=n.dataset.pk,d=n.dataset.table||s.table,u="true"===e.dataset.blob,p="true"===e.dataset.json,f="true"===e.dataset.image,m="true"===e.dataset.audio,h="true"===e.dataset.video,g="true"===e.dataset.pdf,b=e.querySelector("img")&&e.querySelector("img").src||"",y=e.dataset.type||(c(l)?.type??"");window.Inspector&&window.Inspector.open&&window.Inspector.open({dbId:o,table:d,pk:r,col:l,colType:y,value:i,isNull:a,isBlob:u,isJson:p,isImage:f,isAudio:m,isVideo:h,isPdf:g,blobUrl:b,readonly:t,onSave:s=>{if(!t){I(n,e,{value:s,type:"text"})}}})}function J(){const e=document.getElementById("main-table");if(!e)return;const t=document.getElementById("col-manager-modal"),n=document.getElementById("btn-columns-manager"),o=document.getElementById("col-count-badge"),l=document.getElementById("col-manager-list"),i=document.getElementById("col-search"),r=document.getElementById("col-btn-show-all"),u=document.getElementById("col-btn-hide-non-pk"),p=document.getElementById("col-btn-reset"),f=document.getElementById("col-manager-summary"),m=Array.prototype.slice.call(e.querySelectorAll("thead th[data-col]"));if(!m.length)return;const h=m.map(e=>e.dataset.col),g="admindb:col_settings:"+(s.table||"default");let b=function(){try{const e=localStorage.getItem(g);if(e){const t=JSON.parse(e);if(t&&"object"==typeof t){const e=new Set(h),n=Array.isArray(t.order)?t.order.filter(t=>e.has(t)):[];h.forEach(e=>{-1===n.indexOf(e)&&n.push(e)});const s=Array.isArray(t.hidden)?t.hidden.filter(t=>e.has(t)):[],o=Array.isArray(t.pinned)?t.pinned.filter(t=>e.has(t)):[];return{order:n,hidden:s,pinned:o}}}}catch(e){}return{order:h.slice(),hidden:[],pinned:[]}}();function y(){try{localStorage.setItem(g,JSON.stringify(b))}catch(e){}v(),w()}function v(){const t=b.order,n=new Set(b.hidden),s=new Set(b.pinned),l=e.querySelector("thead tr");if(l){const e=l.querySelector("th.sticky-col-actions"),o={};m.forEach(e=>{o[e.dataset.col]=e}),t.forEach(t=>{const a=o[t];if(a){e?l.insertBefore(a,e):l.appendChild(a);const o=n.has(t);a.style.display=o?"none":"";const i=s.has(t)&&!o;a.classList.toggle("sticky-col-left",i)}})}e.querySelectorAll("tbody tr").forEach(e=>{const o=Array.prototype.slice.call(e.querySelectorAll("td[data-col]")),l={};o.forEach(e=>{l[e.dataset.col]=e});const a=e.querySelector("td.sticky-col-actions");t.forEach(t=>{const o=l[t];if(o){a?e.insertBefore(o,a):e.appendChild(o);const l=n.has(t);o.style.display=l?"none":"";const i=s.has(t)&&!l;o.classList.toggle("sticky-col-left",i)}})}),x();const a=h.length-b.hidden.length;o&&(o.textContent=a+"/"+h.length,o.classList.toggle("badge-warning",b.hidden.length>0)),f&&(f.textContent=a+" of "+h.length+" visible · "+b.pinned.length+" pinned")}function x(){const t=b.order.filter(e=>b.pinned.includes(e)&&!b.hidden.includes(e)),n=e.querySelector("thead th.th-select-all");let s=n?n.offsetWidth:40;e.querySelectorAll(".sticky-col-left-last").forEach(e=>e.classList.remove("sticky-col-left-last")),t.forEach((n,o)=>{const l=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]'),a=l?l.offsetWidth:120,i=o===t.length-1;l&&(l.style.left=s+"px",i&&l.classList.add("sticky-col-left-last")),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=s+"px",i&&e.classList.add("sticky-col-left-last")}),s+=a}),b.order.forEach(n=>{if(!t.includes(n)){const t=e.querySelector('thead th[data-col="'+CSS.escape(n)+'"]');t&&(t.style.left=""),e.querySelectorAll('tbody td[data-col="'+CSS.escape(n)+'"]').forEach(e=>{e.style.left=""})}})}function w(){if(!l)return;l.innerHTML="";const e=(i?i.value:"").toLowerCase().trim();b.order.forEach((t,n)=>{if(e&&-1===t.toLowerCase().indexOf(e))return;const s=b.hidden.includes(t),o=b.pinned.includes(t),i=t.startsWith("ref:"),r=i?t.slice(4):"",u=!i&&d(t),p=i?null:c(t),f=p?p.type:"",m=i?r:t,h=document.createElement("div");h.className="col-manager-item"+(s?" is-hidden":"")+(o?" is-pinned":""),h.dataset.col=t,h.dataset.index=String(n),h.draggable=!0,h.innerHTML='<div class="flex items-center gap-2 min-w-0 flex-1"><span class="col-drag-handle" title="Drag to reorder"><svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="currentColor"><circle cx="9" cy="7" r="1.5"/><circle cx="15" cy="7" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="17" r="1.5"/><circle cx="15" cy="17" r="1.5"/></svg></span><input type="checkbox" class="checkbox checkbox-primary checkbox-xs js-col-toggle" '+(s?"":"checked")+' title="Toggle visibility" /><span class="font-mono font-medium truncate text-base-content text-[13px]" title="'+(i?"Referencing table: "+a(r):a(t))+'">'+a(m)+"</span>"+(i?'<span class="chip chip-type text-[9px] font-mono bg-secondary/15 text-secondary">REF</span>':"")+(u?'<span class="chip chip-pk text-[9px]">PK</span>':"")+(f?'<span class="chip chip-type text-[9px] font-mono opacity-70">'+a(f)+"</span>":"")+'</div><div class="flex items-center gap-1 shrink-0"><button type="button" class="btn btn-ghost btn-xs btn-square js-col-pin '+(o?"text-primary bg-primary/10":"text-base-content/40 hover:text-base-content")+'" title="'+(o?"Unpin column":"Pin column to left (sticky)")+'"><svg class="h-3.5 w-3.5" fill="'+(o?"currentColor":"none")+'" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 13.5l-5-5m3-3.5a2.121 2.121 0 00-3 0L9.5 9.5 5 10l5 5 .5 4.5 4.5-4.5a2.121 2.121 0 000-3zM4 20l5-5" /></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-up" '+(0===n?"disabled":"")+' title="Move Up"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg></button><button type="button" class="btn btn-ghost btn-xs btn-square text-base-content/40 hover:text-base-content js-col-down" '+(n===b.order.length-1?"disabled":"")+' title="Move Down"><svg class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg></button></div>';const g=h.querySelector(".js-col-toggle");g&&g.addEventListener("change",()=>{g.checked?b.hidden=b.hidden.filter(e=>e!==t):b.hidden.includes(t)||b.hidden.push(t),y()});const v=h.querySelector(".js-col-pin");v&&v.addEventListener("click",()=>{b.pinned.includes(t)?b.pinned=b.pinned.filter(e=>e!==t):(b.pinned.push(t),b.hidden=b.hidden.filter(e=>e!==t)),y()});const x=h.querySelector(".js-col-up");x&&n>0&&x.addEventListener("click",()=>{const e=b.order[n-1];b.order[n-1]=b.order[n],b.order[n]=e,y()});const w=h.querySelector(".js-col-down");w&&n<b.order.length-1&&w.addEventListener("click",()=>{const e=b.order[n+1];b.order[n+1]=b.order[n],b.order[n]=e,y()}),function(e,t){e.addEventListener("dragstart",n=>{E=t,e.classList.add("is-dragging"),n.dataTransfer&&(n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",t))}),e.addEventListener("dragend",()=>{E=null,e.classList.remove("is-dragging"),l.querySelectorAll(".drag-over-top, .drag-over-bottom").forEach(e=>{e.classList.remove("drag-over-top","drag-over-bottom")})}),e.addEventListener("dragover",n=>{if(n.preventDefault(),n.dataTransfer&&(n.dataTransfer.dropEffect="move"),!E||E===t)return;const s=e.getBoundingClientRect(),o=s.top+s.height/2;n.clientY<o?(e.classList.add("drag-over-top"),e.classList.remove("drag-over-bottom")):(e.classList.add("drag-over-bottom"),e.classList.remove("drag-over-top"))}),e.addEventListener("dragleave",()=>{e.classList.remove("drag-over-top","drag-over-bottom")}),e.addEventListener("drop",n=>{if(n.preventDefault(),e.classList.remove("drag-over-top","drag-over-bottom"),!E||E===t)return;const s=b.order.indexOf(E);let o=b.order.indexOf(t);if(-1===s||-1===o)return;const l=e.getBoundingClientRect(),a=l.top+l.height/2;b.order.splice(s,1),o=b.order.indexOf(t),n.clientY>=a&&(o+=1),b.order.splice(o,0,E),y()})}(h,t),l.appendChild(h)})}let E=null;r&&r.addEventListener("click",()=>{b.hidden=[],y()}),u&&u.addEventListener("click",()=>{b.hidden=h.filter(e=>!d(e)),y()}),p&&p.addEventListener("click",()=>{b={order:h.slice(),hidden:[],pinned:[]};try{localStorage.removeItem(g)}catch(e){}y(),UI.showToast("Columns reset to default.","info")}),i&&i.addEventListener("input",w),n&&t&&n.addEventListener("click",()=>{w(),"function"==typeof t.showModal?t.showModal():t.classList.add("modal-open"),i&&(i.value="",setTimeout(()=>i.focus(),50))}),v(),window.addEventListener("resize",x)}!async function(){try{i=await Api.get("/api/tables/"+o+"/"+l+"/info")}catch(e){i=null}try{r=await Api.get("/api/tables/"+o+"/"+l+"/fk-options")}catch(e){r={}}J(),b(),document.querySelectorAll(".js-row-copy-json").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const s={};n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;t&&(s[t]="true"===e.dataset.null?null:e.dataset.value)}),navigator.clipboard.writeText(JSON.stringify(s,null,2)).then(()=>{UI.showToast("Row copied as JSON.","success")})})}),document.querySelectorAll(".js-row-copy-sql").forEach(e=>{e.addEventListener("click",t=>{t.stopPropagation();const n=e.closest("tr");if(!n)return;const o=[],l=[];n.querySelectorAll("td.cell-editable").forEach(e=>{const t=e.dataset.col;if(t)if(o.push('"'+t.replace(/"/g,'""')+'"'),"true"===e.dataset.null)l.push("NULL");else{const t=e.dataset.value;l.push("'"+String(t).replace(/'/g,"''")+"'")}});const a='INSERT INTO "'+s.table.replace(/"/g,'""')+'" ('+o.join(", ")+") VALUES ("+l.join(", ")+");";navigator.clipboard.writeText(a).then(()=>{UI.showToast("SQL INSERT copied to clipboard.","success")})})}),document.querySelectorAll(".js-row-duplicate").forEach(t=>{t.addEventListener("click",n=>{n.stopPropagation();const s=t.dataset.pk;s&&(window.location.href=e+"/tables/"+o+"/"+l+"/rows/new?duplicate="+encodeURIComponent(s))})}),document.querySelectorAll(".dropdown").forEach(e=>{const t=e.querySelector('label[role="button"]')||e.querySelector("button");t&&t.addEventListener("click",()=>{const n=t.getBoundingClientRect(),s=window.innerHeight-n.bottom;e.classList.toggle("dropdown-top",s<220)})}),R(),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&&K(n))}n&&e.altKey&&(e.stopPropagation(),e.preventDefault(),K(n))}),document.addEventListener("mouseover",n=>{const o=n.target.closest("td.cell-editable");o&&!o.classList.contains("js-editing")&&(clearTimeout(e),e=setTimeout(()=>{const e=o.dataset.value,n="true"===o.dataset.blob,l="true"===o.dataset.json,a="true"===o.dataset.image,i="true"===o.dataset.audio,r="true"===o.dataset.video,d="true"===o.dataset.pdf,u=e&&e.length>40;if(!(n||l||a||i||r||d||u))return;const p=o.dataset.col,f=o.closest("tr")?.dataset.pk,m=o.closest("tr")?.dataset.table||s.table,h=o.querySelector("img")&&o.querySelector("img").src||"",g=o.dataset.type||(c(p)?.type??"");window.Inspector&&window.Inspector.peek&&window.Inspector.peek(o,{table:m,pk:f,col:p,colType:g,value:e,isNull:"true"===o.dataset.null,isBlob:n,isJson:l,isImage:a,isAudio:i,isVideo:r,isPdf:d,blobUrl:h,readonly:t,onSave:e=>{if(!t){const t=o.closest("tr");t&&I(t,o,{value:e,type:"text"})}}})},500))}),document.addEventListener("mouseout",t=>{t.target.closest("td.cell-editable")&&clearTimeout(e)})}(),function(){const e=document.getElementById("bulk-bar");if(!e)return;const n=document.getElementById("select-all-rows"),s=document.getElementById("bulk-count"),o=document.getElementById("bulk-delete"),l=document.getElementById("bulk-export-csv"),a=document.getElementById("bulk-export-json"),i=document.getElementById("bulk-clear");function r(){const l=q(),a=O().length;s&&(s.textContent=String(a)),e.classList.toggle("hidden",0===a),n&&(0===l.length?(n.checked=!1,n.indeterminate=!1,n.disabled=!0):(n.disabled=!1,0===a?(n.checked=!1,n.indeterminate=!1):a===l.length?(n.checked=!0,n.indeterminate=!1):(n.checked=!1,n.indeterminate=!0))),o&&(o.disabled=t||0===a),l.forEach(e=>{const t=e.closest("tr");t&&(e.checked?t.classList.add("bg-primary/[0.08]","is-selected"):t.classList.remove("bg-primary/[0.08]","is-selected"))})}function c(e,t){const n=q();if(e.shiftKey&&D&&D!==t){const e=n.indexOf(D),s=n.indexOf(t);if(-1!==e&&-1!==s){const o=Math.min(e,s),l=Math.max(e,s),a=t.checked;for(let e=o;e<=l;e++)n[e].checked=a}}D=t,r()}const d=document.getElementById("main-table");d&&d.addEventListener("click",e=>{if(e.target&&e.target.classList.contains("row-checkbox"))return void c(e,e.target);const t=e.target.closest("td");if(t&&t.querySelector(".row-checkbox")&&!e.target.closest("a, button, input, select, textarea")){const n=t.querySelector(".row-checkbox");n&&(n.checked=!n.checked,c(e,n))}}),n&&n.addEventListener("click",e=>{const t=q(),s=n.checked;t.forEach(e=>{e.checked=s}),D=null,r()}),i&&i.addEventListener("click",()=>{q().forEach(e=>{e.checked=!1}),D=null,r()}),l&&l.addEventListener("click",()=>W("csv")),a&&a.addEventListener("click",()=>W("json")),o&&o.addEventListener("click",P),r()}(),document.querySelectorAll(".js-delete-row").forEach(e=>{e.addEventListener("click",async()=>{const t=e.dataset.table,n=e.dataset.pk;t&&n&&await UI.confirm({title:"Delete Row",message:"Are you sure you want to permanently delete this row? This action cannot be undone.",item:n,itemType:"Primary Key",confirmText:"Delete row",danger:!0})&&Api.del("/api/tables/"+o+"/"+encodeURIComponent(t)+"/row/"+encodeURIComponent(n)).then(()=>{UI.showToast("Row deleted.","success"),window.location.reload()}).catch(e=>UI.showError(e.message))})}),function(){const t=document.getElementById("page-size");t&&t.addEventListener("change",()=>{const n=t.dataset.db,s=t.dataset.table,o=t.value,l=t.dataset.orderby||"",a=t.dataset.orderdir||"asc",i=t.dataset.filters||"";let r=e+"/tables/"+encodeURIComponent(n)+"/"+encodeURIComponent(s)+"?page=1&size="+encodeURIComponent(o)+"&orderBy="+encodeURIComponent(l)+"&orderDir="+encodeURIComponent(a);i&&(r+="&"+i),window.location.href=r})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void z();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),_(e,!0),H(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
package/dist/public/js/query.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){"use strict";const e=encodeURIComponent(window.APP&&window.APP.dbId||""),t=document.getElementById("query-sql"),n=document.getElementById("query-code"),s=document.getElementById("query-highlight"),o=document.getElementById("query-gutter"),r=document.getElementById("editor-cursor-pos"),i=document.getElementById("editor-stats"),a=document.getElementById("run-query"),l=document.getElementById("format-query"),c=document.getElementById("copy-query"),d=document.getElementById("clear-query"),u=document.getElementById("query-message"),p=document.getElementById("query-result"),m=document.getElementById("query-result-head"),f=document.getElementById("query-result-body"),g=document.getElementById("result-summary"),h=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),w=document.getElementById("save-query-name"),y=document.getElementById("saved-query-select"),x=document.getElementById("delete-query"),v=document.getElementById("export-csv"),b=document.getElementById("export-json");let E=[],I=null;const L=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},T=window.Utils?window.Utils.highlightSql:function(e){return L(e)};const k=new class{constructor(e=100){this.stack=[],this.index=-1,this.limit=e,this.lastRecordTime=0}record(e,t,n=250){const s=Date.now(),o=this.index>=0?this.stack[this.index]:null;if(o&&o.text===e)o.selection=t;else{if(o&&s-this.lastRecordTime<n&&1===Math.abs(e.length-o.text.length)&&!/\s|[;(),]/.test(e.slice(-1)))return o.text=e,o.selection=t,void(this.lastRecordTime=s);this.stack=this.stack.slice(0,this.index+1),this.stack.push({text:e,selection:t}),this.stack.length>this.limit?this.stack.shift():this.index++,this.lastRecordTime=s}}undo(){return this.index>0?(this.index--,this.stack[this.index]):null}redo(){return this.index<this.stack.length-1?(this.index++,this.stack[this.index]):null}}(100);function q(){return t?t.value:""}function S(){if(!t||!n)return;const e=t.value;n.innerHTML=T(e)+(e.endsWith("\n")?" ":""),j(),B(),U()}function U(){t&&(s&&(s.scrollTop=t.scrollTop,s.scrollLeft=t.scrollLeft),o&&(o.scrollTop=t.scrollTop))}function R(e,n=!0){if(t&&(t.value=e||"",S(),n)){const t=(e||"").length;k.record(e||"",{start:t,end:t},0)}}function j(){if(!t||!o)return;const e=t.value.split("\n"),n=Math.max(1,e.length);let s="";for(let e=1;e<=n;e++){const t=null!=I&&e===I;s+='<div class="sql-gutter-line'+(t?" has-error":"")+'" data-line="'+e+'" title="'+(t?"Error on line "+e:"Line "+e)+'">'+e+"</div>"}o.innerHTML=s,o.scrollTop=t.scrollTop}function B(){if(!t||!r)return;const e=t.value,n=t.selectionStart||0,s=t.selectionEnd||0,o=e.slice(0,n).split("\n"),a=o.length,l=o[o.length-1].length+1;if(r.textContent="Ln "+a+", Col "+l,i){const t=e.length,o=e.split("\n").length,r=Math.abs(s-n);i.textContent=r>0?t+" chars ("+r+" selected) · "+o+" lines":t+" chars · "+o+" lines"}}function M(e,n){if(!t)return;const s=t.value.split("\n"),o=Math.max(1,Math.min(s.length,e||1));let r=0;for(let e=0;e<o-1;e++)r+=s[e].length+1;n&&n>1&&(r+=Math.min(s[o-1].length,n-1)),t.focus(),t.setSelectionRange(r,r);t.scrollTop=Math.max(0,21*(o-3)),U(),B()}function O(e,t){if(!u)return;if(!e)return void(u.innerHTML="");const n={error:"border-error/40 bg-error/10 text-error",success:"border-success/40 bg-success/10 text-success",info:"border-info/40 bg-info/10 text-info",warning:"border-warning/40 bg-warning/10 text-warning"};u.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border px-4 py-3 text-sm font-medium '+(n[t]||n.info)+'"><span>'+L(e)+"</span></div>"}function N(e){v&&(v.disabled=!e),b&&(b.disabled=!e)}async function A(t){const n=q().trim();if(n)try{const s=await fetch(Api.url("/api/query/"+e+"/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:n,format:t})});if(!s.ok){let e="Export failed ("+s.status+").";try{const t=await s.json();t&&t.error&&(e=t.error)}catch(e){}throw new Error(e)}const o=await s.blob(),r=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(o,"query-"+r+"."+t),window.UI&&UI.showToast&&UI.showToast("Exported as "+t.toUpperCase()+".","success")}catch(e){O(e.message,"error")}}l&&l.addEventListener("click",function(){const e=q();e.trim()&&(R(function(e){if(!e)return"";let t=e.trim().replace(/\r\n/g,"\n").replace(/\t/g," ");return["SELECT","FROM","WHERE","AND","OR","GROUP BY","HAVING","ORDER BY","LIMIT","OFFSET","LEFT JOIN","RIGHT JOIN","INNER JOIN","CROSS JOIN","FULL JOIN","JOIN","UNION ALL","UNION","INSERT INTO","VALUES","UPDATE","SET","DELETE FROM","CREATE TABLE","ALTER TABLE","DROP TABLE"].forEach(function(e){const n=new RegExp("\\b"+e.replace(" ","\\s+")+"\\b","gi");t=t.replace(n,"\n"+e.toUpperCase())}),t.split("\n").map(function(e){return e.trim()}).filter(function(e){return e.length>0}).map(function(e){const t=e.toUpperCase();return t.startsWith("WHERE")||t.startsWith("AND")||t.startsWith("OR")||t.startsWith("JOIN")||t.startsWith("LEFT")||t.startsWith("RIGHT")||t.startsWith("INNER")||t.startsWith("ON")||t.startsWith("SET")||t.startsWith("VALUES")||t.startsWith("HAVING")||t.startsWith("ORDER")||t.startsWith("GROUP")||t.startsWith("LIMIT")||t.startsWith("OFFSET")?" "+e:e}).join("\n")}(e)),t.focus(),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),c&&c.addEventListener("click",function(){const e=q();e.trim()&&navigator.clipboard.writeText(e).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),d&&d.addEventListener("click",function(){R(""),I=null,O("","info"),t.focus()}),document.querySelectorAll(".js-snippet").forEach(function(e){e.addEventListener("click",function(n){n.preventDefault();const s=e.getAttribute("data-snippet");s&&(R(s.replace(/\\n/g,"\n")),I=null,O("","info"),t.focus())})}),o&&o.addEventListener("click",function(e){const t=e.target.closest(".sql-gutter-line");if(!t)return;const n=parseInt(t.dataset.line,10);n&&M(n,1)}),t&&(t.addEventListener("input",function(){I=null,S(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},250)}),t.addEventListener("scroll",U),t.addEventListener("click",B),t.addEventListener("keyup",B),t.addEventListener("select",B),t.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()&&!e.shiftKey)return e.preventDefault(),void function(){const e=k.undo();e&&(t.value=e.text,S(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),B())}();if((e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()||(e.ctrlKey||e.metaKey)&&e.shiftKey&&"z"===e.key.toLowerCase())return e.preventDefault(),void function(){const e=k.redo();e&&(t.value=e.text,S(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),B())}();if((e.ctrlKey||e.metaKey)&&"Enter"===e.key)return e.preventDefault(),void K();if("Enter"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=(t.value.slice(0,n).split("\n").pop()||"").match(/^(\s+)/),r="\n"+(o?o[1]:"");return t.setRangeText(r,n,s,"end"),S(),void k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0)}if((e.ctrlKey||e.metaKey)&&"/"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=t.value,r=o.lastIndexOf("\n",n-1)+1;let i=o.indexOf("\n",s);-1===i&&(i=o.length);const a=o.slice(r,i).split("\n"),l=a.every(function(e){return e.trim().startsWith("--")}),c=a.map(function(e){return l?e.replace(/^(\s*)--\s?/,"$1"):e?"-- "+e:e}).join("\n");return t.setRangeText(c,r,i,"preserve"),t.setSelectionRange(r,r+c.length),S(),void k.record(t.value,{start:r,end:r+c.length},0)}if("Tab"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,o=t.value;if(n!==s||e.shiftKey){const r=o.lastIndexOf("\n",n-1)+1;let i=o.indexOf("\n",s);-1===i&&(i=o.length);const a=o.slice(r,i).split("\n").map(function(t){return e.shiftKey?t.replace(/^( |\t| )/,""):" "+t}).join("\n");t.setRangeText(a,r,i,"preserve"),t.setSelectionRange(r,r+a.length),S(),k.record(t.value,{start:r,end:r+a.length},0)}else t.setRangeText(" ",n,s,"end"),S(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0);return}})),v&&v.addEventListener("click",()=>A("csv")),b&&b.addEventListener("click",()=>A("json"));const C=window.Utils?window.Utils.isJson:function(e){return!1},H=window.Utils?window.Utils.isHexBlob:function(e){return!1};if(f){let e=null;f.addEventListener("click",function(e){const t=e.target.closest(".js-query-inspect"),n=e.target.closest(".js-query-cell");if(!t&&(!n||e.detail<2&&!e.altKey))return;const s=t?t.closest(".js-query-cell"):n;if(!s)return;const o=s.dataset.col||"Result",r=s.dataset.value||"",i="true"===s.dataset.blob,a="true"===s.dataset.json;window.Inspector&&window.Inspector.open&&window.Inspector.open({col:o,value:r,isNull:!1,isBlob:i,isJson:a,readonly:!0})}),f.addEventListener("mouseover",function(t){const n=t.target.closest(".js-query-cell");n&&(clearTimeout(e),e=setTimeout(function(){const e=n.dataset.value||"";if(!e||e.length<30)return;const t=n.dataset.col||"Result",s="true"===n.dataset.blob,o="true"===n.dataset.json;window.Inspector&&window.Inspector.peek&&window.Inspector.peek(n,{col:t,value:e,isBlob:s,isJson:o,readonly:!0})},450))}),f.addEventListener("mouseout",function(t){clearTimeout(e)})}function D(e,t){m.innerHTML="",f.innerHTML='<tr><td class="px-4 py-8 text-center"><p class="text-2xl font-extrabold text-primary">'+L(e)+'</p><p class="mt-1 text-sm text-base-content/45">'+L(t)+"</p></td></tr>"}function W(e){g&&(g.textContent=e)}async function K(){const t=q().trim();if(!t)return void O("Enter a SQL statement first.","warning");if(function(e){var t=e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim();return!!/\b(drop\s+(table|view|index|trigger)|truncate\b)/i.test(t)||!(!/\bdelete\s+from\b/i.test(t)||/\bwhere\b/i.test(t))}(t)){if(!await UI.confirm({title:"Execute Destructive SQL",message:"This SQL statement contains potentially destructive operations (e.g. DROP, TRUNCATE, or unrestricted DELETE) that may permanently erase database schemas or data.",detailsHtml:'<pre class="font-mono text-xs text-base-content/85 whitespace-pre-wrap break-all">'+L(t.length>350?t.slice(0,350)+"…":t)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}I=null,j(),u.innerHTML="",p.classList.add("hidden"),N(!1);const n=a?a.innerHTML:"";a&&(a.disabled=!0,a.innerHTML='<span class="loading loading-spinner loading-xs"></span> Running…');const s=performance.now();try{const n=await Api.post("/api/query/"+e,{sql:t}),i=Math.max(1,Math.round(performance.now()-s));p.classList.remove("hidden"),p.classList.remove("js-result-slide"),p.offsetWidth,p.classList.add("js-result-slide"),"count"===n.kind?(D(String(n.count),"count"),W("Count query · "+i+"ms")):"select"===n.kind?(o=n.columns,r=n.rows,m.innerHTML="<tr>"+o.map(e=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+L(e)+"</span></th>").join("")+"</tr>",r.length?f.innerHTML=r.map(e=>'<tr class="hover:bg-primary/[0.04]">'+o.map(t=>{const n=e[t];if(null==n)return'<td class="align-middle"><span class="chip chip-null">NULL</span></td>';const s=String(n),o=C(s),r=H(s),i=s.length>140?s.slice(0,140)+"…":s;return o?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-json="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':r?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-blob="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'"><div class="flex items-center justify-between gap-1 group/cell"><span class="truncate font-mono text-[13px]" title="'+L(s)+'">'+L(i)+"</span>"+(s.length>25?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-query-inspect shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></td>"}).join("")+"</tr>").join(""):f.innerHTML='<tr><td colspan="'+Math.max(1,o.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',W(n.rows.length+" row(s) · "+i+"ms"),N(!0)):(D(n.message||"Statement executed.","write"),W(null!=n.changes?n.changes+" row(s) affected · "+i+"ms":i+"ms")),setTimeout(function(){p.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}catch(e){p.classList.add("hidden"),function(e){if(!u)return;const t=e&&e.details,n=e&&e.message||t&&t.message||"Query failed.";if(!t||!t.snippet&&!t.cause&&!t.suggestion)return void O(n,"error");I=t.line||null,j();let s='<div class="sql-error-card space-y-3">';s+='<div class="flex flex-wrap items-center justify-between gap-2 border-b border-error/20 pb-2.5">',s+='<div class="flex items-center gap-2">',s+='<span class="grid h-6 w-6 place-items-center rounded-lg bg-error/15 text-error font-bold">✕</span>',s+='<span class="font-bold text-sm text-error">'+L(n)+"</span>",s+="</div>",t.line&&(s+='<div class="flex items-center gap-1.5">',s+='<span class="badge badge-sm badge-error font-mono font-bold">Line '+t.line+(t.column?", Col "+t.column:"")+"</span>",s+='<button type="button" class="btn btn-xs btn-outline btn-error js-jump-err-line" data-line="'+t.line+'" data-col="'+(t.column||1)+'">Jump to line</button>',s+="</div>"),s+="</div>",t.snippet&&(s+="<div>",s+='<p class="text-[11px] font-bold uppercase tracking-wider text-base-content/45 mb-1.5">Code Snippet</p>',s+='<pre class="sql-error-snippet">'+L(t.snippet)+"</pre>",s+="</div>"),(t.cause||t.suggestion)&&(s+='<div class="grid grid-cols-1 md:grid-cols-2 gap-2.5 pt-1">',t.cause&&(s+='<div class="rounded-lg border border-base-300 bg-base-100/90 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-base-content/80 mb-1">',s+="<span>💡</span><span>Possible Cause</span>",s+="</div>",s+='<p class="text-xs text-base-content/70 leading-relaxed">'+L(t.cause)+"</p>",s+="</div>"),t.suggestion&&(s+='<div class="rounded-lg border border-primary/30 bg-primary/5 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-primary mb-1">',s+="<span>🔧</span><span>Suggested Fix</span>",s+="</div>",s+='<p class="text-xs text-base-content/80 font-medium leading-relaxed">'+L(t.suggestion)+"</p>",s+="</div>"),s+="</div>"),s+="</div>",u.innerHTML=s;const o=u.querySelector(".js-jump-err-line");o&&o.addEventListener("click",function(){M(parseInt(o.dataset.line,10),parseInt(o.dataset.col,10))})}(e),setTimeout(function(){u.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}finally{a&&(a.disabled=!1,a.innerHTML=n)}var o,r}async function J(t){try{E=await Api.get("/api/queries/"+e)}catch(e){E=[]}y.innerHTML='<option value="">— select a saved query —</option>'+E.map(e=>'<option value="'+e.id+'"'+(t&&e.name===t?" selected":"")+">"+L(e.name)+"</option>").join(""),x.disabled=!0}async function P(){const t=q().trim(),n=w.value.trim();if(t)if(n)try{await Api.post("/api/queries/"+e,{name:n,sql:t}),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),await J(n)}catch(e){O(e.message,"error")}else O("Give the query a name.","warning");else O("Write some SQL before saving.","warning")}a&&a.addEventListener("click",K),h.forEach(e=>e.addEventListener("click",P)),y&&y.addEventListener("change",()=>{const e=y.value;if(x.disabled=!e,!e)return;const n=E.find(t=>String(t.id)===String(e));n&&(R(n.sql),w.value=n.name,I=null,O('Loaded "'+n.name+'".',"info"),t.focus())}),x&&x.addEventListener("click",async function(){const t=y.value;if(!t)return;const n=E.find(e=>String(e.id)===String(t)),s=n?n.name:"Saved query";if(await UI.confirm({title:"Delete Saved Query",message:'Are you sure you want to delete the saved query "'+s+'"?',item:s,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{await Api.del("/api/queries/"+e+"/"+t),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),await J()}catch(e){O(e.message,"error")}});const Q=q();Q?R(Q,!0):k.record("",{start:0,end:0},0),S()}();
|
|
1
|
+
!function(){"use strict";const e=encodeURIComponent(window.APP&&window.APP.dbId||""),t=document.getElementById("query-sql"),n=document.getElementById("query-code"),s=document.getElementById("query-highlight"),r=document.getElementById("query-gutter"),o=document.getElementById("editor-cursor-pos"),i=document.getElementById("editor-stats"),a=document.getElementById("run-query"),l=document.getElementById("format-query"),c=document.getElementById("copy-query"),d=document.getElementById("clear-query"),u=document.getElementById("query-message"),p=document.getElementById("query-result"),m=document.getElementById("query-result-head"),f=document.getElementById("query-result-body"),g=document.getElementById("result-summary"),h=Array.prototype.slice.call(document.querySelectorAll("#save-query, #save-query-inline")),y=document.getElementById("save-query-name"),v=document.getElementById("saved-query-select"),w=document.getElementById("delete-query"),x=document.getElementById("export-csv"),b=document.getElementById("export-json");let E=[],I=null;const L=window.Utils?window.Utils.escapeHtml:function(e){return String(null==e?"":e)},T=window.Utils?window.Utils.highlightSql:function(e){return L(e)};const k=new class{constructor(e=100){this.stack=[],this.index=-1,this.limit=e,this.lastRecordTime=0}record(e,t,n=250){const s=Date.now(),r=this.index>=0?this.stack[this.index]:null;if(r&&r.text===e)r.selection=t;else{if(r&&s-this.lastRecordTime<n&&1===Math.abs(e.length-r.text.length)&&!/\s|[;(),]/.test(e.slice(-1)))return r.text=e,r.selection=t,void(this.lastRecordTime=s);this.stack=this.stack.slice(0,this.index+1),this.stack.push({text:e,selection:t}),this.stack.length>this.limit?this.stack.shift():this.index++,this.lastRecordTime=s}}undo(){return this.index>0?(this.index--,this.stack[this.index]):null}redo(){return this.index<this.stack.length-1?(this.index++,this.stack[this.index]):null}}(100);function S(){return t?t.value:""}function q(){if(!t||!n)return;const e=t.value;n.innerHTML=T(e)+(e.endsWith("\n")?" ":""),j(),O(),U()}function U(){t&&(s&&(s.scrollTop=t.scrollTop,s.scrollLeft=t.scrollLeft),r&&(r.scrollTop=t.scrollTop))}function R(e,n=!0){if(t&&(t.value=e||"",q(),n)){const t=(e||"").length;k.record(e||"",{start:t,end:t},0)}}function j(){if(!t||!r)return;const e=t.value.split("\n"),n=Math.max(1,e.length);let s="";for(let e=1;e<=n;e++){const t=null!=I&&e===I;s+='<div class="sql-gutter-line'+(t?" has-error":"")+'" data-line="'+e+'" title="'+(t?"Error on line "+e:"Line "+e)+'">'+e+"</div>"}r.innerHTML=s,r.scrollTop=t.scrollTop}function O(){if(!t||!o)return;const e=t.value,n=t.selectionStart||0,s=t.selectionEnd||0,r=e.slice(0,n).split("\n"),a=r.length,l=r[r.length-1].length+1;if(o.textContent="Ln "+a+", Col "+l,i){const t=e.length,r=e.split("\n").length,o=Math.abs(s-n);i.textContent=o>0?t+" chars ("+o+" selected) · "+r+" lines":t+" chars · "+r+" lines"}}function B(e,n){if(!t)return;const s=t.value.split("\n"),r=Math.max(1,Math.min(s.length,e||1));let o=0;for(let e=0;e<r-1;e++)o+=s[e].length+1;n&&n>1&&(o+=Math.min(s[r-1].length,n-1)),t.focus(),t.setSelectionRange(o,o);t.scrollTop=Math.max(0,21*(r-3)),U(),O()}function M(e,t){if(!u)return;if(!e)return void(u.innerHTML="");const n={error:"border-error/40 bg-error/10 text-error",success:"border-success/40 bg-success/10 text-success",info:"border-info/40 bg-info/10 text-info",warning:"border-warning/40 bg-warning/10 text-warning"};u.innerHTML='<div class="flex items-start gap-2.5 rounded-xl border px-4 py-3 text-sm font-medium '+(n[t]||n.info)+'"><span>'+L(e)+"</span></div>"}function N(e){x&&(x.disabled=!e),b&&(b.disabled=!e)}async function C(t){const n=S().trim();if(n)try{const s=await fetch(Api.url("/api/query/"+e+"/export"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sql:n,format:t})});if(!s.ok){let e="Export failed ("+s.status+").";try{const t=await s.json();t&&t.error&&(e=t.error)}catch(e){}throw new Error(e)}const r=await s.blob(),o=(new Date).toISOString().replace(/[:.]/g,"-");window.Utils&&window.Utils.downloadBlob&&window.Utils.downloadBlob(r,"query-"+o+"."+t),window.UI&&UI.showToast&&UI.showToast("Exported as "+t.toUpperCase()+".","success")}catch(e){M(e.message,"error")}}l&&l.addEventListener("click",function(){const e=S();e.trim()&&(R(function(e){if(!e)return"";let t=e.trim().replace(/\r\n/g,"\n").replace(/\t/g," ");return["SELECT","FROM","WHERE","AND","OR","GROUP BY","HAVING","ORDER BY","LIMIT","OFFSET","LEFT JOIN","RIGHT JOIN","INNER JOIN","CROSS JOIN","FULL JOIN","JOIN","UNION ALL","UNION","INSERT INTO","VALUES","UPDATE","SET","DELETE FROM","CREATE TABLE","ALTER TABLE","DROP TABLE"].forEach(function(e){const n=new RegExp("\\b"+e.replace(" ","\\s+")+"\\b","gi");t=t.replace(n,"\n"+e.toUpperCase())}),t.split("\n").map(function(e){return e.trim()}).filter(function(e){return e.length>0}).map(function(e){const t=e.toUpperCase();return t.startsWith("WHERE")||t.startsWith("AND")||t.startsWith("OR")||t.startsWith("JOIN")||t.startsWith("LEFT")||t.startsWith("RIGHT")||t.startsWith("INNER")||t.startsWith("ON")||t.startsWith("SET")||t.startsWith("VALUES")||t.startsWith("HAVING")||t.startsWith("ORDER")||t.startsWith("GROUP")||t.startsWith("LIMIT")||t.startsWith("OFFSET")?" "+e:e}).join("\n")}(e)),t.focus(),window.UI&&UI.showToast&&UI.showToast("SQL formatted.","info"))}),c&&c.addEventListener("click",function(){const e=S();e.trim()&&navigator.clipboard.writeText(e).then(function(){window.UI&&UI.showToast&&UI.showToast("SQL copied to clipboard.","success")})}),d&&d.addEventListener("click",function(){R(""),I=null,M("","info"),t.focus()}),document.querySelectorAll(".js-snippet").forEach(function(e){e.addEventListener("click",function(n){n.preventDefault();const s=e.getAttribute("data-snippet");s&&(R(s.replace(/\\n/g,"\n")),I=null,M("","info"),t.focus())})}),r&&r.addEventListener("click",function(e){const t=e.target.closest(".sql-gutter-line");if(!t)return;const n=parseInt(t.dataset.line,10);n&&B(n,1)}),t&&(t.addEventListener("input",function(){I=null,q(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},250)}),t.addEventListener("scroll",U),t.addEventListener("click",O),t.addEventListener("keyup",O),t.addEventListener("select",O),t.addEventListener("keydown",function(e){if((e.ctrlKey||e.metaKey)&&"z"===e.key.toLowerCase()&&!e.shiftKey)return e.preventDefault(),void function(){const e=k.undo();e&&(t.value=e.text,q(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),O())}();if((e.ctrlKey||e.metaKey)&&"y"===e.key.toLowerCase()||(e.ctrlKey||e.metaKey)&&e.shiftKey&&"z"===e.key.toLowerCase())return e.preventDefault(),void function(){const e=k.redo();e&&(t.value=e.text,q(),e.selection&&t.setSelectionRange(e.selection.start,e.selection.end),t.focus(),I=null,j(),O())}();if((e.ctrlKey||e.metaKey)&&"Enter"===e.key)return e.preventDefault(),void J();if("Enter"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=(t.value.slice(0,n).split("\n").pop()||"").match(/^(\s+)/),o="\n"+(r?r[1]:"");return t.setRangeText(o,n,s,"end"),q(),void k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0)}if((e.ctrlKey||e.metaKey)&&"/"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=t.value,o=r.lastIndexOf("\n",n-1)+1;let i=r.indexOf("\n",s);-1===i&&(i=r.length);const a=r.slice(o,i).split("\n"),l=a.every(function(e){return e.trim().startsWith("--")}),c=a.map(function(e){return l?e.replace(/^(\s*)--\s?/,"$1"):e?"-- "+e:e}).join("\n");return t.setRangeText(c,o,i,"preserve"),t.setSelectionRange(o,o+c.length),q(),void k.record(t.value,{start:o,end:o+c.length},0)}if("Tab"===e.key){e.preventDefault();const n=t.selectionStart,s=t.selectionEnd,r=t.value;if(n!==s||e.shiftKey){const o=r.lastIndexOf("\n",n-1)+1;let i=r.indexOf("\n",s);-1===i&&(i=r.length);const a=r.slice(o,i).split("\n").map(function(t){return e.shiftKey?t.replace(/^( |\t| )/,""):" "+t}).join("\n");t.setRangeText(a,o,i,"preserve"),t.setSelectionRange(o,o+a.length),q(),k.record(t.value,{start:o,end:o+a.length},0)}else t.setRangeText(" ",n,s,"end"),q(),k.record(t.value,{start:t.selectionStart,end:t.selectionEnd},0);return}})),x&&x.addEventListener("click",()=>C("csv")),b&&b.addEventListener("click",()=>C("json"));const A=window.Utils?window.Utils.isJson:function(e){return!1},H=window.Utils?window.Utils.isHexBlob:function(e){return!1};if(f){let e=null;f.addEventListener("click",function(e){const t=e.target.closest(".js-query-inspect"),n=e.target.closest(".js-query-cell");if(!t&&(!n||e.detail<2&&!e.altKey))return;const s=t?t.closest(".js-query-cell"):n;if(!s)return;const r=s.dataset.col||"Result",o=s.dataset.value||"",i="true"===s.dataset.blob,a="true"===s.dataset.json;window.Inspector&&window.Inspector.open&&window.Inspector.open({col:r,value:o,isNull:!1,isBlob:i,isJson:a,readonly:!0})}),f.addEventListener("mouseover",function(t){const n=t.target.closest(".js-query-cell");n&&(clearTimeout(e),e=setTimeout(function(){const e=n.dataset.value||"";if(!e||e.length<30)return;const t=n.dataset.col||"Result",s="true"===n.dataset.blob,r="true"===n.dataset.json;window.Inspector&&window.Inspector.peek&&window.Inspector.peek(n,{col:t,value:e,isBlob:s,isJson:r,readonly:!0})},450))}),f.addEventListener("mouseout",function(t){clearTimeout(e)})}function D(e,t){m.innerHTML="",f.innerHTML='<tr><td class="px-4 py-8 text-center"><p class="text-2xl font-extrabold text-primary">'+L(e)+'</p><p class="mt-1 text-sm text-base-content/45">'+L(t)+"</p></td></tr>"}function W(e){g&&(g.textContent=e)}async function J(){const t=S().trim();if(!t)return void M("Enter a SQL statement first.","warning");if(function(e){var t=e.replace(/--.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").trim();return!!/\b(drop\s+(table|view|index|trigger)|truncate\b)/i.test(t)||!(!/\bdelete\s+from\b/i.test(t)||/\bwhere\b/i.test(t))}(t)){if(!await UI.confirm({title:"Execute Destructive SQL",message:"This SQL statement contains potentially destructive operations (e.g. DROP, TRUNCATE, or unrestricted DELETE) that may permanently erase database schemas or data.",detailsHtml:'<pre class="font-mono text-xs text-base-content/85 whitespace-pre-wrap break-all">'+L(t.length>350?t.slice(0,350)+"…":t)+"</pre>",confirmText:"Execute anyway",danger:!0}))return}I=null,j(),u.innerHTML="",p.classList.add("hidden"),N(!1);const n=a?a.innerHTML:"";a&&(a.disabled=!0,a.innerHTML='<span class="loading loading-spinner loading-xs"></span> Running…');const s=performance.now();try{const n=await Api.post("/api/query/"+e,{sql:t}),i=Math.max(1,Math.round(performance.now()-s));p.classList.remove("hidden"),p.classList.remove("js-result-slide"),p.offsetWidth,p.classList.add("js-result-slide"),"count"===n.kind?(D(String(n.count),"count"),W("Count query · "+i+"ms")):"select"===n.kind?(r=n.columns,o=n.rows,m.innerHTML="<tr>"+r.map(e=>'<th class="whitespace-nowrap font-semibold"><span class="font-mono normal-case">'+L(e)+"</span></th>").join("")+"</tr>",o.length?f.innerHTML=o.map(e=>'<tr class="hover:bg-primary/[0.04]">'+r.map(t=>{const n=e[t];if(null==n)return'<td class="align-middle"><span class="chip chip-null">NULL</span></td>';const s=String(n),r=A(s),o=H(s),i=s.length>140?s.slice(0,140)+"…":s;return r?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-json="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-info/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-info">{ } JSON</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect JSON"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':o?'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'" data-blob="true"><div class="flex items-center gap-1.5"><span class="inline-flex items-center gap-1 rounded-md bg-secondary/10 px-1.5 py-0.5 text-[11px] font-mono font-medium text-secondary">🗃️ BLOB</span><span class="block max-w-[120px] truncate font-mono text-[12px] text-base-content/70">'+L(i)+'</span><button type="button" class="btn btn-ghost btn-xs btn-circle js-query-inspect" title="Inspect BLOB"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button></div></td>':'<td class="max-w-xs align-middle js-query-cell" data-col="'+L(t)+'" data-value="'+L(s)+'"><div class="flex items-center justify-between gap-1 group/cell"><span class="truncate font-mono text-[13px]" title="'+L(s)+'">'+L(i)+"</span>"+(s.length>25?'<button type="button" class="btn btn-ghost btn-xs btn-circle opacity-0 group-hover/cell:opacity-100 transition-opacity js-query-inspect shrink-0" title="Inspect text"><svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg></button>':"")+"</div></td>"}).join("")+"</tr>").join(""):f.innerHTML='<tr><td colspan="'+Math.max(1,r.length)+'" class="py-10 text-center text-base-content/45">No rows returned.</td></tr>',W(n.rows.length+" row(s) · "+i+"ms"),N(!0)):(D(n.message||"Statement executed.","write"),W(null!=n.changes?n.changes+" row(s) affected · "+i+"ms":i+"ms")),setTimeout(function(){p.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}catch(e){p.classList.add("hidden"),function(e){if(!u)return;const t=e&&e.details,n=e&&e.message||t&&t.message||"Query failed.";if(!t||!t.snippet&&!t.cause&&!t.suggestion)return void M(n,"error");I=t.line||null,j();let s='<div class="sql-error-card space-y-3">';s+='<div class="flex flex-wrap items-center justify-between gap-2 border-b border-error/20 pb-2.5">',s+='<div class="flex items-center gap-2">',s+='<span class="grid h-6 w-6 place-items-center rounded-lg bg-error/15 text-error font-bold">✕</span>',s+='<span class="font-bold text-sm text-error">'+L(n)+"</span>",s+="</div>",t.line&&(s+='<div class="flex items-center gap-1.5">',s+='<span class="badge badge-sm badge-error font-mono font-bold">Line '+t.line+(t.column?", Col "+t.column:"")+"</span>",s+='<button type="button" class="btn btn-xs btn-outline btn-error js-jump-err-line" data-line="'+t.line+'" data-col="'+(t.column||1)+'">Jump to line</button>',s+="</div>"),s+="</div>",t.snippet&&(s+="<div>",s+='<p class="text-[11px] font-bold uppercase tracking-wider text-base-content/45 mb-1.5">Code Snippet</p>',s+='<pre class="sql-error-snippet">'+L(t.snippet)+"</pre>",s+="</div>"),(t.cause||t.suggestion)&&(s+='<div class="grid grid-cols-1 md:grid-cols-2 gap-2.5 pt-1">',t.cause&&(s+='<div class="rounded-lg border border-base-300 bg-base-100/90 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-base-content/80 mb-1">',s+="<span>💡</span><span>Possible Cause</span>",s+="</div>",s+='<p class="text-xs text-base-content/70 leading-relaxed">'+L(t.cause)+"</p>",s+="</div>"),t.suggestion&&(s+='<div class="rounded-lg border border-primary/30 bg-primary/5 p-3 shadow-xs">',s+='<div class="flex items-center gap-1.5 text-xs font-bold text-primary mb-1">',s+="<span>🔧</span><span>Suggested Fix</span>",s+="</div>",s+='<p class="text-xs text-base-content/80 font-medium leading-relaxed">'+L(t.suggestion)+"</p>",s+="</div>"),s+="</div>"),s+="</div>",u.innerHTML=s;const r=u.querySelector(".js-jump-err-line");r&&r.addEventListener("click",function(){B(parseInt(r.dataset.line,10),parseInt(r.dataset.col,10))})}(e),setTimeout(function(){u.scrollIntoView({behavior:"smooth",block:"nearest"})},50)}finally{a&&(a.disabled=!1,a.innerHTML=n)}var r,o}function K(){return"admindb_saved_queries_"+(decodeURIComponent(e)||"default")}function F(){try{const e=localStorage.getItem(K());if(e){const t=JSON.parse(e);if(Array.isArray(t))return t}}catch(e){console.warn("Failed to read saved queries from localStorage:",e)}return[]}function Q(e){try{localStorage.setItem(K(),JSON.stringify(e))}catch(e){console.warn("Failed to save queries to localStorage:",e)}}function P(e){E=F(),E.sort(function(e,t){return(e.name||"").localeCompare(t.name||"",void 0,{sensitivity:"base"})}),v&&(v.innerHTML='<option value="">— select a saved query —</option>'+E.map(function(t){return'<option value="'+L(t.id)+'"'+(e&&t.name===e?" selected":"")+">"+L(t.name)+"</option>"}).join("")),w&&(w.disabled=!v||!v.value)}function z(){const e=S().trim(),t=y?y.value.trim():"";if(e)if(t)try{const n=F(),s=n.findIndex(function(e){return(e.name||"").trim().toLowerCase()===t.toLowerCase()}),r=(new Date).toISOString();if(s>=0)n[s]={id:n[s].id,name:t,sql:e,updated_at:r};else{const s="sq_"+Date.now()+"_"+Math.random().toString(36).substring(2,7);n.push({id:s,name:t,sql:e,created_at:r})}Q(n),window.UI&&UI.showToast&&UI.showToast("Query saved.","success"),M('Query "'+t+'" saved to browser storage.',"success"),P(t)}catch(e){M("Failed to save query: "+e.message,"error")}else M("Give the query a name.","warning");else M("Write some SQL before saving.","warning")}a&&a.addEventListener("click",J),h.forEach(e=>e.addEventListener("click",z)),v&&v.addEventListener("change",()=>{const e=v.value;if(w.disabled=!e,!e)return;const n=E.find(t=>String(t.id)===String(e));n&&(R(n.sql),y&&(y.value=n.name),I=null,M('Loaded "'+n.name+'".',"info"),t.focus())}),w&&w.addEventListener("click",async function(){if(!v)return;const e=v.value;if(!e)return;const t=E.find(function(t){return String(t.id)===String(e)}),n=t?t.name:"Saved query";if(await UI.confirm({title:"Delete Saved Query",message:'Are you sure you want to delete the saved query "'+n+'" from browser storage?',item:n,itemType:"Saved Query",confirmText:"Delete query",danger:!0}))try{Q(F().filter(function(t){return String(t.id)!==String(e)})),window.UI&&UI.showToast&&UI.showToast("Query deleted.","success"),M("Query deleted.","info"),P(),y&&y.value===n&&(y.value="")}catch(e){M("Failed to delete query: "+e.message,"error")}});const G=S();G?R(G,!0):k.record("",{start:0,end:0},0),q(),P()}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";var e=window.APP&&window.APP.dbId||null,t=window.APP&&window.APP.basePath||"",n=null,r=!1,a="all",o=0,s=null,l=[],i=!1,c=[{id:"all",label:"All"},{id:"table",label:"Tables"},{id:"column",label:"Columns"},{id:"index",label:"Indexes"},{id:"relation",label:"Relations"},{id:"command",label:"Commands"},{id:"row",label:"Row Data"}],d={search:'<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="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>',table:'<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="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>',columns:'<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="M9 4.5v15m6-15v15m-10.875 0h15.75c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125H4.125C3.504 4.5 3 5.004 3 5.625v12.75c0 .621.504 1.125 1.125 1.125z" /></svg>',key:'<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="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z" /></svg>',diagram:'<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"><rect x="2" y="3" width="6" height="5" rx="1"/><rect x="16" y="3" width="6" height="5" rx="1"/><rect x="9" y="16" width="6" height="5" rx="1"/><path stroke-linecap="round" stroke-linejoin="round" d="M5 8v4h14V8M12 12v4"/></svg>',query:'<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="M13 10V3L4 14h7v7l9-11h-7z" /></svg>',seed:'<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 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /></svg>',diff:'<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="M8 7h8M8 12h8m-8 5h4M4 4v16a2 2 0 002 2h12a2 2 0 002-2V4a2 2 0 00-2-2H6a2 2 0 00-2 2zM12 3v4m-3-2h6"/></svg>',settings:'<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.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>',export:'<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>',plus:'<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="M12 4v16m8-8H4" /></svg>',home:'<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="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>',inspect:'<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="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" 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>',sparkles:'<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 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /></svg>',spinner:'<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 animate-spin text-primary" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>'};function u(e){return String(null==e?"":e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var p=null,m=null,b=null,h=null,v=null,g=null;function f(e){(a=e,v)&&v.querySelectorAll(".search-tab-btn").forEach(function(t){var n=t.getAttribute("data-category")===e;t.className="search-tab-btn rounded-lg px-2.5 py-1 font-medium transition-colors "+(n?"bg-primary text-primary-content font-semibold shadow-xs":"text-base-content/60 hover:text-base-content hover:bg-base-200")});y(m?m.value:"")}function w(t){!function(){if(!p){(p=document.createElement("div")).id="global-search-modal",p.className="fixed inset-0 z-[200] flex items-start justify-center bg-black/60 backdrop-blur-xs p-4 sm:p-6 md:p-12 opacity-0 pointer-events-none transition-opacity duration-150",p.setAttribute("role","dialog"),p.setAttribute("aria-modal","true");var e=document.createElement("div");e.className="app-card w-full max-w-2xl overflow-hidden shadow-2xl border border-base-300 bg-base-100 flex flex-col max-h-[85vh] transform scale-95 transition-transform duration-150 rounded-2xl";var t=document.createElement("div");t.className="relative flex items-center border-b border-base-200 px-4 py-3 bg-base-100/90",t.innerHTML='<span class="pointer-events-none text-base-content/40 mr-3 shrink-0">'+d.search+'</span><input id="global-search-input" type="search" placeholder="Search tables, columns, indexes, commands, data… (Ctrl+K)" autocomplete="off" spellcheck="false" class="w-full bg-transparent text-base sm:text-lg text-base-content placeholder:text-base-content/40 outline-none font-medium" /><div class="flex items-center gap-2 ml-2 shrink-0"> <span id="global-search-spinner" class="hidden shrink-0">'+d.spinner+'</span> <kbd class="hidden sm:inline-flex items-center rounded border border-base-300 bg-base-200/60 px-1.5 py-0.5 text-[10px] font-mono text-base-content/50">ESC</kbd></div>';var n=document.createElement("div");n.id="global-search-tabs",n.className="flex items-center gap-1.5 overflow-x-auto border-b border-base-200 px-4 py-2 bg-base-200/30 text-xs no-scrollbar",c.forEach(function(e){var t=document.createElement("button");t.type="button",t.className="search-tab-btn rounded-lg px-2.5 py-1 font-medium transition-colors "+("all"===e.id?"bg-primary text-primary-content font-semibold shadow-xs":"text-base-content/60 hover:text-base-content hover:bg-base-200"),t.setAttribute("data-category",e.id),t.textContent=e.label,t.addEventListener("click",function(){f(e.id)}),n.appendChild(t)}),(b=document.createElement("div")).id="global-search-results",b.className="flex-1 overflow-y-auto p-2 sm:p-3 space-y-1 divide-y divide-base-200/50 min-h-[160px] max-h-[480px]";var r=document.createElement("div");r.className="flex flex-wrap items-center justify-between gap-3 border-t border-base-200 bg-base-200/40 px-4 py-2.5 text-[11px] text-base-content/50",r.innerHTML='<div class="flex items-center gap-3"> <span class="flex items-center gap-1"><kbd class="rounded border border-base-300 bg-base-100 px-1 py-0.5 font-mono text-[10px] font-bold">↑</kbd><kbd class="rounded border border-base-300 bg-base-100 px-1 py-0.5 font-mono text-[10px] font-bold">↓</kbd> navigate</span> <span class="flex items-center gap-1"><kbd class="rounded border border-base-300 bg-base-100 px-1.5 py-0.5 font-mono text-[10px] font-bold">↵</kbd> select</span> <span class="flex items-center gap-1"><kbd class="rounded border border-base-300 bg-base-100 px-1 py-0.5 font-mono text-[10px] font-bold">Tab</kbd> category</span></div><div id="global-search-count" class="font-medium text-base-content/60">0 results</div>',e.appendChild(t),e.appendChild(n),e.appendChild(b),e.appendChild(r),p.appendChild(e),document.body.appendChild(p),m=t.querySelector("#global-search-input"),g=t.querySelector("#global-search-spinner"),h=r.querySelector("#global-search-count"),v=n,m.addEventListener("input",function(){y(m.value)}),m.addEventListener("keydown",L),p.addEventListener("click",function(e){e.target===p&&x()})}}(),e||(e=window.APP&&window.APP.dbId||""),p.classList.remove("pointer-events-none","opacity-0"),p.classList.add("opacity-100");var a=p.querySelector(".app-card");a&&(a.classList.remove("scale-95"),a.classList.add("scale-100")),i=!0,void 0!==t&&m&&(m.value=t),setTimeout(function(){m&&(m.focus(),m.select())},40),async function(){if(n||r||!e)return;r=!0;try{if(window.Api){var t=await window.Api.get("/api/search/"+encodeURIComponent(e)+"/metadata");t&&(n=t)}}catch(e){}finally{r=!1}}(),y(m?m.value:"")}function x(){if(p&&i){i=!1,p.classList.remove("opacity-100"),p.classList.add("opacity-0");var e=p.querySelector(".app-card");e&&(e.classList.remove("scale-100"),e.classList.add("scale-95")),setTimeout(function(){p&&p.classList.add("pointer-events-none")},150)}}function y(r){s&&clearTimeout(s);var o=function(r){var o=String(r||"").trim().toLowerCase();if(!n)return[];var s=[],l=encodeURIComponent(e||""),i=function(e){if(!e)return 0;var t=e.toLowerCase();return o?t===o?100:t.startsWith(o)?85:t.includes(o)?50:0:50};"all"!==a&&"table"!==a||(n.tables||[]).forEach(function(e){var n=i(e.name);if(n>0){var r=encodeURIComponent(e.name);s.push({id:"tbl-"+e.name,category:"table",title:e.name,subtitle:"Table · "+e.columnsCount+" columns"+(e.primaryKey.length?" · PK ("+e.primaryKey.join(", ")+")":""),icon:"table",url:t+"/tables/"+l+"/"+r,score:n+15,badge:"Table",badgeType:"primary",actions:[{label:"Browse",url:t+"/tables/"+l+"/"+r,primary:!0},{label:"Schema",url:t+"/schema/"+l+"/"+r},{label:"Query",url:t+"/query/"+l+"?sql="+encodeURIComponent("SELECT * FROM "+e.name+" LIMIT 100;")}]})}});"all"!==a&&"column"!==a||(n.columns||[]).forEach(function(e){var n=i(e.name),r=i(e.table+"."+e.name),a=Math.max(n,r);if(a>0){var o=encodeURIComponent(e.table);s.push({id:"col-"+e.table+"-"+e.name,category:"column",title:e.name,subtitle:e.table+" · "+e.type+(e.isPk?" · Primary Key":"")+(e.isFk?" · FK → "+e.fkTarget:""),icon:"columns",url:t+"/tables/"+l+"/"+o,score:a+10,badge:e.isPk?"PK":e.isFk?"FK":e.type,badgeType:e.isPk?"accent":e.isFk?"info":"neutral",actions:[{label:"Browse Table",url:t+"/tables/"+l+"/"+o,primary:!0},{label:"View Schema",url:t+"/schema/"+l+"/"+o}]})}});"all"!==a&&"index"!==a||(n.indexes||[]).forEach(function(e){var n=i(e.name),r=(e.columns||[]).some(function(e){return i(e)>60})?60:0,a=Math.max(n,r);if(a>0){var o=encodeURIComponent(e.table);s.push({id:"idx-"+e.table+"-"+e.name,category:"index",title:e.name,subtitle:e.table+" ("+(e.columns||[]).join(", ")+") · "+(e.unique?"UNIQUE ":"")+"Index",icon:"key",url:t+"/schema/"+l+"/"+o,score:a,badge:e.unique?"Unique Index":"Index",badgeType:e.unique?"warning":"neutral",actions:[{label:"View in Schema",url:t+"/schema/"+l+"/"+o,primary:!0}]})}});"all"!==a&&"relation"!==a||(n.foreignKeys||[]).forEach(function(e){var n=i(e.fromTable+"."+e.fromColumn),r=i(e.toTable+"."+(e.toColumn||"id")),a=Math.max(n,r);a>0&&s.push({id:"fk-"+e.fromTable+"-"+e.fromColumn,category:"relation",title:e.fromTable+"."+e.fromColumn+" → "+e.toTable+"."+(e.toColumn||"id"),subtitle:"Foreign Key · ON UPDATE "+e.onUpdate+", ON DELETE "+e.onDelete,icon:"diagram",url:t+"/erd/"+l,score:a,badge:"Relation",badgeType:"info",actions:[{label:"View ERD",url:t+"/erd/"+l,primary:!0}]})});"all"!==a&&"command"!==a||(n.commands||[]).forEach(function(e){var t=i(e.title),n=(e.keywords||[]).some(function(e){return i(e)>60})?70:0,r=Math.max(t,n);r>0&&s.push({id:e.id,category:"command",title:e.title,subtitle:e.description,icon:e.icon,url:e.url,score:r+5,badge:"Command",badgeType:"secondary",actions:[{label:"Execute",url:e.url,primary:!0}]})});return s.sort(function(e,t){return t.score-e.score}),s.slice(0,50)}(r);k(o,r),e&&(g&&g.classList.remove("hidden"),s=setTimeout(async function(){try{var t="?q="+encodeURIComponent(r||"");"all"!==a&&(t+="&type="+encodeURIComponent(a));var n=await window.Api.get("/api/search/"+encodeURIComponent(e)+t);n&&n.results&&k(n.results,r)}catch(e){}finally{g&&g.classList.add("hidden")}},180))}function k(e,t){if(l=e||[],o=0,b){if(b.innerHTML="",h&&(h.textContent=l.length+(1===l.length?" result":" results")),0===l.length){var n=document.createElement("div");return n.className="empty-state py-10 px-4 text-center",n.innerHTML='<div class="empty-icon mx-auto mb-2 text-base-content/30">'+d.search+'</div><p class="text-sm font-semibold text-base-content/70">No matching items found</p><p class="text-xs text-base-content/40 mt-1">Try searching for a different table, column name, or command.</p>',void b.appendChild(n)}l.forEach(function(e,n){var r=document.createElement("div");r.className="search-result-item group flex items-center justify-between gap-3 p-2.5 rounded-xl cursor-pointer transition-all duration-100 "+(0===n?"bg-primary/10 border border-primary/20 shadow-xs":"hover:bg-base-200/70 border border-transparent"),r.setAttribute("data-index",n),r.setAttribute("data-url",e.url);var a,o,s,l,i=d[e.icon]||d[e.category]||d.search,c=function(e,t){if(!e||!t)return u(e);var n=u(e),r=u(t).replace(/[.*+?^${}()|[\]\\]/g,"\\$&");try{var a=new RegExp("("+r+")","gi");return n.replace(a,'<span class="text-primary font-bold underline underline-offset-2">$1</span>')}catch(e){return n}}(e.title,t),p=(a=e.category,o=e.badge,s=e.badgeType,l="bg-base-200 text-base-content/60","primary"===s||"table"===a?l="bg-primary/10 text-primary border border-primary/20":"accent"===s||"column"===a?l="bg-secondary/10 text-secondary border border-secondary/20":"info"===s||"relation"===a?l="bg-info/10 text-info border border-info/20":"warning"===s||"index"===a?l="bg-warning/10 text-warning border border-warning/20":"secondary"!==s&&"command"!==a||(l="bg-purple-500/10 text-purple-600 dark:text-purple-400 border border-purple-500/20"),'<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase tracking-wider '+l+'">'+u(o||a)+"</span>"),m="";e.actions&&e.actions.length>0&&(m='<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity duration-150">',e.actions.forEach(function(e){m+='<a href="'+u(e.url)+'" class="btn btn-xs '+(e.primary?"btn-primary":"btn-ghost")+' rounded-md px-2 py-0.5 text-[11px] font-medium" onclick="event.stopPropagation();">'+u(e.label)+"</a>"}),m+="</div>"),r.innerHTML='<div class="flex items-center gap-3 min-w-0 flex-1"> <div class="grid h-8 w-8 shrink-0 place-items-center rounded-lg bg-base-200 text-base-content/70 group-hover:text-primary transition-colors">'+i+' </div> <div class="min-w-0 flex-1"> <div class="flex items-center gap-2"> <span class="font-medium text-sm text-base-content truncate">'+c+"</span>"+p+' </div> <div class="text-xs text-base-content/50 truncate mt-0.5">'+u(e.subtitle)+'</div> </div></div><div class="flex items-center gap-2 shrink-0">'+m+' <span class="js-enter-badge '+(0===n?"inline-flex":"hidden")+' items-center rounded border border-base-300 bg-base-100 px-1.5 py-0.5 text-[10px] font-mono text-base-content/60 font-bold shadow-xs">↵</span></div>',r.addEventListener("click",function(){E(e.url)}),r.addEventListener("mouseenter",function(){C(n)}),b.appendChild(r)})}}function C(e){if(b&&0!==l.length){e<0&&(e=0),e>=l.length&&(e=l.length-1),o=e;var t=b.querySelectorAll(".search-result-item");t.forEach(function(e,t){var n=t===o;e.className="search-result-item group flex items-center justify-between gap-3 p-2.5 rounded-xl cursor-pointer transition-all duration-100 "+(n?"bg-primary/10 border border-primary/20 shadow-xs":"hover:bg-base-200/70 border border-transparent");var r=e.querySelector(".js-enter-badge");r&&(n?r.classList.remove("hidden"):r.classList.add("hidden"))});var n=t[o];n&&n.scrollIntoView({block:"nearest",behavior:"smooth"})}}function E(e){e&&(x(),window.location.href=e)}function L(e){if("ArrowDown"===e.key)e.preventDefault(),C(o+1);else if("ArrowUp"===e.key)e.preventDefault(),C(o-1);else if("Enter"===e.key)e.preventDefault(),l[o]&&E(l[o].url);else if("Escape"===e.key)e.preventDefault(),x();else if("Tab"===e.key){e.preventDefault();var t=c.findIndex(function(e){return e.id===a}),n=e.shiftKey?(t-1+c.length)%c.length:(t+1)%c.length;f(c[n].id)}}document.addEventListener("keydown",function(e){var t=(e.ctrlKey||e.metaKey)&&"k"===e.key.toLowerCase(),n="/"===e.key&&!function(e){if(!e)return!1;var t=(e.tagName||"").toLowerCase();return"input"===t||"textarea"===t||"select"===t||e.isContentEditable}(document.activeElement);(t||n)&&(e.preventDefault(),i?x():w())}),document.addEventListener("DOMContentLoaded",function(){var e=document.getElementById("global-search-trigger");e&&e.addEventListener("click",function(){w()});var t=document.getElementById("table-search");t&&t.addEventListener("keydown",function(e){"Enter"===e.key&&(e.preventDefault(),w(t.value))})}),window.GlobalSearch={open:w,close:x,setCategory:f}}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(){"use strict";const e=document.getElementById("select-source-db"),t=document.getElementById("select-target-db"),n=document.getElementById("btn-swap-dbs"),a=document.getElementById("btn-compare-now"),s=document.getElementById("btn-generate-sql"),o=document.getElementById("btn-dry-run"),d=document.getElementById("btn-execute-sync"),l=document.getElementById("src-badge-engine"),c=document.getElementById("tgt-badge-engine"),r=document.getElementById("src-path-label"),i=document.getElementById("tgt-path-label"),m=document.getElementById("toggle-options-header"),b=document.getElementById("options-panel"),u=document.getElementById("options-chevron"),g=document.getElementById("badge-active-mode"),p=document.getElementById("table-checkboxes-container"),f=document.getElementById("btn-select-all-tables"),y=document.getElementById("btn-deselect-all-tables"),v=document.getElementById("diff-tabs"),h=document.getElementById("tab-content-unified"),x=document.getElementById("tab-content-visual"),E=document.getElementById("tab-content-data"),I=document.getElementById("unified-diff-view"),L=document.getElementById("visual-diff-cards"),w=document.getElementById("diff-stats-chips"),$=document.getElementById("btn-copy-unified-diff"),B=document.getElementById("input-filter-visual-tables"),k=document.querySelectorAll(".js-filter-status"),T=document.getElementById("select-data-diff-table"),C=document.getElementById("select-data-diff-limit"),D=document.getElementById("btn-fetch-data-diff"),S=document.getElementById("data-diff-stats"),M=document.getElementById("data-diff-results"),A=document.getElementById("modal-sql-patch"),R=document.getElementById("patch-sql-code"),H=document.getElementById("tab-up-sql"),U=document.getElementById("tab-down-sql"),O=document.getElementById("btn-copy-patch-sql"),q=document.getElementById("modal-sync-execute"),N=document.getElementById("exec-prompt-content"),j=document.getElementById("exec-destructive-banner"),F=document.getElementById("lbl-destructive-action-desc"),W=document.getElementById("lbl-confirm-tgt-highlight"),P=document.getElementById("lbl-affected-tables-list"),V=document.getElementById("lbl-confirm-src"),_=document.getElementById("lbl-confirm-tgt"),K=document.getElementById("lbl-confirm-mode"),Q=document.getElementById("lbl-confirm-mask"),J=document.getElementById("lbl-confirm-tables"),Y=(document.getElementById("exec-safety-locks"),document.getElementById("chk-confirm-destruction")),z=(document.getElementById("box-confirm-phrase"),document.getElementById("lbl-required-confirm-word")),G=document.getElementById("input-confirm-phrase"),X=document.getElementById("exec-progress-container"),Z=document.getElementById("exec-progress-bar"),ee=document.getElementById("exec-progress-percent"),te=document.getElementById("exec-current-table"),ne=document.getElementById("exec-log-container"),ae=document.getElementById("btn-exec-start"),se=document.getElementById("btn-exec-cancel"),oe=document.getElementById("btn-exec-close-x"),de=window.Utils?window.Utils.escapeHtml:e=>String(null==e?"":e);let le=null,ce=null,re="up",ie="all";function me(){const n=e.selectedOptions[0],a=t.selectedOptions[0];if(n&&n.value){const e=n.getAttribute("data-dialect")||"sqlite",t="true"===n.getAttribute("data-readonly");l.textContent=("postgres"===e?"PostgreSQL":"SQLite")+(t?" (RO)":""),l.className="badge badge-sm font-mono "+("postgres"===e?"badge-info":"badge-success"),r.innerHTML=`<span>ID: <code class="font-bold text-base-content/70">${de(n.value)}</code></span>`}if(a&&a.value){const e=a.getAttribute("data-dialect")||"postgres",t="true"===a.getAttribute("data-readonly");c.textContent=("postgres"===e?"PostgreSQL":"SQLite")+(t?" (RO)":""),c.className="badge badge-sm font-mono badge-info",i.innerHTML=`<span>ID: <code class="font-bold text-base-content/70">${de(a.value)}</code></span>`}!async function(){const t=e.value;if(!t)return;try{const e=await Api.get("/api/tables/"+encodeURIComponent(t))||[];if(T.innerHTML='<option value="" disabled selected>Select table…</option>',p.innerHTML="",0===e.length)return void(p.innerHTML='<span class="text-base-content/40 italic p-1 block">No tables found in this database.</span>');e.forEach(e=>{const t="object"==typeof e&&null!==e&&"name"in e?e.name:String(e),n=document.createElement("label");n.className="flex items-center gap-2 p-1.5 hover:bg-base-200/60 rounded-lg cursor-pointer transition",n.innerHTML=`\n <input type="checkbox" class="checkbox checkbox-xs checkbox-primary js-table-check" value="${de(t)}" checked />\n <span class="truncate font-mono">${de(t)}</span>\n `,p.appendChild(n);const a=document.createElement("option");a.value=t,a.textContent=t,T.appendChild(a)})}catch{}}()}function be(){return Array.from(p.querySelectorAll(".js-table-check:checked")).map(e=>e.value)}async function ue(){const n=e.value,l=t.value;if(!n||!l)return UI.showError("Please select both a Source and a Target database.");if(n===l)return UI.showError("Source and Target database must be different.");a.disabled=!0,a.innerHTML='<span class="loading loading-spinner loading-xs"></span> Computing Diff…';try{const e=await Api.post("/api/sync/diff/schema",{sourceDbId:n,targetDbId:l});le=e,function(e){w.innerHTML=`\n <span class="badge badge-sm badge-success gap-1 font-mono font-bold shadow-sm">+${e.stats.addedTables} Added</span>\n <span class="badge badge-sm badge-error gap-1 font-mono font-bold shadow-sm">-${e.stats.removedTables} Removed</span>\n <span class="badge badge-sm badge-warning gap-1 font-mono font-bold shadow-sm">~${e.stats.modifiedTables} Modified</span>\n <span class="badge badge-sm badge-ghost gap-1 font-mono">${e.stats.identicalTables} Identical</span>\n `;const t=e.unifiedDiff.split("\n"),n=t.map((e,t)=>{const n=de(e),a=`<span class="inline-block w-8 text-right pr-3 select-none opacity-30 font-mono text-[10px]">${t+1}</span>`;return e.startsWith("+++")||e.startsWith("---")||e.startsWith("diff --git")?`<div class="font-bold text-neutral-content/90 bg-white/[0.04] py-0.5">${a}${n}</div>`:e.startsWith("@@")?`<div class="font-bold text-cyan-400 bg-cyan-400/10 py-1 my-1 rounded border-l-2 border-cyan-400">${a}${n}</div>`:e.startsWith("+")?`<div class="text-emerald-400 bg-emerald-500/10 py-0.5 border-l-2 border-emerald-500">${a}${n}</div>`:e.startsWith("-")?`<div class="text-rose-400 bg-rose-500/10 py-0.5 border-l-2 border-rose-500">${a}${n}</div>`:e.startsWith("~")?`<div class="text-amber-400 bg-amber-500/10 py-0.5 border-l-2 border-amber-500">${a}${n}</div>`:`<div class="text-neutral-content/60 py-0.5">${a}${n}</div>`}).join("");I.innerHTML=n,ge()}(e),e.tables&&e.tables.length>0&&(T.innerHTML="",e.tables.forEach(e=>{const t=document.createElement("option");t.value=e.name,t.textContent=`${e.name} (${e.action})`,T.appendChild(t)}),T.options.length>0&&(T.selectedIndex=0)),s.disabled=!1,o.disabled=!1,d.disabled=!1,UI.showToast("Schema diff computed successfully.","success")}catch(e){UI.showError(e.message)}finally{a.disabled=!1,a.innerHTML="Compare Diff Now"}}function ge(){if(!le||!le.tables||0===le.tables.length)return void(L.innerHTML='<div class="text-base-content/40 italic text-center py-16">No tables found.</div>');const e=(B?.value||"").trim().toLowerCase(),t=le.tables.filter(t=>!(e&&!t.name.toLowerCase().includes(e))&&(("modified"!==ie||"modified"===t.action)&&(("added"!==ie||"added"===t.action)&&("removed"!==ie||"removed"===t.action))));0!==t.length?(L.innerHTML=t.map(e=>{let t="",n="border-base-300";"added"===e.action?(t='<span class="badge badge-sm badge-success font-bold font-mono">+ ADDED</span>',n="border-success/40 bg-success/[0.03] shadow-sm"):"removed"===e.action?(t='<span class="badge badge-sm badge-error font-bold font-mono">- REMOVED</span>',n="border-error/40 bg-error/[0.03] shadow-sm"):"modified"===e.action?(t='<span class="badge badge-sm badge-warning font-bold font-mono">~ MODIFIED</span>',n="border-warning/40 bg-warning/[0.03] shadow-sm"):t='<span class="badge badge-sm badge-ghost font-mono">IDENTICAL</span>';const a=[];for(const t of e.columnDiffs)"added"===t.action?a.push(`<li class="text-emerald-500 font-semibold">+ Column: <strong>${de(t.name)}</strong> (${t.sourceCol?t.sourceCol.type:""})</li>`):"removed"===t.action?a.push(`<li class="text-rose-500 font-semibold">- Column: <strong>${de(t.name)}</strong> (${t.targetCol?t.targetCol.type:""})</li>`):"modified"===t.action&&a.push(`<li class="text-amber-500 font-semibold">~ Column <strong>${de(t.name)}</strong>: ${de(t.changes.join("; "))}</li>`);for(const t of e.indexesAdded)a.push(`<li class="text-emerald-500 font-semibold">+ Index: ${de(t)}</li>`);for(const t of e.indexesRemoved)a.push(`<li class="text-rose-500 font-semibold">- Index: ${de(t)}</li>`);for(const t of e.fksAdded)a.push(`<li class="text-emerald-500 font-semibold">+ Foreign Key: ${de(t)}</li>`);for(const t of e.fksRemoved)a.push(`<li class="text-rose-500 font-semibold">- Foreign Key: ${de(t)}</li>`);const s=a.length>0?`<ul class="list-disc list-inside space-y-1.5 text-xs font-mono mt-3 p-3 rounded-lg bg-base-100 border border-base-200">${a.join("")}</ul>`:'<div class="text-xs text-base-content/40 mt-1">Schema structure is identical.</div>';return`\n <div class="rounded-xl border ${n} p-4 space-y-2 transition hover:shadow-md">\n <div class="flex items-center justify-between">\n <span class="font-mono text-sm font-bold flex items-center gap-2">\n ${de(e.name)}\n </span>\n <div class="flex items-center gap-2">\n ${t}\n <button type="button" class="btn btn-ghost btn-xs text-primary gap-1 js-inspect-data-btn hover:bg-primary/10" data-table="${de(e.name)}">\n Inspect Data ➔\n </button>\n </div>\n </div>\n ${s}\n </div>\n `}).join(""),L.querySelectorAll(".js-inspect-data-btn").forEach(e=>{e.addEventListener("click",()=>{const t=e.getAttribute("data-table");t&&(T.value=t,v.querySelector('[data-tab="data"]').click(),D.click())})})):L.innerHTML='<div class="text-base-content/40 italic text-center py-10">No tables match the current filter.</div>'}function pe(){ce&&(H.classList.toggle("tab-active","up"===re),U.classList.toggle("tab-active","down"===re),R.textContent="up"===re?ce.upSql:ce.downSql)}e.addEventListener("change",me),t.addEventListener("change",me),n.addEventListener("click",()=>{const n=e.value,a=t.value;e.value=a,t.value=n,me(),e.value&&t.value&&ue()}),m.addEventListener("click",()=>{const e=b.classList.toggle("hidden");u&&(u.style.transform=e?"rotate(0deg)":"rotate(180deg)")}),document.querySelectorAll('input[name="sync-mode"]').forEach(e=>{e.addEventListener("change",e=>{const t=e.target.value;g&&("full"===t?g.textContent="Full Clone":"schema_only"===t?g.textContent="Schema Only":"data_only_replace"===t&&(g.textContent="Data Only"))})}),f.addEventListener("click",()=>{p.querySelectorAll(".js-table-check").forEach(e=>e.checked=!0)}),y.addEventListener("click",()=>{p.querySelectorAll(".js-table-check").forEach(e=>e.checked=!1)}),a.addEventListener("click",ue),B&&B.addEventListener("input",ge),k.forEach(e=>{e.addEventListener("click",()=>{k.forEach(e=>e.classList.remove("btn-active")),e.classList.add("btn-active"),ie=e.getAttribute("data-filter")||"all",ge()})}),$&&$.addEventListener("click",()=>{le&&le.unifiedDiff&&navigator.clipboard.writeText(le.unifiedDiff).then(()=>{UI.showToast("Unified diff copied to clipboard.","success")})}),v.querySelectorAll(".tab").forEach(e=>{e.addEventListener("click",()=>{v.querySelectorAll(".tab").forEach(e=>e.classList.remove("tab-active")),e.classList.add("tab-active");const t=e.getAttribute("data-tab");h.classList.toggle("hidden","unified"!==t),x.classList.toggle("hidden","visual"!==t),E.classList.toggle("hidden","data"!==t)})}),D.addEventListener("click",async()=>{const n=T.value,a=e.value,s=t.value,o=parseInt(C?.value||"150",10);if(!a||!s||!n)return UI.showError("Select source, target, and a table to inspect.");D.disabled=!0,D.innerHTML='<span class="loading loading-spinner loading-xs"></span> Fetching…';try{const e=await Api.post("/api/sync/diff/data",{sourceDbId:a,targetDbId:s,table:n,limit:o});if(S.innerHTML=`\n <span class="badge badge-sm badge-outline font-mono">Source: ${e.sourceRowCount}</span>\n <span class="badge badge-sm badge-outline font-mono">Target: ${e.targetRowCount}</span>\n <span class="badge badge-sm badge-success font-mono font-bold">+${e.addedCount} Added</span>\n <span class="badge badge-sm badge-error font-mono font-bold">-${e.removedCount} Removed</span>\n <span class="badge badge-sm badge-warning font-mono font-bold">~${e.modifiedCount} Modified</span>\n `,0===e.rows.length)return void(M.innerHTML=`\n <div class="text-success font-bold text-center py-16 flex flex-col items-center gap-2">\n <span class="text-base">✓ Data records in table "${de(n)}" are completely identical.</span>\n <span class="text-xs font-normal text-base-content/60 font-mono">${e.sourceRowCount} matching records compared (sample limit: ${o}).</span>\n </div>\n `);const t=e.rows.map(e=>{let t="",n="bg-base-200/40 border-base-300";if("added"===e.action)return t='<span class="badge badge-xs badge-success font-bold">+ ADDED IN SOURCE</span>',n="bg-success/[0.04] border-success/30",`\n <div class="rounded-xl border ${n} p-3.5 space-y-2 font-mono text-xs">\n <div class="flex items-center justify-between">\n <span class="font-bold text-emerald-600 dark:text-emerald-400">PK: ${de(e.key)}</span>\n ${t}\n </div>\n <div class="text-base-content/80 text-[11px] overflow-x-auto bg-base-100 p-2.5 rounded-lg border border-base-200">\n <pre class="m-0">${de(JSON.stringify(e.sourceRow,null,2))}</pre>\n </div>\n </div>\n `;if("removed"===e.action)return t='<span class="badge badge-xs badge-error font-bold">- REMOVED FROM SOURCE</span>',n="bg-error/[0.04] border-error/30",`\n <div class="rounded-xl border ${n} p-3.5 space-y-2 font-mono text-xs">\n <div class="flex items-center justify-between">\n <span class="font-bold text-rose-600 dark:text-rose-400">PK: ${de(e.key)}</span>\n ${t}\n </div>\n <div class="text-base-content/80 text-[11px] overflow-x-auto bg-base-100 p-2.5 rounded-lg border border-base-200">\n <pre class="m-0">${de(JSON.stringify(e.targetRow,null,2))}</pre>\n </div>\n </div>\n `;t='<span class="badge badge-xs badge-warning font-bold">~ MODIFIED</span>',n="bg-warning/[0.04] border-warning/30";const a=(e.changes||[]).map(e=>`\n <tr class="hover:bg-base-200/50">\n <td class="font-bold text-base-content">${de(e.column)}</td>\n <td class="text-rose-500 bg-rose-500/10 rounded px-2 py-1 font-mono">${de(JSON.stringify(e.targetValue))}</td>\n <td class="text-emerald-500 bg-emerald-500/10 rounded px-2 py-1 font-mono">${de(JSON.stringify(e.sourceValue))}</td>\n </tr>\n `).join("");return`\n <div class="rounded-xl border ${n} p-3.5 space-y-2 font-mono text-xs">\n <div class="flex items-center justify-between">\n <span class="font-bold text-amber-600 dark:text-amber-400">PK: ${de(e.key)}</span>\n ${t}\n </div>\n <div class="overflow-x-auto rounded-lg border border-base-200 bg-base-100">\n <table class="table table-xs w-full">\n <thead>\n <tr class="bg-base-200/60 text-base-content/60">\n <th>Field</th>\n <th>Target (Current Value)</th>\n <th>Source (Incoming Value)</th>\n </tr>\n </thead>\n <tbody>\n ${a}\n </tbody>\n </table>\n </div>\n </div>\n `}).join("");M.innerHTML=`\n <div class="space-y-3">\n ${t}\n </div>\n `}catch(e){UI.showError(e.message)}finally{D.disabled=!1,D.innerHTML="Compare Rows"}}),s.addEventListener("click",async()=>{const n=e.value,a=t.value;try{const e=await Api.post("/api/sync/patch",{sourceDbId:n,targetDbId:a});ce=e,re="up",pe(),A.showModal()}catch(e){UI.showError(e.message)}}),H.addEventListener("click",()=>{re="up",pe()}),U.addEventListener("click",()=>{re="down",pe()}),O.addEventListener("click",()=>{const e=R.textContent||"";e&&navigator.clipboard.writeText(e).then(()=>{UI.showToast("Migration SQL copied to clipboard.","success")})}),o.addEventListener("click",async()=>{const n=e.value,a=t.value,s=document.querySelector('input[name="sync-mode"]:checked')?.value||"full",d=document.getElementById("chk-mask-data")?.checked,l=be();o.disabled=!0,o.innerHTML='<span class="loading loading-spinner loading-xs"></span> Simulating…';try{const e=await Api.post("/api/sync/dry-run",{sourceDbId:n,targetDbId:a,mode:s,tables:l,maskData:d});UI.showToast(e.summary,"info")}catch(e){UI.showError(e.message)}finally{o.disabled=!1,o.innerHTML="Dry Run"}});let fe="OVERWRITE";function ye(){const e=Y.checked,t=(G.value||"").trim().toUpperCase()===fe.toUpperCase();ae.disabled=!(e&&t)}Y.addEventListener("change",ye),G.addEventListener("input",ye),d.addEventListener("click",()=>{const n=e.value,a=t.value,s=document.querySelector('input[name="sync-mode"]:checked')?.value||"full",o=document.getElementById("chk-mask-data")?.checked,d=be();V.textContent=n,_.textContent=a,W.textContent=a,K.textContent="full"===s?"Full Clone (Drop & Recreate)":"schema_only"===s?"Schema Only":"Data Only (Truncate & Replace)",Q.textContent=o?"Enabled (Synthetic PII)":"Disabled",J.textContent=d.length>0?`${d.length} tables selected`:"All discovered tables",P.textContent=d.length>0?d.join(", "):"All tables in database";const l="full"===s||"data_only_replace"===s;a.toLowerCase().includes("prod")||a.toLowerCase().includes("production")?(fe=a,F.textContent="PERMANENTLY OVERWRITE PRODUCTION DATABASE"):"full"===s?(fe="OVERWRITE",F.textContent="permanently drop and recreate tables"):"data_only_replace"===s?(fe="TRUNCATE",F.textContent="permanently truncate/delete all existing rows"):(fe="SYNC",F.textContent="apply schema structures to"),z.textContent=fe,j.classList.toggle("hidden",!l),Y.checked=!1,G.value="",ae.disabled=!0,N.classList.remove("hidden"),X.classList.add("hidden"),ae.classList.remove("hidden"),se.classList.remove("hidden"),q.showModal()}),ae.addEventListener("click",async()=>{const n=e.value,a=t.value,s=document.querySelector('input[name="sync-mode"]:checked')?.value||"full",o=document.getElementById("chk-mask-data")?.checked,d=be(),l=(G.value||"").trim();if(!Y.checked||l.toUpperCase()!==fe.toUpperCase())return UI.showError(`Type "${fe}" and check the confirmation box to proceed.`);N.classList.add("hidden"),X.classList.remove("hidden"),ae.classList.add("hidden"),se.classList.add("hidden"),oe.disabled=!0,Z.value=10,ee.textContent="10%",ne.innerHTML=`<div>[${(new Date).toLocaleTimeString()}] Initializing replication: ${de(n)} ➔ ${de(a)}...</div>`,ne.innerHTML+=`<div>[${(new Date).toLocaleTimeString()}] Mode: ${s}, Masking: ${o?"Yes":"No"}</div>`;try{Z.value=35,ee.textContent="35%";const e=await Api.post("/api/sync/execute",{sourceDbId:n,targetDbId:a,mode:s,tables:d,maskData:o,confirmTargetName:l});Z.value=100,ee.textContent="100%",te.innerHTML='<span class="text-success font-bold">✓ Replication Succeeded!</span>',ne.innerHTML+=`<div class="text-success font-bold mt-1.5">[${(new Date).toLocaleTimeString()}] ${de(e.summary)}</div>`,ne.innerHTML+=`<div class="text-base-content/60 mt-1">[${(new Date).toLocaleTimeString()}] Completed in ${e.executionTimeMs}ms. (${e.totalRowsTransferred} rows transferred).</div>`,UI.showToast(e.summary,"success"),setTimeout(()=>{oe.disabled=!1,ue()},1200)}catch(e){te.innerHTML='<span class="text-error font-bold">✗ Replication Failed</span>',ne.innerHTML+=`<div class="text-error font-bold mt-1.5">[${(new Date).toLocaleTimeString()}] Error: ${de(e.message)}</div>`,oe.disabled=!1,UI.showError(e.message)}}),me()}();
|
package/dist/types/api.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {ColumnInfo,ForeignKeyInfo,IndexInfo,
|
|
1
|
+
import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
|
package/dist/utils/common.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const
|
|
1
|
+
export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const encodePk:(values:unknown[])=>string;export declare const decodePk:(encoded:string)=>string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare const truncate:(str:string,maxLength:number)=>string;
|