admindb 2.5.0 → 2.6.2
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 +13 -1
- 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/router.d.ts +1 -1
- 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/db/dialects/postgres/introspector.js +71 -71
- package/dist/db/dialects/sqlite/introspector.js +6 -3
- package/dist/modules/index.d.ts +1 -1
- package/dist/modules/index.js +9 -1
- 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/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/public/css/app.css +2 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/search.js +1 -0
- package/dist/public/js/sync.js +1 -0
- package/dist/utils/icons.js +3 -0
- package/dist/views/layouts/main.hbs +1 -1
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/seed-select.hbs +252 -1
- package/dist/views/pages/seed.hbs +307 -1
- package/dist/views/pages/sync.hbs +1 -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/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +14 -9
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.db,s=t.dataset.table,o=t.value,l=t.dataset.orderby||"",a=t.dataset.orderdir||"asc",i=t.dataset.filters||"";let r=e+"/tables/"+encodeURIComponent(n)+"/"+encodeURIComponent(s)+"?page=1&size="+encodeURIComponent(o)+"&orderBy="+encodeURIComponent(l)+"&orderDir="+encodeURIComponent(a);i&&(r+="&"+i),window.location.href=r})}(),function(){const e=document.getElementById("import-csv"),t=document.getElementById("import-panel"),n=document.getElementById("import-csv-close"),s=document.getElementById("import-csv-text"),l=document.getElementById("import-csv-file"),a=document.getElementById("import-csv-submit");e&&t&&e.addEventListener("click",()=>{t.classList.toggle("hidden"),t.classList.contains("hidden")||s.focus()}),n&&n.addEventListener("click",()=>t.classList.add("hidden")),l&&l.addEventListener("change",()=>{const e=l.files&&l.files[0];if(!e)return;const t=new FileReader;t.onload=()=>{s.value=String(t.result||"")},t.readAsText(e)}),a&&s&&a.addEventListener("click",async()=>{const e=s.value.trim();if(!e)return UI.showError("Paste or choose a CSV file first.");const t=window.location.pathname.match(/\/tables\/([^/]+)\/([^/]+)/),n=t?decodeURIComponent(t[2]):"";if(!n)return UI.showError("Could not determine the table.");a.disabled=!0;try{const t=await Api.post("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/import",{csv:e});UI.showToast(t.message||"Import complete.","success"),window.location.reload()}catch(e){UI.showError(e.message)}finally{a.disabled=!1}})}(),document.querySelectorAll(".js-ref-toggle").forEach(e=>{e.addEventListener("click",t=>{if(t.stopPropagation(),"true"===e.getAttribute("aria-expanded"))return void _();const n=e.dataset.table,s=e.dataset.pk;n&&s&&(e.classList.add("opacity-50"),Api.get("/api/tables/"+o+"/"+encodeURIComponent(n)+"/rows/"+encodeURIComponent(s)+"/references").then(t=>{e.classList.remove("opacity-50");const n=e.dataset.refTable,s=(t&&t.references||[]).filter(e=>!n||e.table===n);e.setAttribute("aria-expanded","true"),W(e,!0),V(e,s)}).catch(t=>{e.classList.remove("opacity-50"),UI.showError(t.message)}))})})}()}();
|
|
1
|
+
!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)}))})})}()}();
|
|
@@ -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/utils/icons.js
CHANGED
|
@@ -13,6 +13,9 @@ exports.ICONS = {
|
|
|
13
13
|
book: '<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 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /></svg>',
|
|
14
14
|
schema: '<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>',
|
|
15
15
|
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" stroke-linecap="round" stroke-linejoin="round"/><rect x="16" y="3" width="6" height="5" rx="1" stroke-linecap="round" stroke-linejoin="round"/><rect x="9" y="16" width="6" height="5" rx="1" stroke-linecap="round" stroke-linejoin="round"/><path stroke-linecap="round" stroke-linejoin="round" d="M5 8v4h14V8M12 12v4"/></svg>',
|
|
16
|
+
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>',
|
|
17
|
+
sync: '<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 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /></svg>',
|
|
18
|
+
swap: '<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 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4" /></svg>',
|
|
16
19
|
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>',
|
|
17
20
|
filter: '<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 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414A1 1 0 0014 15.414V20a1 1 0 01-1 1h-2a1 1 0 01-1-1v-4.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /></svg>',
|
|
18
21
|
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>',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
1
|
<meta charset="utf-8" />
|
|
3
2
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
4
3
|
<title>{{title}} · AdminDB</title>
|
|
5
4
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
6
5
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
7
6
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
8
7
|
<link rel="stylesheet" href="{{basePath}}/css/app.css" />
|
|
9
8
|
<script>
|
|
10
9
|
window.APP = {
|
|
11
10
|
basePath: {{{json basePath}}},
|
|
12
11
|
dbId: {{{json dbId}}},
|
|
13
12
|
readonly: {{{json readonly}}},
|
|
14
13
|
serverless: {{{json serverless}}},
|
|
15
14
|
dialect: {{{json dialect}}},
|
|
16
15
|
isSqlite: {{{json isSqlite}}},
|
|
17
16
|
isPostgres: {{{json isPostgres}}}
|
|
18
17
|
};
|
|
19
18
|
// Apply the saved theme before first paint to avoid a flash.
|
|
20
19
|
(function () {
|
|
21
20
|
try {
|
|
22
21
|
var t = localStorage.getItem('admindb-theme');
|
|
23
22
|
if (t === 'dark') document.documentElement.setAttribute('data-theme', 'admindbDark');
|
|
24
23
|
} catch (e) {}
|
|
25
24
|
})();
|
|
26
25
|
</script>
|
|
27
26
|
<script>
|
|
28
27
|
window.AdminIcons = {
|
|
29
28
|
map: {{{json icons}}},
|
|
30
29
|
get: function(name, customClass) {
|
|
31
30
|
var svg = this.map[name] || '';
|
|
32
31
|
if (!svg || !customClass) return svg;
|
|
33
32
|
if (svg.indexOf('class="') !== -1) {
|
|
34
33
|
return svg.replace(/class="[^"]*"/, 'class="' + customClass + '"');
|
|
35
34
|
}
|
|
36
35
|
return svg.replace('<svg', '<svg class="' + customClass + '"');
|
|
37
36
|
}
|
|
38
37
|
};
|
|
39
38
|
</script>
|
|
40
39
|
<script src="{{basePath}}/js/common.js" defer></script>
|
|
41
40
|
<script src="{{basePath}}/js/api.js" defer></script>
|
|
42
41
|
<script src="{{basePath}}/js/app.js" defer></script>
|
|
43
42
|
<script src="{{basePath}}/js/inspector.js" defer></script>
|
|
44
43
|
<div class="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
|
|
45
44
|
<div class="absolute -left-24 top-32 h-72 w-72 rounded-full bg-primary/15 blur-3xl"></div>
|
|
46
45
|
<div class="absolute -right-24 bottom-20 h-80 w-80 rounded-full bg-orange-500/10 blur-3xl"></div>
|
|
47
46
|
</div>
|
|
48
47
|
{{#if readonly}}
|
|
49
48
|
<div class="flex items-center justify-center gap-2 bg-warning/15 px-4 py-2 text-center text-sm font-semibold text-warning">
|
|
50
49
|
<span class="shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
51
50
|
{{#if serverless}}
|
|
52
51
|
Serverless mode active — database is open in read-only mode. Writes and schema modifications are disabled.
|
|
53
52
|
{{else}}
|
|
54
53
|
Read-only mode — this database is open without write access. All writes are disabled.
|
|
55
54
|
{{/if}}
|
|
56
55
|
</div>
|
|
57
56
|
{{/if}}
|
|
58
57
|
{{#if noSidebar}}
|
|
59
58
|
<div class="flex min-h-screen flex-col">
|
|
60
59
|
{{> navbar}}
|
|
61
60
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
62
61
|
{{{body}}}
|
|
63
62
|
</main>
|
|
64
63
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
65
64
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
66
65
|
</footer>
|
|
67
66
|
</div>
|
|
68
67
|
{{else}}
|
|
69
68
|
<div class="drawer lg:drawer-open">
|
|
70
69
|
<input id="sidebar-toggle" type="checkbox" class="drawer-toggle" />
|
|
71
70
|
<div class="drawer-content flex min-h-screen flex-col">
|
|
72
71
|
{{> navbar}}
|
|
73
72
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
74
73
|
{{{body}}}
|
|
75
74
|
</main>
|
|
76
75
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
77
76
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
78
77
|
</footer>
|
|
79
78
|
</div>
|
|
80
79
|
<div class="drawer-side z-40 lg:sticky lg:top-0 lg:h-screen lg:max-h-screen">
|
|
81
80
|
<label for="sidebar-toggle" aria-label="close sidebar" class="drawer-overlay"></label>
|
|
82
81
|
{{> sidebar}}
|
|
83
82
|
</div>
|
|
84
83
|
</div>
|
|
85
84
|
{{/if}}
|
|
86
85
|
<div id="toast-container" class="pointer-events-none fixed inset-x-0 top-4 z-50 flex flex-col items-center gap-2 px-4"></div>
|
|
86
|
+
<!DOCTYPE html>
|
|
87
87
|
<meta charset="utf-8" />
|
|
88
88
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
89
89
|
<title>{{title}} · AdminDB</title>
|
|
90
90
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
91
91
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
92
92
|
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
|
|
93
93
|
<link rel="stylesheet" href="{{basePath}}/css/app.css" />
|
|
94
94
|
<script>
|
|
95
95
|
window.APP = {
|
|
96
96
|
basePath: {{{json basePath}}},
|
|
97
97
|
dbId: {{{json dbId}}},
|
|
98
98
|
readonly: {{{json readonly}}},
|
|
99
99
|
serverless: {{{json serverless}}},
|
|
100
100
|
dialect: {{{json dialect}}},
|
|
101
101
|
isSqlite: {{{json isSqlite}}},
|
|
102
102
|
isPostgres: {{{json isPostgres}}}
|
|
103
103
|
};
|
|
104
104
|
// Apply the saved theme before first paint to avoid a flash.
|
|
105
105
|
(function () {
|
|
106
106
|
try {
|
|
107
107
|
var t = localStorage.getItem('admindb-theme');
|
|
108
108
|
if (t === 'dark') document.documentElement.setAttribute('data-theme', 'admindbDark');
|
|
109
109
|
} catch (e) {}
|
|
110
110
|
})();
|
|
111
111
|
</script>
|
|
112
112
|
<script>
|
|
113
113
|
window.AdminIcons = {
|
|
114
114
|
map: {{{json icons}}},
|
|
115
115
|
get: function(name, customClass) {
|
|
116
116
|
var svg = this.map[name] || '';
|
|
117
117
|
if (!svg || !customClass) return svg;
|
|
118
118
|
if (svg.indexOf('class="') !== -1) {
|
|
119
119
|
return svg.replace(/class="[^"]*"/, 'class="' + customClass + '"');
|
|
120
120
|
}
|
|
121
121
|
return svg.replace('<svg', '<svg class="' + customClass + '"');
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
</script>
|
|
125
125
|
<script src="{{basePath}}/js/common.js" defer></script>
|
|
126
126
|
<script src="{{basePath}}/js/api.js" defer></script>
|
|
127
127
|
<script src="{{basePath}}/js/app.js" defer></script>
|
|
128
128
|
<script src="{{basePath}}/js/inspector.js" defer></script>
|
|
129
129
|
<script src="{{basePath}}/js/search.js" defer></script>
|
|
130
130
|
<div class="pointer-events-none fixed inset-0 -z-10 overflow-hidden">
|
|
131
131
|
<div class="absolute -left-24 top-32 h-72 w-72 rounded-full bg-primary/15 blur-3xl"></div>
|
|
132
132
|
<div class="absolute -right-24 bottom-20 h-80 w-80 rounded-full bg-orange-500/10 blur-3xl"></div>
|
|
133
133
|
</div>
|
|
134
134
|
{{#if readonly}}
|
|
135
135
|
<div class="flex items-center justify-center gap-2 bg-warning/15 px-4 py-2 text-center text-sm font-semibold text-warning">
|
|
136
136
|
<span class="shrink-0">{{{icon "warning" class="h-4 w-4"}}}</span>
|
|
137
137
|
{{#if serverless}}
|
|
138
138
|
Serverless mode active — database is open in read-only mode. Writes and schema modifications are disabled.
|
|
139
139
|
{{else}}
|
|
140
140
|
Read-only mode — this database is open without write access. All writes are disabled.
|
|
141
141
|
{{/if}}
|
|
142
142
|
</div>
|
|
143
143
|
{{/if}}
|
|
144
144
|
{{#if noSidebar}}
|
|
145
145
|
<div class="flex min-h-screen flex-col">
|
|
146
146
|
{{> navbar}}
|
|
147
147
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
148
148
|
{{{body}}}
|
|
149
149
|
</main>
|
|
150
150
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
151
151
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
152
152
|
</footer>
|
|
153
153
|
</div>
|
|
154
154
|
{{else}}
|
|
155
155
|
<div class="drawer lg:drawer-open">
|
|
156
156
|
<input id="sidebar-toggle" type="checkbox" class="drawer-toggle" />
|
|
157
157
|
<div class="drawer-content flex min-h-screen flex-col">
|
|
158
158
|
{{> navbar}}
|
|
159
159
|
<main class="mx-auto w-full max-w-[1500px] flex-1 px-4 py-6 sm:px-6 lg:px-8 lg:py-8">
|
|
160
160
|
{{{body}}}
|
|
161
161
|
</main>
|
|
162
162
|
<footer class="border-t border-base-300 px-6 py-4 text-center text-xs text-base-content/40">
|
|
163
163
|
AdminDB{{#if version}} v{{version}}{{/if}} · SQLite & PostgreSQL database administration
|
|
164
164
|
</footer>
|
|
165
165
|
</div>
|
|
166
166
|
<div class="drawer-side z-40 lg:sticky lg:top-0 lg:h-screen lg:max-h-screen">
|
|
167
167
|
<label for="sidebar-toggle" aria-label="close sidebar" class="drawer-overlay"></label>
|
|
168
168
|
{{> sidebar}}
|
|
169
169
|
</div>
|
|
170
170
|
</div>
|
|
171
171
|
{{/if}}
|
|
172
172
|
<div id="toast-container" class="pointer-events-none fixed inset-x-0 top-4 z-50 flex flex-col items-center gap-2 px-4"></div>
|