db-studio 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +29 -27
- package/dist/index.js.map +1 -1
- package/dist/web-dist/assets/_pathlessLayout-C6coCMpi.js +1 -0
- package/dist/web-dist/assets/{_pathlessLayout-CEv6uosT.js.map → _pathlessLayout-C6coCMpi.js.map} +1 -1
- package/dist/web-dist/assets/_pathlessLayout-DoBLI2ja.js +132 -0
- package/dist/web-dist/assets/_pathlessLayout-DoBLI2ja.js.map +1 -0
- package/dist/web-dist/assets/_queryId-Chj34V6b.js +1 -0
- package/dist/web-dist/assets/{_queryId-Bq64AuuI.js.map → _queryId-Chj34V6b.js.map} +1 -1
- package/dist/web-dist/assets/{_table-Bt9of1on.js → _table-Bg4NvAi4.js} +1 -1
- package/dist/web-dist/assets/{_table-Bt9of1on.js.map → _table-Bg4NvAi4.js.map} +1 -1
- package/dist/web-dist/assets/_table-CHFe8v3q.js +1 -0
- package/dist/web-dist/assets/{_table-CQhKMAcZ.js.map → _table-CHFe8v3q.js.map} +1 -1
- package/dist/web-dist/assets/ai-assistant-D2oc4TtC.js +1 -0
- package/dist/web-dist/assets/ai-assistant-D2oc4TtC.js.map +1 -0
- package/dist/web-dist/assets/browser-B3ZulIJb.js +1 -0
- package/dist/web-dist/assets/{browser-CFsrLhq1.js.map → browser-B3ZulIJb.js.map} +1 -1
- package/dist/web-dist/assets/code-editor-q3FlZk-L.js +9 -0
- package/dist/web-dist/assets/code-editor-q3FlZk-L.js.map +1 -0
- package/dist/web-dist/assets/database.store-O79u8Fhj.js +1 -0
- package/dist/web-dist/assets/database.store-O79u8Fhj.js.map +1 -0
- package/dist/web-dist/assets/{dist-B90HBock.js → dist-DBHqGKiG.js} +1 -1
- package/dist/web-dist/assets/{dist-B90HBock.js.map → dist-DBHqGKiG.js.map} +1 -1
- package/dist/web-dist/assets/{highlighted-body-OFNGDK62-BGFI1Y6G.js → highlighted-body-OFNGDK62-CFFJC4SW.js} +1 -1
- package/dist/web-dist/assets/{highlighted-body-OFNGDK62-BGFI1Y6G.js.map → highlighted-body-OFNGDK62-CFFJC4SW.js.map} +1 -1
- package/dist/web-dist/assets/icons-CtpwLWUI.js +1 -0
- package/dist/web-dist/assets/icons-CtpwLWUI.js.map +1 -0
- package/dist/web-dist/assets/index-Cyl_0l9I.css +2 -0
- package/dist/web-dist/assets/index-XoXxezo4.js +35 -0
- package/dist/web-dist/assets/index-XoXxezo4.js.map +1 -0
- package/dist/web-dist/assets/{indexes-BKOZUa9Z.js → indexes-C9QqJKhp.js} +1 -1
- package/dist/web-dist/assets/{indexes-BKOZUa9Z.js.map → indexes-C9QqJKhp.js.map} +1 -1
- package/dist/web-dist/assets/{logs-DHha3vDy.js → logs-B8ah8F7D.js} +1 -1
- package/dist/web-dist/assets/{logs-DHha3vDy.js.map → logs-B8ah8F7D.js.map} +1 -1
- package/dist/web-dist/assets/mermaid-GHXKKRXX-TpVEoP0K.js +1 -0
- package/dist/web-dist/assets/middleware-CCyr6cvp.js +1 -0
- package/dist/web-dist/assets/middleware-CCyr6cvp.js.map +1 -0
- package/dist/web-dist/assets/popover-wbdw6Hmx.js +1 -0
- package/dist/web-dist/assets/popover-wbdw6Hmx.js.map +1 -0
- package/dist/web-dist/assets/queries.store-CoJVGJKk.js +1 -0
- package/dist/web-dist/assets/queries.store-CoJVGJKk.js.map +1 -0
- package/dist/web-dist/assets/query-runner-Bgs0j50a.js +17 -0
- package/dist/web-dist/assets/query-runner-Bgs0j50a.js.map +1 -0
- package/dist/web-dist/assets/radix-ui-CgJOuzDv.js +51 -0
- package/dist/web-dist/assets/radix-ui-CgJOuzDv.js.map +1 -0
- package/dist/web-dist/assets/react-Cxb6eTZp.js +1 -0
- package/dist/web-dist/assets/react-Cxb6eTZp.js.map +1 -0
- package/dist/web-dist/assets/{react-dom-0lXVP1OR.js → react-dom-yg5bT29P.js} +1 -1
- package/dist/web-dist/assets/{react-dom-0lXVP1OR.js.map → react-dom-yg5bT29P.js.map} +1 -1
- package/dist/web-dist/assets/redis-value-COa6YzFD.js +1 -0
- package/dist/web-dist/assets/{redis-value-CJoRCZun.js.map → redis-value-COa6YzFD.js.map} +1 -1
- package/dist/web-dist/assets/runner-DW5iXMmq.js +1 -0
- package/dist/web-dist/assets/{runner-CZKDzRoU.js.map → runner-DW5iXMmq.js.map} +1 -1
- package/dist/web-dist/assets/schema-CtOicTEt.js +64 -0
- package/dist/web-dist/assets/schema-CtOicTEt.js.map +1 -0
- package/dist/web-dist/assets/{schema-0sozVL-z.js → schema-oy5o_4bt.js} +1 -1
- package/dist/web-dist/assets/{schema-0sozVL-z.js.map → schema-oy5o_4bt.js.map} +1 -1
- package/dist/web-dist/assets/{table-CPQhgD-a.js → table-CN9ZTCOi.js} +1 -1
- package/dist/web-dist/assets/{table-CPQhgD-a.js.map → table-CN9ZTCOi.js.map} +1 -1
- package/dist/web-dist/assets/tanstack-g1w0v_rJ.js +10 -0
- package/dist/web-dist/assets/tanstack-g1w0v_rJ.js.map +1 -0
- package/dist/web-dist/assets/tooltip-B7gM8xl3.js +9 -0
- package/dist/web-dist/assets/tooltip-B7gM8xl3.js.map +1 -0
- package/dist/web-dist/assets/{visualizer-DTPn0ywW.js → visualizer-DcEwqmuY.js} +1 -1
- package/dist/web-dist/assets/{visualizer-DTPn0ywW.js.map → visualizer-DcEwqmuY.js.map} +1 -1
- package/dist/web-dist/index.html +13 -11
- package/package.json +1 -1
- package/dist/web-dist/assets/_pathlessLayout-CEv6uosT.js +0 -1
- package/dist/web-dist/assets/_pathlessLayout-eo6iLvBo.js +0 -132
- package/dist/web-dist/assets/_pathlessLayout-eo6iLvBo.js.map +0 -1
- package/dist/web-dist/assets/_queryId-Bq64AuuI.js +0 -1
- package/dist/web-dist/assets/_table-CQhKMAcZ.js +0 -1
- package/dist/web-dist/assets/browser-CFsrLhq1.js +0 -1
- package/dist/web-dist/assets/code-editor-DQ86aZkm.js +0 -9
- package/dist/web-dist/assets/code-editor-DQ86aZkm.js.map +0 -1
- package/dist/web-dist/assets/database.store-KDHkiedw.js +0 -1
- package/dist/web-dist/assets/database.store-KDHkiedw.js.map +0 -1
- package/dist/web-dist/assets/icons-Bm7rSZLJ.js +0 -1
- package/dist/web-dist/assets/icons-Bm7rSZLJ.js.map +0 -1
- package/dist/web-dist/assets/index-BtqHnOp0.js +0 -23
- package/dist/web-dist/assets/index-BtqHnOp0.js.map +0 -1
- package/dist/web-dist/assets/index-ktDWx_UP.css +0 -2
- package/dist/web-dist/assets/mermaid-GHXKKRXX-DjR37lQN.js +0 -1
- package/dist/web-dist/assets/popover-Cm3PsrMh.js +0 -1
- package/dist/web-dist/assets/popover-Cm3PsrMh.js.map +0 -1
- package/dist/web-dist/assets/query-runner-6BhVPaC9.js +0 -9
- package/dist/web-dist/assets/query-runner-6BhVPaC9.js.map +0 -1
- package/dist/web-dist/assets/radix-ui-BUv2Nuc1.js +0 -51
- package/dist/web-dist/assets/radix-ui-BUv2Nuc1.js.map +0 -1
- package/dist/web-dist/assets/redis-value-CJoRCZun.js +0 -1
- package/dist/web-dist/assets/runner-CZKDzRoU.js +0 -1
- package/dist/web-dist/assets/schema-cZHeTHA0.js +0 -64
- package/dist/web-dist/assets/schema-cZHeTHA0.js.map +0 -1
- package/dist/web-dist/assets/tanstack-D6l4LIt3.js +0 -10
- package/dist/web-dist/assets/tanstack-D6l4LIt3.js.map +0 -1
- package/dist/web-dist/assets/toggle-group-BRWnLosM.js +0 -1
- package/dist/web-dist/assets/toggle-group-BRWnLosM.js.map +0 -1
- package/dist/web-dist/assets/tooltip-DinwklI8.js +0 -9
- package/dist/web-dist/assets/tooltip-DinwklI8.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{kt as o}from"./radix-ui-CgJOuzDv.js";import{n as t}from"./index-XoXxezo4.js";import{t as e}from"./query-runner-Bgs0j50a.js";var n=o();function i(){const{queryId:r}=t.useParams();return(0,n.jsx)(e,{queryId:r})}export{i as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_queryId-
|
|
1
|
+
{"version":3,"file":"_queryId-Chj34V6b.js","names":["RunnerScreen","Route","RouteComponent","queryId","useParams","component"],"sources":["../../src/routes/_pathlessLayout/runner/$queryId.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute } from \"@tanstack/react-router\";\nimport { RunnerScreen } from \"@/features/query-runner\";\n\nexport const Route = createFileRoute(\"/_pathlessLayout/runner/$queryId\")({\n\tcomponent: RouteComponent,\n});\n\nfunction RouteComponent() {\n\tconst { queryId } = Route.useParams();\n\treturn <RunnerScreen queryId={queryId} />;\n}\n"],"mappings":"6IAOA,SAASE,GAAiB,CACzB,KAAM,CAAEC,QAAAA,CAAAA,EAAYF,EAAMG,UAAU,EACpC,SAAO,EAAA,KAAC,EAAD,CAAuBD,QAAAA,CAAQ,CAAA,CACvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{kt as r}from"./radix-ui-
|
|
1
|
+
import{kt as r}from"./radix-ui-CgJOuzDv.js";import{t}from"./schema-CtOicTEt.js";import{t as m}from"./index-XoXxezo4.js";var e=r();function i(){const{table:o}=m.useParams();return(0,e.jsx)("main",{className:"flex-1 flex flex-col overflow-hidden",children:(0,e.jsx)(t,{tableName:o})})}export{i as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_table-
|
|
1
|
+
{"version":3,"file":"_table-Bg4NvAi4.js","names":["SchemaScreen","Route","RouteComponent","table","useParams","component"],"sources":["../../src/routes/_pathlessLayout/schema/$table.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute } from \"@tanstack/react-router\";\nimport { SchemaScreen } from \"@/features/schema\";\n\nexport const Route = createFileRoute(\"/_pathlessLayout/schema/$table\")({\n\tcomponent: RouteComponent,\n});\n\nfunction RouteComponent() {\n\tconst { table } = Route.useParams();\n\n\treturn (\n\t\t<main className=\"flex-1 flex flex-col overflow-hidden\">\n\t\t\t<SchemaScreen tableName={table} />\n\t\t</main>\n\t);\n}\n"],"mappings":"kIAOA,SAASE,GAAiB,CACzB,KAAM,CAAEC,MAAAA,CAAAA,EAAUF,EAAMG,UAAU,EAElC,SACC,EAAA,KAAC,OAAD,CAAM,UAAU,uCACf,YAAA,EAAA,KAAC,EAAD,CAAc,UAAWD,CAAM,CAAA,CAC1B,CAAA,CAER"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{kt as t}from"./radix-ui-CgJOuzDv.js";import{u as r,y as o}from"./index-XoXxezo4.js";var a=t();function s(){const{table:e}=o.useParams();return(0,a.jsx)(r,{tableName:e})}export{s as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_table-
|
|
1
|
+
{"version":3,"file":"_table-CHFe8v3q.js","names":["TableScreen","Route","RouteComponent","table","useParams","component"],"sources":["../../src/routes/_pathlessLayout/table/$table.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute } from \"@tanstack/react-router\";\nimport { TableScreen } from \"@/features/tables\";\n\nexport const Route = createFileRoute(\"/_pathlessLayout/table/$table\")({\n\tcomponent: RouteComponent,\n});\n\nfunction RouteComponent() {\n\tconst { table } = Route.useParams();\n\treturn <TableScreen tableName={table} />;\n}\n"],"mappings":"qGAOA,SAASE,GAAiB,CACzB,KAAM,CAAEC,MAAAA,CAAAA,EAAUF,EAAMG,UAAU,EAClC,SAAO,EAAA,KAAC,EAAD,CAAa,UAAWD,CAAM,CAAA,CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as s}from"./react-Cxb6eTZp.js";var n=s()(t=>({pendingPrompt:null,requestAssistant:e=>t({pendingPrompt:e}),consumePrompt:()=>t({pendingPrompt:null})}));export{n as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-assistant-D2oc4TtC.js","names":[],"sources":["../../src/features/ai-assistant/stores/assistant-request.store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\ninterface AssistantRequestStore {\n\tpendingPrompt: string | null;\n\trequestAssistant: (prompt: string) => void;\n\tconsumePrompt: () => void;\n}\n\nexport const useAssistantRequestStore = create<AssistantRequestStore>()((set) => ({\n\tpendingPrompt: null,\n\trequestAssistant: (pendingPrompt) => set({ pendingPrompt }),\n\tconsumePrompt: () => set({ pendingPrompt: null }),\n}));\n"],"mappings":"wCAQA,IAAa,EAA2B,EAA8B,EAAG,IAAS,CACjF,cAAe,KACf,iBAAmB,GAAkB,EAAI,CAAE,cAAA,CAAc,CAAC,EAC1D,cAAA,IAAqB,EAAI,CAAE,cAAe,IAAK,CAAC,CACjD,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as me}from"./rolldown-runtime-CXHxssQy.js";import{b as he}from"./tanstack-g1w0v_rJ.js";import{H as xe,_ as ee,a as M,b as fe,et as be,g as ge,h as J,m as pe,s as ve,st as je,ut as ye,z as Ne}from"./icons-CtpwLWUI.js";import{kt as ke}from"./radix-ui-CgJOuzDv.js";import{n as we}from"./dist-DBHqGKiG.js";import{C as Se,H as Ce,R as Re,U as q,i as Z,n as Q,t as W,z as m}from"./tooltip-B7gM8xl3.js";import{a as De,d as I,i as Me,r as Le,t as _e}from"./popover-wbdw6Hmx.js";import{t as Te}from"./database.store-O79u8Fhj.js";import{F as ie,O as $e,P as Fe,_ as Ee,c as Oe,d as ze,f as Ae,g as Pe,h as Be,j as B,l as X,m as Je,o as Y,p as Ie,s as Ke,v as He}from"./index-XoXxezo4.js";import{t as Ue}from"./queries.store-CoJVGJKk.js";import{a as Ge,i as Ve,n as S,o as qe,r as Ze,t as D}from"./redis-value-COa6YzFD.js";var g=me(ye(),1),e=ke(),le=({text:s,label:r,className:t})=>{const[a,i]=(0,g.useState)(!1);return(0,e.jsxs)(W,{children:[(0,e.jsx)(Z,{asChild:!0,children:(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:t,onClick:async()=>{await navigator.clipboard.writeText(typeof s=="function"?s():s),i(!0),window.setTimeout(()=>i(!1),1200)},"aria-label":r,children:a?(0,e.jsx)(be,{}):(0,e.jsx)(xe,{})})}),(0,e.jsx)(Q,{children:a?"Copied":r})]})},Qe=[{label:"60s",ttlMs:6e4},{label:"10m",ttlMs:6e5},{label:"1h",ttlMs:36e5},{label:"1d",ttlMs:864e5},{label:"7d",ttlMs:6048e5}],We=s=>s===null?"—":s<1024?`${s} B`:s<1048576?`${(s/1024).toFixed(1)} KiB`:`${(s/1048576).toFixed(1)} MiB`,Xe=s=>{const r=Math.max(0,Math.ceil(s/1e3));if(r<60)return`${r}s`;const t=Math.floor(r/60);if(t<60)return`${t}m ${r%60}s`;const a=Math.floor(t/60);return a<24?`${a}h ${t%60}m`:`${Math.floor(a/24)}d ${a%24}h`},Ye=s=>{const[,r]=(0,g.useReducer)(t=>t+1,0);(0,g.useEffect)(()=>{if(!s)return;const t=window.setInterval(r,1e3);return()=>window.clearInterval(t)},[s])},es=({detail:s,label:r,loadedCount:t,fetchedAt:a,isFetching:i,pending:n,command:l,act:o,onRefresh:d,onOpenRunner:h,onDelete:c})=>{const[b,u]=(0,g.useState)(s.key),[L,H]=(0,g.useState)("");Ye(s.ttlMs>0);const C=s.ttlMs>0?s.ttlMs-(Date.now()-a):s.ttlMs,N=s.ttlMs===-1,x=!N&&C<=0,A=[{name:"TTL",value:N?"∞":x?"expired":Xe(C),hint:N?"No expiry — the key persists until deleted.":x?"The TTL elapsed; Redis has evicted or is about to evict this key.":`Expires ${new Date(a+s.ttlMs).toLocaleString()}`,alert:x},{name:"MEM",value:We(s.memoryBytes),hint:"Approximate memory usage reported by MEMORY USAGE."},{name:"LEN",value:s.length?.toLocaleString()??"—",hint:t!==null&&s.length!==null?`${t.toLocaleString()} of ${s.length.toLocaleString()} entries loaded in this view.`:"Length reported by Redis (entries, or bytes for strings)."},{name:"REV",value:s.revision.slice(0,8),hint:"Optimistic-lock revision. Writes are rejected when the key changed since this fetch."},{name:"AT",value:new Date(a).toLocaleTimeString(),hint:"When this snapshot was fetched from Redis."}];return(0,e.jsx)("header",{className:"border-b border-border",children:(0,e.jsxs)("div",{className:"flex min-h-10 items-stretch justify-between",children:[(0,e.jsxs)("div",{className:"flex min-w-0 flex-1 items-stretch overflow-x-auto",children:[(0,e.jsxs)("div",{className:"flex min-w-0 max-w-[min(36rem,45vw)] shrink items-center gap-1 px-3 py-1.5",children:[(0,e.jsx)("code",{className:"min-w-0 truncate text-sm text-foreground",title:r,children:r||"(empty key)"}),(0,e.jsx)(le,{text:s.key.utf8??s.key.base64,label:"Copy key name",className:"shrink-0 text-muted-foreground"}),(0,e.jsx)(De,{variant:"outline",className:"h-4 shrink-0 rounded-sm px-1.5 text-[8px] font-normal uppercase text-muted-foreground",children:s.type})]}),A.map(({name:v,value:P,hint:_,alert:T},$)=>(0,e.jsxs)(W,{children:[(0,e.jsx)(Z,{asChild:!0,children:(0,e.jsxs)("div",{className:q("flex shrink-0 cursor-default items-center gap-1.5 border-s border-border px-2 text-[10px]",$===A.length-1&&"border-e"),children:[(0,e.jsx)("span",{className:"text-muted-foreground",children:v}),(0,e.jsx)("span",{className:q("tabular-nums",T?"text-destructive":"text-foreground"),children:P})]})}),(0,e.jsx)(Q,{side:"bottom",children:_})]},v))]}),(0,e.jsxs)("div",{className:"flex shrink-0 items-stretch",children:[(0,e.jsxs)(m,{variant:"ghost",size:"sm",className:"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2",onClick:d,disabled:i,children:[(0,e.jsx)(ge,{className:q(i&&"animate-spin")})," Refresh"]}),l&&(0,e.jsxs)(W,{children:[(0,e.jsx)(Z,{asChild:!0,children:(0,e.jsxs)(m,{variant:"ghost",size:"sm",className:"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2",onClick:h,children:[(0,e.jsx)(ve,{})," Runner"]})}),(0,e.jsxs)(Q,{side:"bottom",children:["Open in the query runner as ",(0,e.jsx)("code",{className:"font-mono",children:l})]})]}),(0,e.jsxs)(_e,{children:[(0,e.jsx)(Me,{asChild:!0,children:(0,e.jsxs)(m,{variant:"ghost",size:"sm",className:"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2",children:[(0,e.jsx)(fe,{})," Edit"]})}),(0,e.jsx)(Le,{align:"end",className:"w-72 rounded-sm p-3 font-mono",children:(0,e.jsxs)(Oe,{className:"gap-4",children:[(0,e.jsxs)(Y,{className:"gap-1.5",children:[(0,e.jsx)(X,{htmlFor:"redis-key-rename",children:"Rename key"}),(0,e.jsxs)("form",{className:"flex",onSubmit:v=>{v.preventDefault(),!(b===null||b.base64===s.key.base64||n)&&o({action:"rename",newKey:b})},children:[(0,e.jsx)(S,{id:"redis-key-rename",value:b,onChange:u,className:"rounded-e-none font-mono"}),(0,e.jsx)(m,{type:"submit",size:"icon",className:"h-auto self-stretch rounded-s-none border-0 bg-clip-border",disabled:b===null||b.base64===s.key.base64||n,"aria-label":"Rename key",children:(0,e.jsx)(je,{})})]})]}),(0,e.jsxs)(Y,{className:"gap-1.5",children:[(0,e.jsx)(X,{htmlFor:"redis-key-ttl-operation",children:"TTL in seconds"}),(0,e.jsxs)("form",{className:"flex",onSubmit:v=>{v.preventDefault(),!n&&o({action:"setTtl",ttlMs:L?Number(L)*1e3:null})},children:[(0,e.jsx)(I,{id:"redis-key-ttl-operation",type:"number",min:1,value:L,onChange:v=>H(v.target.value),placeholder:s.ttlMs>0?String(Math.ceil(s.ttlMs/1e3)):"Persistent",className:"rounded-e-none font-mono"}),(0,e.jsx)(m,{type:"submit",className:"h-auto self-stretch rounded-s-none border-0 bg-clip-border",disabled:n,children:"Apply"})]}),(0,e.jsxs)("div",{className:"flex flex-wrap gap-1",children:[Qe.map(v=>(0,e.jsx)(m,{type:"button",variant:"outline",size:"xs",disabled:n,onClick:()=>o({action:"setTtl",ttlMs:v.ttlMs}),children:v.label},v.label)),(0,e.jsx)(m,{type:"button",variant:"outline",size:"xs",disabled:n||N,onClick:()=>o({action:"setTtl",ttlMs:null}),children:"Persist"})]}),(0,e.jsx)(Ke,{children:"Leave empty to persist the key."})]})]})})]}),(0,e.jsxs)(m,{variant:"destructive",size:"sm",className:"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2",disabled:n,onClick:c,children:[(0,e.jsx)(M,{})," Delete"]})]})]})})},w=Ve(""),j=s=>s.utf8??`base64:${s.base64}`,F=(s,...r)=>r.some(t=>String(t).toLowerCase().includes(s)),E=s=>{switch(s.kind){case"string":return JSON.stringify(j(s.value),null,2);case"hash":return JSON.stringify(Object.fromEntries(s.entries.map(r=>[j(r.field),j(r.value)])),null,2);case"list":return JSON.stringify(s.entries.map(r=>j(r.value)),null,2);case"set":return JSON.stringify(s.members.map(j),null,2);case"zset":return JSON.stringify(s.entries.map(r=>({member:j(r.member),score:r.score})),null,2);case"stream":return JSON.stringify(s.entries.map(r=>({id:r.id,fields:Object.fromEntries(r.fields.map(t=>[j(t.field),j(t.value)]))})),null,2);default:return""}},K=(s,r)=>{const[t,a]=(0,g.useState)(s),[i,n]=(0,g.useState)(r);return i!==r&&(n(r),a(s)),[t,a]},O=({filtered:s})=>(0,e.jsx)("div",{className:"flex h-10 items-center justify-center px-2 text-xs text-muted-foreground",children:s?"No loaded entries match the filter.":"No entries loaded."}),z=({filter:s,onFilterChange:r,shownCount:t,loadedCount:a,totalCount:i,copyJson:n,extra:l,header:o,children:d,footer:h})=>(0,e.jsxs)("div",{className:"overflow-hidden border border-border",children:[(0,e.jsxs)("div",{className:"flex h-8 items-center gap-1 border-b border-border bg-muted/10 px-1",children:[(0,e.jsx)(pe,{className:"ms-1 size-3 shrink-0 text-muted-foreground"}),(0,e.jsx)(I,{variant:"outline",value:s,onChange:c=>r(c.target.value),placeholder:"Filter loaded entries","aria-label":"Filter loaded entries",className:"h-6 flex-1 rounded-none border-none px-1 font-mono focus-visible:ring-0"}),l,(0,e.jsx)("span",{className:"shrink-0 px-1 text-[10px] tabular-nums text-muted-foreground",children:s?`${t} / ${a}`:i!==null&&i>a?`${a.toLocaleString()} of ${i.toLocaleString()}`:a.toLocaleString()}),(0,e.jsx)(le,{text:n,label:"Copy loaded entries as JSON"})]}),o,d,h]}),se=({labels:s,onSave:r,pending:t,score:a=!1})=>{const[i,n]=(0,g.useState)(w),[l,o]=(0,g.useState)(w),[d,h]=(0,g.useState)("0"),c=i===null||!a&&l===null,b=async u=>{u.preventDefault(),!(i===null||l===null)&&await r(i,a?Number(d):l)&&(n(w),o(w),h("0"))};return(0,e.jsxs)("form",{onSubmit:b,className:"grid grid-cols-[1fr_1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2",children:[(0,e.jsx)(S,{value:i,onChange:n,placeholder:s[0]}),a?(0,e.jsx)(I,{type:"number",step:"any",value:d,onChange:u=>h(u.target.value),placeholder:s[1],className:"font-mono"}):(0,e.jsx)(S,{value:l,onChange:o,placeholder:s[1]}),(0,e.jsxs)(m,{type:"submit",disabled:t||c,children:[(0,e.jsx)(ee,{})," Add"]})]})},ss=({value:s,truncated:r,act:t,pending:a,onLoadFull:i,onDownload:n})=>{const[l,o]=K(s,s.base64),d=l!==null&&l.base64!==s.base64;return(0,e.jsxs)("div",{className:"space-y-3",children:[r&&(0,e.jsxs)("div",{className:"grid gap-2 border-b border-border pb-3 sm:grid-cols-[1fr_auto] sm:items-center",children:[(0,e.jsx)(B,{variant:"warning",title:"Preview truncated",message:"Load the full value before editing (up to the 8 MiB response cap).",className:"rounded-sm"}),(0,e.jsx)(m,{variant:"outline",onClick:i,children:"Load full"})]}),(0,e.jsx)(D,{value:s,onDownload:n}),(0,e.jsxs)("form",{onSubmit:h=>{h.preventDefault(),l!==null&&d&&t({action:"setString",value:l})},className:"border border-border p-3",children:[(0,e.jsxs)(Y,{className:"gap-1.5",children:[(0,e.jsx)(X,{htmlFor:"redis-string-value",children:"Replace value"}),(0,e.jsx)(S,{id:"redis-string-value",value:l,onChange:o})]}),(0,e.jsxs)(m,{type:"submit",className:"mt-3",disabled:r||a||!d,children:[(0,e.jsx)(J,{})," Save value"]})]})]})},rs=({field:s,value:r,act:t,pending:a})=>{const[i,n]=K(r,r.base64),l=i!==null&&i.base64!==r.base64;return(0,e.jsxs)("div",{className:"grid min-h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1",children:[(0,e.jsx)(D,{value:s,compact:!0}),(0,e.jsx)(S,{value:i,onChange:n}),(0,e.jsxs)("div",{className:"flex",children:[(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:l?"text-primary":void 0,disabled:a||!l,onClick:()=>i!==null&&t({action:"upsertHash",field:s,value:i}),"aria-label":"Save hash value",children:(0,e.jsx)(J,{})}),(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:"text-muted-foreground hover:text-destructive",disabled:a,onClick:()=>t({action:"deleteHash",field:s}),"aria-label":"Delete hash field",children:(0,e.jsx)(M,{})})]})]})},ts=({value:s,total:r,act:t,pending:a})=>{const[i,n]=(0,g.useState)(""),l=i.trim().toLowerCase(),o=l?s.entries.filter(d=>F(l,j(d.field),j(d.value))):s.entries;return(0,e.jsx)(z,{filter:i,onFilterChange:n,shownCount:o.length,loadedCount:s.entries.length,totalCount:r,copyJson:()=>E(s),header:(0,e.jsxs)("div",{className:"grid h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground",children:[(0,e.jsx)("span",{children:"Field"}),(0,e.jsx)("span",{children:"Value"}),(0,e.jsx)("span",{})]}),footer:(0,e.jsx)(se,{labels:["field","value"],pending:a,onSave:(d,h)=>t({action:"upsertHash",field:d,value:h})}),children:o.length===0?(0,e.jsx)(O,{filtered:l.length>0}):o.map(d=>(0,e.jsx)(rs,{field:d.field,value:d.value,act:t,pending:a},d.field.base64))})},as=({index:s,value:r,revision:t,act:a,pending:i})=>{const[n,l]=K(r,`${t}:${r.base64}`),o=n!==null&&n.base64!==r.base64;return(0,e.jsxs)("div",{className:"grid min-h-8 grid-cols-[3rem_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1",children:[(0,e.jsx)("code",{className:"text-xs text-muted-foreground",children:s}),(0,e.jsx)(S,{value:n,onChange:l}),(0,e.jsxs)("div",{className:"flex",children:[(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:o?"text-primary":void 0,disabled:i||!o,onClick:()=>n!==null&&a({action:"setList",index:s,value:n}),"aria-label":`Save list item ${s}`,children:(0,e.jsx)(J,{})}),(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:"text-muted-foreground hover:text-destructive",disabled:i,onClick:()=>a({action:"deleteList",index:s}),"aria-label":`Delete list item ${s}`,children:(0,e.jsx)(M,{})})]})]})},ns=({value:s,revision:r,total:t,act:a,pending:i})=>{const[n,l]=(0,g.useState)(""),[o,d]=(0,g.useState)(w),h=n.trim().toLowerCase(),c=h?s.entries.filter(u=>F(h,u.index,j(u.value))):s.entries,b=async u=>{o!==null&&await a({action:"pushList",side:u,value:o})&&d(w)};return(0,e.jsx)(z,{filter:n,onFilterChange:l,shownCount:c.length,loadedCount:s.entries.length,totalCount:t,copyJson:()=>E(s),header:(0,e.jsxs)("div",{className:"grid h-8 grid-cols-[3rem_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground",children:[(0,e.jsx)("span",{children:"Index"}),(0,e.jsx)("span",{children:"Value"}),(0,e.jsx)("span",{})]}),footer:(0,e.jsxs)("form",{onSubmit:u=>{u.preventDefault(),b("right")},className:"grid grid-cols-[1fr_auto_auto] items-center gap-2 border-t border-border bg-muted/10 p-2",children:[(0,e.jsx)(S,{value:o,onChange:d,placeholder:"new item"}),(0,e.jsx)(m,{type:"button",variant:"outline",disabled:i||o===null,onClick:()=>b("left"),children:"Push left"}),(0,e.jsx)(m,{type:"submit",disabled:i||o===null,children:"Push right"})]}),children:c.length===0?(0,e.jsx)(O,{filtered:h.length>0}):c.map(u=>(0,e.jsx)(as,{index:u.index,value:u.value,revision:r,act:a,pending:i},u.index))})},is=({value:s,total:r,act:t,pending:a})=>{const[i,n]=(0,g.useState)(""),[l,o]=(0,g.useState)(w),d=i.trim().toLowerCase(),h=d?s.members.filter(c=>F(d,j(c))):s.members;return(0,e.jsx)(z,{filter:i,onFilterChange:n,shownCount:h.length,loadedCount:s.members.length,totalCount:r,copyJson:()=>E(s),header:(0,e.jsxs)("div",{className:"grid h-8 grid-cols-[1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground",children:[(0,e.jsx)("span",{children:"Member"}),(0,e.jsx)("span",{})]}),footer:(0,e.jsxs)("form",{onSubmit:async c=>{c.preventDefault(),l!==null&&await t({action:"addSet",member:l})&&o(w)},className:"grid grid-cols-[1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2",children:[(0,e.jsx)(S,{value:l,onChange:o,placeholder:"new member"}),(0,e.jsxs)(m,{type:"submit",disabled:a||l===null,children:[(0,e.jsx)(ee,{})," Add member"]})]}),children:h.length===0?(0,e.jsx)(O,{filtered:d.length>0}):h.map(c=>(0,e.jsxs)("div",{className:"flex min-h-8 items-center gap-2 border-b border-border px-2 py-1",children:[(0,e.jsx)("span",{className:"min-w-0 flex-1",children:(0,e.jsx)(D,{value:c,compact:!0})}),(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:"text-muted-foreground hover:text-destructive",disabled:a,onClick:()=>t({action:"removeSet",member:c}),"aria-label":"Remove set member",children:(0,e.jsx)(M,{})})]},c.base64))})},ls=({member:s,score:r,act:t,pending:a})=>{const[i,n]=K(String(r),String(r)),l=Number(i),o=i!==String(r);return(0,e.jsxs)("div",{className:"grid min-h-8 grid-cols-[1fr_8rem_5rem] items-center gap-2 border-b border-border px-2 py-1",children:[(0,e.jsx)(D,{value:s,compact:!0}),(0,e.jsx)(I,{inputMode:"decimal",value:i,onChange:d=>n(d.target.value),"aria-label":"Sorted set score",className:"font-mono"}),(0,e.jsxs)("div",{className:"flex",children:[(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:o?"text-primary":void 0,disabled:a||!o||!Number.isFinite(l),onClick:()=>t({action:"upsertZset",member:s,score:l}),"aria-label":"Save sorted set score",children:(0,e.jsx)(J,{})}),(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:"text-muted-foreground hover:text-destructive",disabled:a,onClick:()=>t({action:"removeZset",member:s}),"aria-label":"Remove sorted set member",children:(0,e.jsx)(M,{})})]})]})},os=({value:s,total:r,act:t,pending:a})=>{const[i,n]=(0,g.useState)(""),l=i.trim().toLowerCase(),o=l?s.entries.filter(d=>F(l,j(d.member),d.score)):s.entries;return(0,e.jsx)(z,{filter:i,onFilterChange:n,shownCount:o.length,loadedCount:s.entries.length,totalCount:r,copyJson:()=>E(s),header:(0,e.jsxs)("div",{className:"grid h-8 grid-cols-[1fr_8rem_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground",children:[(0,e.jsx)("span",{children:"Member"}),(0,e.jsx)("span",{children:"Score"}),(0,e.jsx)("span",{})]}),footer:(0,e.jsx)(se,{score:!0,labels:["member","score"],pending:a,onSave:(d,h)=>t({action:"upsertZset",member:d,score:h})}),children:o.length===0?(0,e.jsx)(O,{filtered:l.length>0}):o.map(d=>(0,e.jsx)(ls,{member:d.member,score:d.score,act:t,pending:a},d.member.base64))})},ds=({value:s,total:r,act:t,pending:a,direction:i,onDirectionChange:n})=>{const[l,o]=(0,g.useState)(""),d=l.trim().toLowerCase(),h=d?s.entries.filter(c=>F(d,c.id,...c.fields.flatMap(b=>[j(b.field),j(b.value)]))):s.entries;return(0,e.jsx)(z,{filter:l,onFilterChange:o,shownCount:h.length,loadedCount:s.entries.length,totalCount:r,copyJson:()=>E(s),extra:(0,e.jsxs)(Fe,{type:"single",variant:"ghost",size:"sm",value:i,onValueChange:c=>c&&n(c),"aria-label":"Stream order",className:"shrink-0 rounded-none",children:[(0,e.jsx)(ie,{value:"forward",className:"h-6 rounded-none px-2 text-[10px]",children:"Oldest first"}),(0,e.jsx)(ie,{value:"backward",className:"h-6 rounded-none px-2 text-[10px]",children:"Newest first"})]}),header:(0,e.jsxs)("div",{className:"grid h-8 grid-cols-[10rem_1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground",children:[(0,e.jsx)("span",{children:"Entry ID"}),(0,e.jsx)("span",{children:"Fields"}),(0,e.jsx)("span",{})]}),footer:(0,e.jsx)(se,{labels:["field","value"],pending:a,onSave:(c,b)=>t({action:"appendStream",id:"*",fields:[{field:c,value:b}]})}),children:h.length===0?(0,e.jsx)(O,{filtered:d.length>0}):h.map(c=>(0,e.jsxs)("div",{className:"grid grid-cols-[10rem_1fr_2rem] border-b border-border",children:[(0,e.jsx)("code",{className:"border-e border-border px-2 py-1.5 text-xs text-primary",children:c.id}),(0,e.jsx)("div",{className:"grid gap-1 px-2 py-1.5",children:c.fields.map(b=>(0,e.jsxs)("div",{className:"grid grid-cols-[minmax(8rem,0.4fr)_1fr] gap-3",children:[(0,e.jsx)(D,{value:b.field,compact:!0}),(0,e.jsx)(D,{value:b.value,compact:!0})]},b.field.base64))}),(0,e.jsx)(m,{variant:"ghost",size:"icon-sm",className:"rounded-none text-muted-foreground hover:text-destructive",disabled:a,onClick:()=>t({action:"deleteStream",id:c.id}),"aria-label":`Delete stream entry ${c.id}`,children:(0,e.jsx)(M,{})})]},c.id))})},cs=({detail:s,act:r,pending:t,onLoadFull:a,onDownload:i,streamDirection:n,onStreamDirectionChange:l})=>{const o=s.value;switch(o.kind){case"string":return(0,e.jsx)(ss,{value:o.value,truncated:o.truncated,act:r,pending:t,onLoadFull:a,onDownload:i});case"hash":return(0,e.jsx)(ts,{value:o,total:s.length,act:r,pending:t});case"list":return(0,e.jsx)(ns,{value:o,revision:s.revision,total:s.length,act:r,pending:t});case"set":return(0,e.jsx)(is,{value:o,total:s.length,act:r,pending:t});case"zset":return(0,e.jsx)(os,{value:o,total:s.length,act:r,pending:t});case"stream":return(0,e.jsx)(ds,{value:o,total:s.length,act:r,pending:t,direction:n,onDirectionChange:l});default:return(0,e.jsx)(B,{variant:"info",title:"Metadata only",message:"This Redis module type cannot be edited in this release.",className:"rounded-sm"})}},us=s=>{if(s.key.utf8===void 0)return null;const r=JSON.stringify(s.key.utf8);switch(s.type){case"string":return`GET ${r}`;case"hash":return`HGETALL ${r}`;case"list":return`LRANGE ${r} 0 -1`;case"set":return`SMEMBERS ${r}`;case"zset":return`ZRANGE ${r} 0 -1 WITHSCORES`;case"stream":return`XRANGE ${r} - + COUNT 100`;default:return null}},ms=s=>{switch(s.value.kind){case"hash":case"list":case"zset":case"stream":return s.value.entries.length;case"set":return s.value.members.length;default:return null}},hs=()=>{const s=he(),{selectedDatabase:r}=Te(),{openOverlay:t}=Re(),{addQuery:a,updateQuery:i}=Ue(),[n,l]=$e("key"),[o,d]=(0,g.useState)(null),[h,c]=(0,g.useState)(null),b=h===n?"backward":"forward",u=Ge(n,o===n,b),{applyAction:L,deleteKey:H,isPending:C}=qe(),N=u.data?.pages,x=N?.[0],[A,v]=(0,g.useState)(!1),[P,_]=(0,g.useState)(null),T=x?.key.utf8??(x?`binary:${x.key.base64}`:""),$=x?us(x):null,R=(0,g.useMemo)(()=>{if(!x||!N||N.length===1)return x;const f=structuredClone(x);for(const p of N.slice(1))f.value.kind==="hash"&&p.value.kind==="hash"&&f.value.entries.push(...p.value.entries),f.value.kind==="list"&&p.value.kind==="list"&&f.value.entries.push(...p.value.entries),f.value.kind==="set"&&p.value.kind==="set"&&f.value.members.push(...p.value.members),f.value.kind==="zset"&&p.value.kind==="zset"&&f.value.entries.push(...p.value.entries),f.value.kind==="stream"&&p.value.kind==="stream"&&f.value.entries.push(...p.value.entries);return f},[x,N]),U=R?ms(R):null,G=async(f,p=!1)=>{if(!n||!x)return!1;try{const k=await L(n,{expectedRevision:x.revision,force:p,operation:f});return _(null),k.deleted?l(null):(k.key.base64||"-")!==n&&l(k.key.base64||"-"),!0}catch(k){return k.status===409&&_(f),!1}},oe=()=>{if(!$)return;const f=a(`redis: ${T.slice(0,40)}`);i(f,{query:$}),s({to:"/runner/$queryId",params:{queryId:f}})},de=async()=>{if(!n||!x||x.type!=="string")return;const f=[];let p=0,k=!0;for(;k;){const y=(await Se({db:r??"",key:n,offset:p,expectedRevision:x.revision})).data.data;if(f.push(Ze(y.chunk.base64)),k=y.hasMore,y.hasMore&&(y.nextOffset===null||y.nextOffset<=p))throw new Error("Redis download made no forward progress");p=y.nextOffset??p}const ce=f.reduce((y,ue)=>y+ue.length,0),te=new Uint8Array(ce);let ae=0;for(const y of f)te.set(y,ae),ae+=y.length;const ne=URL.createObjectURL(new Blob([te.buffer])),V=document.createElement("a");V.href=ne,V.download=`${x.key.utf8?.replaceAll(/[^\w.-]+/g,"_")||"redis-value"}.bin`,V.click(),URL.revokeObjectURL(ne)};if(!n)return(0,e.jsx)("main",{className:"flex flex-1 items-center justify-center bg-background font-mono",children:(0,e.jsxs)("div",{className:"max-w-sm text-center",children:[(0,e.jsx)(Ne,{className:"mx-auto mb-3 size-5 text-muted-foreground"}),(0,e.jsx)("h1",{className:"text-sm font-medium text-foreground",children:"No key selected"}),(0,e.jsx)("p",{className:"mt-1 text-xs leading-5 text-muted-foreground",children:"Select a key from the navigator or create one to inspect its value."}),(0,e.jsxs)(m,{variant:"outline",className:"mt-3",onClick:()=>t("redis-browser.create-key"),children:[(0,e.jsx)(ee,{})," Create key"]})]})});if(u.isLoading)return(0,e.jsx)("main",{className:"flex flex-1 items-center justify-center",children:(0,e.jsx)(Ce,{})});if(u.error||!x||!R)return(0,e.jsx)("main",{className:"flex flex-1 items-center justify-center bg-background p-4",children:(0,e.jsxs)("div",{className:"w-full max-w-md text-center",children:[(0,e.jsx)(B,{variant:"error",title:"Unable to load key",message:u.error?.message??"The key no longer exists.",className:"rounded-sm text-start"}),(0,e.jsx)(m,{variant:"outline",className:"mt-4",onClick:()=>l(null),children:"Back to keyspace"})]})});const re=R.length!==null&&U!==null?Math.max(0,R.length-U):null;return(0,e.jsxs)("main",{className:"flex min-h-0 flex-1 flex-col bg-background font-mono",children:[(0,e.jsx)(es,{detail:x,label:T,loadedCount:U,fetchedAt:u.dataUpdatedAt,isFetching:u.isFetching,pending:C,command:$,act:G,onRefresh:()=>u.refetch(),onOpenRunner:oe,onDelete:()=>v(!0)},x.key.base64),(0,e.jsx)(ze,{open:A,onOpenChange:v,children:(0,e.jsxs)(Je,{children:[(0,e.jsxs)(Ee,{children:[(0,e.jsx)(He,{children:"Delete key"}),(0,e.jsxs)(Be,{children:["Are you sure you want to delete"," ",(0,e.jsx)("code",{className:"break-all text-foreground",children:T||"(empty key)"}),"? This action cannot be undone."]})]}),(0,e.jsxs)(Pe,{children:[(0,e.jsx)(Ie,{children:"Cancel"}),(0,e.jsx)(Ae,{variant:"destructive",onClick:async()=>{v(!1);try{await H(n,x.revision),l(null)}catch{}},children:"Delete"})]})]})}),P&&(0,e.jsxs)("div",{className:"grid gap-2 border-b border-border p-2 sm:grid-cols-[1fr_auto] sm:items-center",children:[(0,e.jsx)(B,{variant:"warning",title:"Revision conflict",message:"This key changed on the server. Reload it or overwrite with the pending change.",className:"rounded-sm py-2"}),(0,e.jsxs)("div",{className:"flex gap-2",children:[(0,e.jsx)(m,{variant:"outline",onClick:()=>{_(null),u.refetch()},children:"Reload"}),(0,e.jsx)(m,{disabled:C,onClick:()=>G(P,!0),children:"Overwrite"})]})]}),(0,e.jsx)("div",{className:"min-h-0 flex-1 overflow-auto",children:(0,e.jsxs)("section",{className:"p-3",children:[(0,e.jsx)(cs,{detail:R,act:G,pending:C,onLoadFull:()=>d(n),onDownload:()=>{we.promise(de(),{loading:"Downloading original Redis bytes…",success:"Redis value downloaded",error:f=>f.message||"Download failed"})},streamDirection:b,onStreamDirectionChange:f=>c(f==="backward"?n:null)},x.key.base64),u.hasNextPage&&(0,e.jsx)(m,{variant:"outline",className:"mt-3 w-full",onClick:()=>u.fetchNextPage(),disabled:u.isFetchingNextPage,children:u.isFetchingNextPage?"Loading…":x.type==="stream"&&b==="backward"?"Load 100 older entries":`Load next 100${re!==null?` (${re.toLocaleString()} remaining)`:""}`})]})})]})},Ss=hs;export{Ss as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-CFsrLhq1.js","names":["RedisBrowserScreen","SplitComponent","component"],"sources":["../../src/features/redis-browser/components/redis-copy-button.tsx","../../src/features/redis-browser/components/redis-key-header.tsx","../../src/features/redis-browser/components/redis-value-editors.tsx","../../src/features/redis-browser/screens/redis-browser-screen.tsx","../../src/routes/_pathlessLayout/browser.tsx?tsr-split=component"],"sourcesContent":["import { Button } from \"@db-studio/ui/button\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@db-studio/ui/tooltip\";\nimport { Check, Copy } from \"lucide-react\";\nimport { useState } from \"react\";\n\n/**\n * Icon button that copies `text` (or its lazy producer) to the clipboard and\n * flashes a check mark. Used for key names, values, and JSON exports.\n */\nexport const RedisCopyButton = ({\n\ttext,\n\tlabel,\n\tclassName,\n}: {\n\ttext: string | (() => string);\n\tlabel: string;\n\tclassName?: string;\n}) => {\n\tconst [copied, setCopied] = useState(false);\n\treturn (\n\t\t<Tooltip>\n\t\t\t<TooltipTrigger asChild>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\tawait navigator.clipboard.writeText(typeof text === \"function\" ? text() : text);\n\t\t\t\t\t\tsetCopied(true);\n\t\t\t\t\t\twindow.setTimeout(() => setCopied(false), 1_200);\n\t\t\t\t\t}}\n\t\t\t\t\taria-label={label}\n\t\t\t\t>\n\t\t\t\t\t{copied ? <Check /> : <Copy />}\n\t\t\t\t</Button>\n\t\t\t</TooltipTrigger>\n\t\t\t<TooltipContent>{copied ? \"Copied\" : label}</TooltipContent>\n\t\t</Tooltip>\n\t);\n};\n","import type {\n\tEncodedRedisValueSchemaType,\n\tKeyDetailsResultSchemaType,\n} from \"@db-studio/shared/types\";\nimport { Badge } from \"@db-studio/ui/badge\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Field, FieldDescription, FieldGroup, FieldLabel } from \"@db-studio/ui/field\";\nimport { Input } from \"@db-studio/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@db-studio/ui/popover\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@db-studio/ui/tooltip\";\nimport { cn } from \"@db-studio/ui/utils\";\nimport { ArrowRight, Pencil, RefreshCw, Terminal, Trash2 } from \"lucide-react\";\nimport { type ReactNode, useEffect, useReducer, useState } from \"react\";\nimport { RedisCopyButton } from \"./redis-copy-button\";\nimport { RedisValueInput } from \"./redis-value\";\nimport type { RedisAct } from \"./redis-value-editors\";\n\nconst TTL_PRESETS = [\n\t{ label: \"60s\", ttlMs: 60_000 },\n\t{ label: \"10m\", ttlMs: 600_000 },\n\t{ label: \"1h\", ttlMs: 3_600_000 },\n\t{ label: \"1d\", ttlMs: 86_400_000 },\n\t{ label: \"7d\", ttlMs: 604_800_000 },\n] as const;\n\nconst formatBytes = (bytes: number | null): string => {\n\tif (bytes === null) return \"—\";\n\tif (bytes < 1_024) return `${bytes} B`;\n\tif (bytes < 1_048_576) return `${(bytes / 1_024).toFixed(1)} KiB`;\n\treturn `${(bytes / 1_048_576).toFixed(1)} MiB`;\n};\n\nconst formatDuration = (ms: number): string => {\n\tconst seconds = Math.max(0, Math.ceil(ms / 1_000));\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ${seconds % 60}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ${minutes % 60}m`;\n\treturn `${Math.floor(hours / 24)}d ${hours % 24}h`;\n};\n\n/** Re-renders once a second while `active`, so TTLs count down live. */\nconst useSecondTick = (active: boolean) => {\n\tconst [, tick] = useReducer((count: number) => count + 1, 0);\n\tuseEffect(() => {\n\t\tif (!active) return;\n\t\tconst id = window.setInterval(tick, 1_000);\n\t\treturn () => window.clearInterval(id);\n\t}, [active]);\n};\n\n/**\n * Key toolbar: name + copy, type badge, live stats, refresh, open-in-runner,\n * rename/TTL popover, and delete. Mount with `key={detail.key.base64}` so the\n * rename/TTL drafts reset when the selected key changes.\n */\nexport const RedisKeyHeader = ({\n\tdetail,\n\tlabel,\n\tloadedCount,\n\tfetchedAt,\n\tisFetching,\n\tpending,\n\tcommand,\n\tact,\n\tonRefresh,\n\tonOpenRunner,\n\tonDelete,\n}: {\n\tdetail: KeyDetailsResultSchemaType;\n\tlabel: string;\n\tloadedCount: number | null;\n\tfetchedAt: number;\n\tisFetching: boolean;\n\tpending: boolean;\n\tcommand: string | null;\n\tact: RedisAct;\n\tonRefresh: () => void;\n\tonOpenRunner: () => void;\n\tonDelete: () => void;\n}) => {\n\tconst [rename, setRename] = useState<EncodedRedisValueSchemaType | null>(detail.key);\n\tconst [ttl, setTtl] = useState(\"\");\n\tuseSecondTick(detail.ttlMs > 0);\n\n\tconst remainingMs =\n\t\tdetail.ttlMs > 0 ? detail.ttlMs - (Date.now() - fetchedAt) : detail.ttlMs;\n\tconst persistent = detail.ttlMs === -1;\n\tconst expired = !persistent && remainingMs <= 0;\n\tconst stats: { name: string; value: ReactNode; hint: string; alert?: boolean }[] = [\n\t\t{\n\t\t\tname: \"TTL\",\n\t\t\tvalue: persistent ? \"∞\" : expired ? \"expired\" : formatDuration(remainingMs),\n\t\t\thint: persistent\n\t\t\t\t? \"No expiry — the key persists until deleted.\"\n\t\t\t\t: expired\n\t\t\t\t\t? \"The TTL elapsed; Redis has evicted or is about to evict this key.\"\n\t\t\t\t\t: `Expires ${new Date(fetchedAt + detail.ttlMs).toLocaleString()}`,\n\t\t\talert: expired,\n\t\t},\n\t\t{\n\t\t\tname: \"MEM\",\n\t\t\tvalue: formatBytes(detail.memoryBytes),\n\t\t\thint: \"Approximate memory usage reported by MEMORY USAGE.\",\n\t\t},\n\t\t{\n\t\t\tname: \"LEN\",\n\t\t\tvalue: detail.length?.toLocaleString() ?? \"—\",\n\t\t\thint:\n\t\t\t\tloadedCount !== null && detail.length !== null\n\t\t\t\t\t? `${loadedCount.toLocaleString()} of ${detail.length.toLocaleString()} entries loaded in this view.`\n\t\t\t\t\t: \"Length reported by Redis (entries, or bytes for strings).\",\n\t\t},\n\t\t{\n\t\t\tname: \"REV\",\n\t\t\tvalue: detail.revision.slice(0, 8),\n\t\t\thint: \"Optimistic-lock revision. Writes are rejected when the key changed since this fetch.\",\n\t\t},\n\t\t{\n\t\t\tname: \"AT\",\n\t\t\tvalue: new Date(fetchedAt).toLocaleTimeString(),\n\t\t\thint: \"When this snapshot was fetched from Redis.\",\n\t\t},\n\t];\n\n\treturn (\n\t\t<header className=\"border-b border-border\">\n\t\t\t<div className=\"flex min-h-10 items-stretch justify-between\">\n\t\t\t\t<div className=\"flex min-w-0 flex-1 items-stretch overflow-x-auto\">\n\t\t\t\t\t<div className=\"flex min-w-0 max-w-[min(36rem,45vw)] shrink items-center gap-1 px-3 py-1.5\">\n\t\t\t\t\t\t<code\n\t\t\t\t\t\t\tclassName=\"min-w-0 truncate text-sm text-foreground\"\n\t\t\t\t\t\t\ttitle={label}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{label || \"(empty key)\"}\n\t\t\t\t\t\t</code>\n\t\t\t\t\t\t<RedisCopyButton\n\t\t\t\t\t\t\ttext={detail.key.utf8 ?? detail.key.base64}\n\t\t\t\t\t\t\tlabel=\"Copy key name\"\n\t\t\t\t\t\t\tclassName=\"shrink-0 text-muted-foreground\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tclassName=\"h-4 shrink-0 rounded-sm px-1.5 text-[8px] font-normal uppercase text-muted-foreground\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{detail.type}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t</div>\n\t\t\t\t\t{stats.map(({ name, value, hint, alert }, index) => (\n\t\t\t\t\t\t<Tooltip key={name}>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex shrink-0 cursor-default items-center gap-1.5 border-s border-border px-2 text-[10px]\",\n\t\t\t\t\t\t\t\t\t\tindex === stats.length - 1 && \"border-e\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{name}</span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"tabular-nums\",\n\t\t\t\t\t\t\t\t\t\t\talert ? \"text-destructive\" : \"text-foreground\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent side=\"bottom\">{hint}</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex shrink-0 items-stretch\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\tonClick={onRefresh}\n\t\t\t\t\t\tdisabled={isFetching}\n\t\t\t\t\t>\n\t\t\t\t\t\t<RefreshCw className={cn(isFetching && \"animate-spin\")} /> Refresh\n\t\t\t\t\t</Button>\n\t\t\t\t\t{command && (\n\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\t\t\t\tonClick={onOpenRunner}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Terminal /> Runner\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent side=\"bottom\">\n\t\t\t\t\t\t\t\tOpen in the query runner as <code className=\"font-mono\">{command}</code>\n\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t)}\n\t\t\t\t\t<Popover>\n\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Pencil /> Edit\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t<PopoverContent\n\t\t\t\t\t\t\talign=\"end\"\n\t\t\t\t\t\t\tclassName=\"w-72 rounded-sm p-3 font-mono\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<FieldGroup className=\"gap-4\">\n\t\t\t\t\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t\t\t\t\t<FieldLabel htmlFor=\"redis-key-rename\">Rename key</FieldLabel>\n\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex\"\n\t\t\t\t\t\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\tif (rename === null || rename.base64 === detail.key.base64 || pending)\n\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\tact({ action: \"rename\", newKey: rename });\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\t\t\t\t\t\tid=\"redis-key-rename\"\n\t\t\t\t\t\t\t\t\t\t\tvalue={rename}\n\t\t\t\t\t\t\t\t\t\t\tonChange={setRename}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-e-none font-mono\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-auto self-stretch rounded-s-none border-0 bg-clip-border\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\t\t\trename === null || rename.base64 === detail.key.base64 || pending\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Rename key\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<ArrowRight />\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t</Field>\n\t\t\t\t\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t\t\t\t\t<FieldLabel htmlFor=\"redis-key-ttl-operation\">TTL in seconds</FieldLabel>\n\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex\"\n\t\t\t\t\t\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\tif (pending) return;\n\t\t\t\t\t\t\t\t\t\t\tact({ action: \"setTtl\", ttlMs: ttl ? Number(ttl) * 1_000 : null });\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\t\tid=\"redis-key-ttl-operation\"\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tvalue={ttl}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(event) => setTtl(event.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tplaceholder={\n\t\t\t\t\t\t\t\t\t\t\t\tdetail.ttlMs > 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t? String(Math.ceil(detail.ttlMs / 1_000))\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Persistent\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-e-none font-mono\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-auto self-stretch rounded-s-none border-0 bg-clip-border\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tApply\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t\t\t\t\t\t{TTL_PRESETS.map((preset) => (\n\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\tkey={preset.label}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => act({ action: \"setTtl\", ttlMs: preset.ttlMs })}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{preset.label}\n\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={pending || persistent}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => act({ action: \"setTtl\", ttlMs: null })}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tPersist\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<FieldDescription>Leave empty to persist the key.</FieldDescription>\n\t\t\t\t\t\t\t\t</Field>\n\t\t\t\t\t\t\t</FieldGroup>\n\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t</Popover>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\tonClick={onDelete}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash2 /> Delete\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</header>\n\t);\n};\n","import type {\n\tEncodedRedisValueSchemaType,\n\tKeyActionSchemaType,\n\tKeyDetailsResultSchemaType,\n} from \"@db-studio/shared/types\";\nimport { Alert } from \"@db-studio/ui/alert\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Field, FieldLabel } from \"@db-studio/ui/field\";\nimport { Input } from \"@db-studio/ui/input\";\nimport { ToggleGroup, ToggleGroupItem } from \"@db-studio/ui/toggle-group\";\nimport { Plus, Save, Search, Trash2 } from \"lucide-react\";\nimport { type FormEvent, type ReactNode, useState } from \"react\";\nimport { RedisCopyButton } from \"./redis-copy-button\";\nimport { encodeTextValue, RedisValue, RedisValueInput } from \"./redis-value\";\n\n/**\n * Runs one key operation against the server. Resolves `true` on success so\n * add-forms know when to clear; conflict handling lives in the screen.\n */\nexport type RedisAct = (\n\toperation: KeyActionSchemaType[\"operation\"],\n\tforce?: boolean,\n) => Promise<boolean>;\n\nexport type StreamDirection = \"forward\" | \"backward\";\n\ntype RedisValueUnion = KeyDetailsResultSchemaType[\"value\"];\ntype HashValue = Extract<RedisValueUnion, { kind: \"hash\" }>;\ntype ListValue = Extract<RedisValueUnion, { kind: \"list\" }>;\ntype SetValue = Extract<RedisValueUnion, { kind: \"set\" }>;\ntype ZsetValue = Extract<RedisValueUnion, { kind: \"zset\" }>;\ntype StreamValue = Extract<RedisValueUnion, { kind: \"stream\" }>;\n\nconst emptyValue = encodeTextValue(\"\");\n\n/** `null` = the input's visible draft is unparseable and MUST NOT be submitted. */\ntype DraftValue = EncodedRedisValueSchemaType | null;\n\nconst textOf = (value: EncodedRedisValueSchemaType): string =>\n\tvalue.utf8 ?? `base64:${value.base64}`;\n\nconst matches = (needle: string, ...haystack: (string | number)[]): boolean =>\n\thaystack.some((part) => String(part).toLowerCase().includes(needle));\n\n/** Serializes the loaded (client-side) entries so devs can yank them into code. */\nconst collectionJson = (value: RedisValueUnion): string => {\n\tswitch (value.kind) {\n\t\tcase \"string\":\n\t\t\treturn JSON.stringify(textOf(value.value), null, 2);\n\t\tcase \"hash\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tvalue.entries.map((entry) => [textOf(entry.field), textOf(entry.value)]),\n\t\t\t\t),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"list\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => textOf(entry.value)),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"set\":\n\t\t\treturn JSON.stringify(value.members.map(textOf), null, 2);\n\t\tcase \"zset\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => ({ member: textOf(entry.member), score: entry.score })),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"stream\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => ({\n\t\t\t\t\tid: entry.id,\n\t\t\t\t\tfields: Object.fromEntries(\n\t\t\t\t\t\tentry.fields.map((field) => [textOf(field.field), textOf(field.value)]),\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n};\n\n/**\n * Local draft that re-syncs whenever the server value changes (identified by\n * `revision`), without wiping unrelated drafts on every refetch.\n */\nconst useSyncedDraft = <T,>(server: T, revision: string): [T, (next: T) => void] => {\n\tconst [draft, setDraft] = useState(server);\n\tconst [synced, setSynced] = useState(revision);\n\tif (synced !== revision) {\n\t\tsetSynced(revision);\n\t\tsetDraft(server);\n\t}\n\treturn [draft, setDraft];\n};\n\nconst EmptyRow = ({ filtered }: { filtered: boolean }) => (\n\t<div className=\"flex h-10 items-center justify-center px-2 text-xs text-muted-foreground\">\n\t\t{filtered ? \"No loaded entries match the filter.\" : \"No entries loaded.\"}\n\t</div>\n);\n\n/**\n * Frame around every collection editor: a client-side filter over the loaded\n * entries, a loaded/total counter, copy-as-JSON, and an optional `extra` slot\n * (e.g. the stream direction toggle).\n */\nconst CollectionShell = ({\n\tfilter,\n\tonFilterChange,\n\tshownCount,\n\tloadedCount,\n\ttotalCount,\n\tcopyJson,\n\textra,\n\theader,\n\tchildren,\n\tfooter,\n}: {\n\tfilter: string;\n\tonFilterChange: (next: string) => void;\n\tshownCount: number;\n\tloadedCount: number;\n\ttotalCount: number | null;\n\tcopyJson: () => string;\n\textra?: ReactNode;\n\theader: ReactNode;\n\tchildren: ReactNode;\n\tfooter: ReactNode;\n}) => (\n\t<div className=\"overflow-hidden border border-border\">\n\t\t<div className=\"flex h-8 items-center gap-1 border-b border-border bg-muted/10 px-1\">\n\t\t\t<Search className=\"ms-1 size-3 shrink-0 text-muted-foreground\" />\n\t\t\t<Input\n\t\t\t\tvariant=\"outline\"\n\t\t\t\tvalue={filter}\n\t\t\t\tonChange={(event) => onFilterChange(event.target.value)}\n\t\t\t\tplaceholder=\"Filter loaded entries\"\n\t\t\t\taria-label=\"Filter loaded entries\"\n\t\t\t\tclassName=\"h-6 flex-1 rounded-none border-none px-1 font-mono focus-visible:ring-0\"\n\t\t\t/>\n\t\t\t{extra}\n\t\t\t<span className=\"shrink-0 px-1 text-[10px] tabular-nums text-muted-foreground\">\n\t\t\t\t{filter\n\t\t\t\t\t? `${shownCount} / ${loadedCount}`\n\t\t\t\t\t: totalCount !== null && totalCount > loadedCount\n\t\t\t\t\t\t? `${loadedCount.toLocaleString()} of ${totalCount.toLocaleString()}`\n\t\t\t\t\t\t: loadedCount.toLocaleString()}\n\t\t\t</span>\n\t\t\t<RedisCopyButton\n\t\t\t\ttext={copyJson}\n\t\t\t\tlabel=\"Copy loaded entries as JSON\"\n\t\t\t/>\n\t\t</div>\n\t\t{header}\n\t\t{children}\n\t\t{footer}\n\t</div>\n);\n\nconst AddPair = ({\n\tlabels,\n\tonSave,\n\tpending,\n\tscore = false,\n}: {\n\tlabels: [string, string];\n\tonSave: (\n\t\tfirst: EncodedRedisValueSchemaType,\n\t\tsecond: EncodedRedisValueSchemaType | number,\n\t) => Promise<boolean>;\n\tpending: boolean;\n\tscore?: boolean;\n}) => {\n\tconst [first, setFirst] = useState<DraftValue>(emptyValue);\n\tconst [second, setSecond] = useState<DraftValue>(emptyValue);\n\tconst [numeric, setNumeric] = useState(\"0\");\n\tconst invalid = first === null || (!score && second === null);\n\tconst submit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tif (first === null || second === null) return;\n\t\tconst saved = await onSave(first, score ? Number(numeric) : second);\n\t\tif (!saved) return;\n\t\tsetFirst(emptyValue);\n\t\tsetSecond(emptyValue);\n\t\tsetNumeric(\"0\");\n\t};\n\treturn (\n\t\t<form\n\t\t\tonSubmit={submit}\n\t\t\tclassName=\"grid grid-cols-[1fr_1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={first}\n\t\t\t\tonChange={setFirst}\n\t\t\t\tplaceholder={labels[0]}\n\t\t\t/>\n\t\t\t{score ? (\n\t\t\t\t<Input\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tstep=\"any\"\n\t\t\t\t\tvalue={numeric}\n\t\t\t\t\tonChange={(event) => setNumeric(event.target.value)}\n\t\t\t\t\tplaceholder={labels[1]}\n\t\t\t\t\tclassName=\"font-mono\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<RedisValueInput\n\t\t\t\t\tvalue={second}\n\t\t\t\t\tonChange={setSecond}\n\t\t\t\t\tplaceholder={labels[1]}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<Button\n\t\t\t\ttype=\"submit\"\n\t\t\t\tdisabled={pending || invalid}\n\t\t\t>\n\t\t\t\t<Plus /> Add\n\t\t\t</Button>\n\t\t</form>\n\t);\n};\n\nconst StringEditor = ({\n\tvalue,\n\ttruncated,\n\tact,\n\tpending,\n\tonLoadFull,\n\tonDownload,\n}: {\n\tvalue: EncodedRedisValueSchemaType;\n\ttruncated: boolean;\n\tact: RedisAct;\n\tpending: boolean;\n\tonLoadFull: () => void;\n\tonDownload: () => void;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, value.base64);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"space-y-3\">\n\t\t\t{truncated && (\n\t\t\t\t<div className=\"grid gap-2 border-b border-border pb-3 sm:grid-cols-[1fr_auto] sm:items-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\ttitle=\"Preview truncated\"\n\t\t\t\t\t\tmessage=\"Load the full value before editing (up to the 8 MiB response cap).\"\n\t\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={onLoadFull}\n\t\t\t\t\t>\n\t\t\t\t\t\tLoad full\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<RedisValue\n\t\t\t\tvalue={value}\n\t\t\t\tonDownload={onDownload}\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (draft !== null && dirty) act({ action: \"setString\", value: draft });\n\t\t\t\t}}\n\t\t\t\tclassName=\"border border-border p-3\"\n\t\t\t>\n\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t<FieldLabel htmlFor=\"redis-string-value\">Replace value</FieldLabel>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tid=\"redis-string-value\"\n\t\t\t\t\t\tvalue={draft}\n\t\t\t\t\t\tonChange={setDraft}\n\t\t\t\t\t/>\n\t\t\t\t</Field>\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\tclassName=\"mt-3\"\n\t\t\t\t\tdisabled={truncated || pending || !dirty}\n\t\t\t\t>\n\t\t\t\t\t<Save /> Save value\n\t\t\t\t</Button>\n\t\t\t</form>\n\t\t</div>\n\t);\n};\n\nconst HashRow = ({\n\tfield,\n\tvalue,\n\tact,\n\tpending,\n}: {\n\tfield: EncodedRedisValueSchemaType;\n\tvalue: EncodedRedisValueSchemaType;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, value.base64);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<RedisValue\n\t\t\t\tvalue={field}\n\t\t\t\tcompact\n\t\t\t/>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={setDraft}\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty}\n\t\t\t\t\tonClick={() => draft !== null && act({ action: \"upsertHash\", field, value: draft })}\n\t\t\t\t\taria-label=\"Save hash value\"\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"deleteHash\", field })}\n\t\t\t\t\taria-label=\"Delete hash field\"\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst HashEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: HashValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) =>\n\t\t\t\tmatches(needle, textOf(entry.field), textOf(entry.value)),\n\t\t\t)\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Field</span>\n\t\t\t\t\t<span>Value</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tlabels={[\"field\", \"value\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(field, next) =>\n\t\t\t\t\t\tact({ action: \"upsertHash\", field, value: next as EncodedRedisValueSchemaType })\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<HashRow\n\t\t\t\t\t\tkey={entry.field.base64}\n\t\t\t\t\t\tfield={entry.field}\n\t\t\t\t\t\tvalue={entry.value}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst ListRow = ({\n\tindex,\n\tvalue,\n\trevision,\n\tact,\n\tpending,\n}: {\n\tindex: number;\n\tvalue: EncodedRedisValueSchemaType;\n\trevision: string;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, `${revision}:${value.base64}`);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[3rem_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<code className=\"text-xs text-muted-foreground\">{index}</code>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={setDraft}\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty}\n\t\t\t\t\tonClick={() => draft !== null && act({ action: \"setList\", index, value: draft })}\n\t\t\t\t\taria-label={`Save list item ${index}`}\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"deleteList\", index })}\n\t\t\t\t\taria-label={`Delete list item ${index}`}\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst ListEditor = ({\n\tvalue,\n\trevision,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: ListValue;\n\trevision: string;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst [item, setItem] = useState<DraftValue>(emptyValue);\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) => matches(needle, entry.index, textOf(entry.value)))\n\t\t: value.entries;\n\tconst push = async (side: \"left\" | \"right\") => {\n\t\tif (item === null) return;\n\t\tconst saved = await act({ action: \"pushList\", side, value: item });\n\t\tif (saved) setItem(emptyValue);\n\t};\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[3rem_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Index</span>\n\t\t\t\t\t<span>Value</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<form\n\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tpush(\"right\");\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"grid grid-cols-[1fr_auto_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t\t\t>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tvalue={item}\n\t\t\t\t\t\tonChange={setItem}\n\t\t\t\t\t\tplaceholder=\"new item\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={pending || item === null}\n\t\t\t\t\t\tonClick={() => push(\"left\")}\n\t\t\t\t\t>\n\t\t\t\t\t\tPush left\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tdisabled={pending || item === null}\n\t\t\t\t\t>\n\t\t\t\t\t\tPush right\n\t\t\t\t\t</Button>\n\t\t\t\t</form>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<ListRow\n\t\t\t\t\t\tkey={entry.index}\n\t\t\t\t\t\tindex={entry.index}\n\t\t\t\t\t\tvalue={entry.value}\n\t\t\t\t\t\trevision={revision}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst SetEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: SetValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst [member, setMember] = useState<DraftValue>(emptyValue);\n\tconst needle = filter.trim().toLowerCase();\n\tconst members = needle\n\t\t? value.members.filter((entry) => matches(needle, textOf(entry)))\n\t\t: value.members;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={members.length}\n\t\t\tloadedCount={value.members.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Member</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<form\n\t\t\t\t\tonSubmit={async (event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tif (member === null) return;\n\t\t\t\t\t\tconst saved = await act({ action: \"addSet\", member });\n\t\t\t\t\t\tif (saved) setMember(emptyValue);\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"grid grid-cols-[1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t\t\t>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tvalue={member}\n\t\t\t\t\t\tonChange={setMember}\n\t\t\t\t\t\tplaceholder=\"new member\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tdisabled={pending || member === null}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Plus /> Add member\n\t\t\t\t\t</Button>\n\t\t\t\t</form>\n\t\t\t}\n\t\t>\n\t\t\t{members.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tmembers.map((entry) => (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={entry.base64}\n\t\t\t\t\t\tclassName=\"flex min-h-8 items-center gap-2 border-b border-border px-2 py-1\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\tvalue={entry}\n\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\tonClick={() => act({ action: \"removeSet\", member: entry })}\n\t\t\t\t\t\t\taria-label=\"Remove set member\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst ZsetRow = ({\n\tmember,\n\tscore,\n\tact,\n\tpending,\n}: {\n\tmember: EncodedRedisValueSchemaType;\n\tscore: number | \"inf\" | \"-inf\";\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft(String(score), String(score));\n\tconst parsedDraft = Number(draft);\n\tconst dirty = draft !== String(score);\n\tconst valid = Number.isFinite(parsedDraft);\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[1fr_8rem_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<RedisValue\n\t\t\t\tvalue={member}\n\t\t\t\tcompact\n\t\t\t/>\n\t\t\t<Input\n\t\t\t\tinputMode=\"decimal\"\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={(event) => setDraft(event.target.value)}\n\t\t\t\taria-label=\"Sorted set score\"\n\t\t\t\tclassName=\"font-mono\"\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty || !valid}\n\t\t\t\t\tonClick={() => act({ action: \"upsertZset\", member, score: parsedDraft })}\n\t\t\t\t\taria-label=\"Save sorted set score\"\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"removeZset\", member })}\n\t\t\t\t\taria-label=\"Remove sorted set member\"\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst ZsetEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: ZsetValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) => matches(needle, textOf(entry.member), entry.score))\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[1fr_8rem_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Member</span>\n\t\t\t\t\t<span>Score</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tscore\n\t\t\t\t\tlabels={[\"member\", \"score\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(member, score) =>\n\t\t\t\t\t\tact({ action: \"upsertZset\", member, score: score as number })\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<ZsetRow\n\t\t\t\t\t\tkey={entry.member.base64}\n\t\t\t\t\t\tmember={entry.member}\n\t\t\t\t\t\tscore={entry.score}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst StreamEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n\tdirection,\n\tonDirectionChange,\n}: {\n\tvalue: StreamValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n\tdirection: StreamDirection;\n\tonDirectionChange: (direction: StreamDirection) => void;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) =>\n\t\t\t\tmatches(\n\t\t\t\t\tneedle,\n\t\t\t\t\tentry.id,\n\t\t\t\t\t...entry.fields.flatMap((field) => [textOf(field.field), textOf(field.value)]),\n\t\t\t\t),\n\t\t\t)\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\textra={\n\t\t\t\t<ToggleGroup\n\t\t\t\t\ttype=\"single\"\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tvalue={direction}\n\t\t\t\t\tonValueChange={(next) => next && onDirectionChange(next as StreamDirection)}\n\t\t\t\t\taria-label=\"Stream order\"\n\t\t\t\t\tclassName=\"shrink-0 rounded-none\"\n\t\t\t\t>\n\t\t\t\t\t<ToggleGroupItem\n\t\t\t\t\t\tvalue=\"forward\"\n\t\t\t\t\t\tclassName=\"h-6 rounded-none px-2 text-[10px]\"\n\t\t\t\t\t>\n\t\t\t\t\t\tOldest first\n\t\t\t\t\t</ToggleGroupItem>\n\t\t\t\t\t<ToggleGroupItem\n\t\t\t\t\t\tvalue=\"backward\"\n\t\t\t\t\t\tclassName=\"h-6 rounded-none px-2 text-[10px]\"\n\t\t\t\t\t>\n\t\t\t\t\t\tNewest first\n\t\t\t\t\t</ToggleGroupItem>\n\t\t\t\t</ToggleGroup>\n\t\t\t}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[10rem_1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Entry ID</span>\n\t\t\t\t\t<span>Fields</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tlabels={[\"field\", \"value\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(field, next) =>\n\t\t\t\t\t\tact({\n\t\t\t\t\t\t\taction: \"appendStream\",\n\t\t\t\t\t\t\tid: \"*\",\n\t\t\t\t\t\t\tfields: [{ field, value: next as EncodedRedisValueSchemaType }],\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={entry.id}\n\t\t\t\t\t\tclassName=\"grid grid-cols-[10rem_1fr_2rem] border-b border-border\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<code className=\"border-e border-border px-2 py-1.5 text-xs text-primary\">\n\t\t\t\t\t\t\t{entry.id}\n\t\t\t\t\t\t</code>\n\t\t\t\t\t\t<div className=\"grid gap-1 px-2 py-1.5\">\n\t\t\t\t\t\t\t{entry.fields.map((field) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={field.field.base64}\n\t\t\t\t\t\t\t\t\tclassName=\"grid grid-cols-[minmax(8rem,0.4fr)_1fr] gap-3\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\t\t\tvalue={field.field}\n\t\t\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\t\t\tvalue={field.value}\n\t\t\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\t\t\tclassName=\"rounded-none text-muted-foreground hover:text-destructive\"\n\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\tonClick={() => act({ action: \"deleteStream\", id: entry.id })}\n\t\t\t\t\t\t\taria-label={`Delete stream entry ${entry.id}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\n/** Dispatches the loaded key to the editor matching its Redis type. */\nexport const RedisValueEditor = ({\n\tdetail,\n\tact,\n\tpending,\n\tonLoadFull,\n\tonDownload,\n\tstreamDirection,\n\tonStreamDirectionChange,\n}: {\n\tdetail: KeyDetailsResultSchemaType;\n\tact: RedisAct;\n\tpending: boolean;\n\tonLoadFull: () => void;\n\tonDownload: () => void;\n\tstreamDirection: StreamDirection;\n\tonStreamDirectionChange: (direction: StreamDirection) => void;\n}) => {\n\tconst value = detail.value;\n\tswitch (value.kind) {\n\t\tcase \"string\":\n\t\t\treturn (\n\t\t\t\t<StringEditor\n\t\t\t\t\tvalue={value.value}\n\t\t\t\t\ttruncated={value.truncated}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonLoadFull={onLoadFull}\n\t\t\t\t\tonDownload={onDownload}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"hash\":\n\t\t\treturn (\n\t\t\t\t<HashEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"list\":\n\t\t\treturn (\n\t\t\t\t<ListEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\trevision={detail.revision}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"set\":\n\t\t\treturn (\n\t\t\t\t<SetEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"zset\":\n\t\t\treturn (\n\t\t\t\t<ZsetEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"stream\":\n\t\t\treturn (\n\t\t\t\t<StreamEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tdirection={streamDirection}\n\t\t\t\t\tonDirectionChange={onStreamDirectionChange}\n\t\t\t\t/>\n\t\t\t);\n\t\tdefault:\n\t\t\treturn (\n\t\t\t\t<Alert\n\t\t\t\t\tvariant=\"info\"\n\t\t\t\t\ttitle=\"Metadata only\"\n\t\t\t\t\tmessage=\"This Redis module type cannot be edited in this release.\"\n\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t/>\n\t\t\t);\n\t}\n};\n","import type { KeyActionSchemaType, KeyDetailsResultSchemaType } from \"@db-studio/shared/types\";\nimport { Alert } from \"@db-studio/ui/alert\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n} from \"@db-studio/ui/alert-dialog\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Spinner } from \"@db-studio/ui/spinner\";\nimport { useNavigate } from \"@tanstack/react-router\";\nimport { DatabaseZap, Plus } from \"lucide-react\";\nimport { useQueryState } from \"nuqs\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { getRedisStringChunk } from \"@/shared/api\";\nimport { useDatabaseStore } from \"@/stores/database.store\";\nimport { useOverlayStore } from \"@/stores/overlay.store\";\nimport { useQueriesStore } from \"@/stores/queries.store\";\nimport { RedisKeyHeader } from \"../components/redis-key-header\";\nimport { bytesFromBase64Url } from \"../components/redis-value\";\nimport {\n\ttype RedisAct,\n\tRedisValueEditor,\n\ttype StreamDirection,\n} from \"../components/redis-value-editors\";\nimport { useRedisKey, useRedisKeyMutations } from \"../hooks/use-redis-browser\";\n\n/** The read command a dev would run for this key, used by \"Open in runner\". */\nconst runnerCommand = (detail: KeyDetailsResultSchemaType): string | null => {\n\tif (detail.key.utf8 === undefined) return null;\n\tconst key = JSON.stringify(detail.key.utf8);\n\tswitch (detail.type) {\n\t\tcase \"string\":\n\t\t\treturn `GET ${key}`;\n\t\tcase \"hash\":\n\t\t\treturn `HGETALL ${key}`;\n\t\tcase \"list\":\n\t\t\treturn `LRANGE ${key} 0 -1`;\n\t\tcase \"set\":\n\t\t\treturn `SMEMBERS ${key}`;\n\t\tcase \"zset\":\n\t\t\treturn `ZRANGE ${key} 0 -1 WITHSCORES`;\n\t\tcase \"stream\":\n\t\t\treturn `XRANGE ${key} - + COUNT 100`;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\n\n/** Entries fetched so far across pages; null for scalar/unknown values. */\nconst countLoaded = (detail: KeyDetailsResultSchemaType): number | null => {\n\tswitch (detail.value.kind) {\n\t\tcase \"hash\":\n\t\tcase \"list\":\n\t\tcase \"zset\":\n\t\tcase \"stream\":\n\t\t\treturn detail.value.entries.length;\n\t\tcase \"set\":\n\t\t\treturn detail.value.members.length;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\n\nexport const RedisBrowserScreen = () => {\n\tconst navigate = useNavigate();\n\tconst { selectedDatabase } = useDatabaseStore();\n\tconst { openOverlay } = useOverlayStore();\n\tconst { addQuery, updateQuery } = useQueriesStore();\n\tconst [selectedKey, setSelectedKey] = useQueryState(\"key\");\n\tconst [fullKey, setFullKey] = useState<string | null>(null);\n\tconst [reverseStreamKey, setReverseStreamKey] = useState<string | null>(null);\n\tconst streamDirection: StreamDirection =\n\t\treverseStreamKey === selectedKey ? \"backward\" : \"forward\";\n\tconst query = useRedisKey(selectedKey, fullKey === selectedKey, streamDirection);\n\tconst { applyAction, deleteKey, isPending } = useRedisKeyMutations();\n\tconst pages = query.data?.pages;\n\tconst detail = pages?.[0];\n\tconst [confirmingDelete, setConfirmingDelete] = useState(false);\n\tconst [conflictOperation, setConflictOperation] = useState<\n\t\tKeyActionSchemaType[\"operation\"] | null\n\t>(null);\n\tconst label = detail?.key.utf8 ?? (detail ? `binary:${detail.key.base64}` : \"\");\n\tconst command = detail ? runnerCommand(detail) : null;\n\n\tconst mergedDetail = useMemo(() => {\n\t\tif (!detail || !pages || pages.length === 1) return detail;\n\t\tconst next = structuredClone(detail);\n\t\tfor (const page of pages.slice(1)) {\n\t\t\tif (next.value.kind === \"hash\" && page.value.kind === \"hash\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"list\" && page.value.kind === \"list\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"set\" && page.value.kind === \"set\")\n\t\t\t\tnext.value.members.push(...page.value.members);\n\t\t\tif (next.value.kind === \"zset\" && page.value.kind === \"zset\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"stream\" && page.value.kind === \"stream\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t}\n\t\treturn next;\n\t}, [detail, pages]);\n\tconst loadedCount = mergedDetail ? countLoaded(mergedDetail) : null;\n\n\tconst act: RedisAct = async (operation, force = false) => {\n\t\tif (!selectedKey || !detail) return false;\n\t\ttry {\n\t\t\tconst result = await applyAction(selectedKey, {\n\t\t\t\texpectedRevision: detail.revision,\n\t\t\t\tforce,\n\t\t\t\toperation,\n\t\t\t});\n\t\t\tsetConflictOperation(null);\n\t\t\tif (result.deleted) setSelectedKey(null);\n\t\t\telse if ((result.key.base64 || \"-\") !== selectedKey)\n\t\t\t\tsetSelectedKey(result.key.base64 || \"-\");\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\t// toast.promise in the mutation hook already surfaced the failure.\n\t\t\tif ((error as Error & { status?: number }).status === 409)\n\t\t\t\tsetConflictOperation(operation);\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst openInRunner = () => {\n\t\tif (!command) return;\n\t\tconst queryId = addQuery(`redis: ${label.slice(0, 40)}`);\n\t\tupdateQuery(queryId, { query: command });\n\t\tnavigate({ to: \"/runner/$queryId\", params: { queryId } });\n\t};\n\n\tconst downloadOriginalString = async () => {\n\t\tif (!selectedKey || !detail || detail.type !== \"string\") return;\n\t\tconst chunks: Uint8Array[] = [];\n\t\tlet offset = 0;\n\t\tlet hasMore = true;\n\t\twhile (hasMore) {\n\t\t\tconst response = await getRedisStringChunk({\n\t\t\t\tdb: selectedDatabase ?? \"\",\n\t\t\t\tkey: selectedKey,\n\t\t\t\toffset,\n\t\t\t\texpectedRevision: detail.revision,\n\t\t\t});\n\t\t\tconst page = response.data.data;\n\t\t\tchunks.push(bytesFromBase64Url(page.chunk.base64));\n\t\t\thasMore = page.hasMore;\n\t\t\tif (page.hasMore && (page.nextOffset === null || page.nextOffset <= offset)) {\n\t\t\t\tthrow new Error(\"Redis download made no forward progress\");\n\t\t\t}\n\t\t\toffset = page.nextOffset ?? offset;\n\t\t}\n\t\tconst size = chunks.reduce((total, chunk) => total + chunk.length, 0);\n\t\tconst bytes = new Uint8Array(size);\n\t\tlet position = 0;\n\t\tfor (const chunk of chunks) {\n\t\t\tbytes.set(chunk, position);\n\t\t\tposition += chunk.length;\n\t\t}\n\t\tconst url = URL.createObjectURL(new Blob([bytes.buffer as ArrayBuffer]));\n\t\tconst anchor = document.createElement(\"a\");\n\t\tanchor.href = url;\n\t\tanchor.download = `${detail.key.utf8?.replaceAll(/[^\\w.-]+/g, \"_\") || \"redis-value\"}.bin`;\n\t\tanchor.click();\n\t\tURL.revokeObjectURL(url);\n\t};\n\n\tif (!selectedKey)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center bg-background font-mono\">\n\t\t\t\t<div className=\"max-w-sm text-center\">\n\t\t\t\t\t<DatabaseZap className=\"mx-auto mb-3 size-5 text-muted-foreground\" />\n\t\t\t\t\t<h1 className=\"text-sm font-medium text-foreground\">No key selected</h1>\n\t\t\t\t\t<p className=\"mt-1 text-xs leading-5 text-muted-foreground\">\n\t\t\t\t\t\tSelect a key from the navigator or create one to inspect its value.\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"mt-3\"\n\t\t\t\t\t\tonClick={() => openOverlay(\"redis-browser.create-key\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Plus /> Create key\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</main>\n\t\t);\n\tif (query.isLoading)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center\">\n\t\t\t\t<Spinner />\n\t\t\t</main>\n\t\t);\n\tif (query.error || !detail || !mergedDetail)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center bg-background p-4\">\n\t\t\t\t<div className=\"w-full max-w-md text-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"error\"\n\t\t\t\t\t\ttitle=\"Unable to load key\"\n\t\t\t\t\t\tmessage={query.error?.message ?? \"The key no longer exists.\"}\n\t\t\t\t\t\tclassName=\"rounded-sm text-start\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"mt-4\"\n\t\t\t\t\t\tonClick={() => setSelectedKey(null)}\n\t\t\t\t\t>\n\t\t\t\t\t\tBack to keyspace\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</main>\n\t\t);\n\n\tconst remaining =\n\t\tmergedDetail.length !== null && loadedCount !== null\n\t\t\t? Math.max(0, mergedDetail.length - loadedCount)\n\t\t\t: null;\n\n\treturn (\n\t\t<main className=\"flex min-h-0 flex-1 flex-col bg-background font-mono\">\n\t\t\t<RedisKeyHeader\n\t\t\t\tkey={detail.key.base64}\n\t\t\t\tdetail={detail}\n\t\t\t\tlabel={label}\n\t\t\t\tloadedCount={loadedCount}\n\t\t\t\tfetchedAt={query.dataUpdatedAt}\n\t\t\t\tisFetching={query.isFetching}\n\t\t\t\tpending={isPending}\n\t\t\t\tcommand={command}\n\t\t\t\tact={act}\n\t\t\t\tonRefresh={() => query.refetch()}\n\t\t\t\tonOpenRunner={openInRunner}\n\t\t\t\tonDelete={() => setConfirmingDelete(true)}\n\t\t\t/>\n\t\t\t<AlertDialog\n\t\t\t\topen={confirmingDelete}\n\t\t\t\tonOpenChange={setConfirmingDelete}\n\t\t\t>\n\t\t\t\t<AlertDialogContent>\n\t\t\t\t\t<AlertDialogHeader>\n\t\t\t\t\t\t<AlertDialogTitle>Delete key</AlertDialogTitle>\n\t\t\t\t\t\t<AlertDialogDescription>\n\t\t\t\t\t\t\tAre you sure you want to delete{\" \"}\n\t\t\t\t\t\t\t<code className=\"break-all text-foreground\">{label || \"(empty key)\"}</code>? This\n\t\t\t\t\t\t\taction cannot be undone.\n\t\t\t\t\t\t</AlertDialogDescription>\n\t\t\t\t\t</AlertDialogHeader>\n\t\t\t\t\t<AlertDialogFooter>\n\t\t\t\t\t\t<AlertDialogCancel>Cancel</AlertDialogCancel>\n\t\t\t\t\t\t<AlertDialogAction\n\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\t\t\tsetConfirmingDelete(false);\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait deleteKey(selectedKey, detail.revision);\n\t\t\t\t\t\t\t\t\tsetSelectedKey(null);\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// toast.promise in the mutation hook already surfaced the failure.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t</AlertDialogAction>\n\t\t\t\t\t</AlertDialogFooter>\n\t\t\t\t</AlertDialogContent>\n\t\t\t</AlertDialog>\n\t\t\t{conflictOperation && (\n\t\t\t\t<div className=\"grid gap-2 border-b border-border p-2 sm:grid-cols-[1fr_auto] sm:items-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\ttitle=\"Revision conflict\"\n\t\t\t\t\t\tmessage=\"This key changed on the server. Reload it or overwrite with the pending change.\"\n\t\t\t\t\t\tclassName=\"rounded-sm py-2\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tsetConflictOperation(null);\n\t\t\t\t\t\t\t\tquery.refetch();\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tReload\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tdisabled={isPending}\n\t\t\t\t\t\t\tonClick={() => act(conflictOperation, true)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tOverwrite\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"min-h-0 flex-1 overflow-auto\">\n\t\t\t\t<section className=\"p-3\">\n\t\t\t\t\t<RedisValueEditor\n\t\t\t\t\t\tkey={detail.key.base64}\n\t\t\t\t\t\tdetail={mergedDetail}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={isPending}\n\t\t\t\t\t\tonLoadFull={() => setFullKey(selectedKey)}\n\t\t\t\t\t\tonDownload={() => {\n\t\t\t\t\t\t\ttoast.promise(downloadOriginalString(), {\n\t\t\t\t\t\t\t\tloading: \"Downloading original Redis bytes…\",\n\t\t\t\t\t\t\t\tsuccess: \"Redis value downloaded\",\n\t\t\t\t\t\t\t\terror: (error: Error) => error.message || \"Download failed\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tstreamDirection={streamDirection}\n\t\t\t\t\t\tonStreamDirectionChange={(direction) =>\n\t\t\t\t\t\t\tsetReverseStreamKey(direction === \"backward\" ? selectedKey : null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t{query.hasNextPage && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tclassName=\"mt-3 w-full\"\n\t\t\t\t\t\t\tonClick={() => query.fetchNextPage()}\n\t\t\t\t\t\t\tdisabled={query.isFetchingNextPage}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{query.isFetchingNextPage\n\t\t\t\t\t\t\t\t? \"Loading…\"\n\t\t\t\t\t\t\t\t: detail.type === \"stream\" && streamDirection === \"backward\"\n\t\t\t\t\t\t\t\t\t? \"Load 100 older entries\"\n\t\t\t\t\t\t\t\t\t: `Load next 100${remaining !== null ? ` (${remaining.toLocaleString()} remaining)` : \"\"}`}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</main>\n\t);\n};\n","import { createFileRoute } from \"@tanstack/react-router\";\nimport { RedisBrowserScreen } from \"@/features/redis-browser\";\n\nexport const Route = createFileRoute(\"/_pathlessLayout/browser\")({\n\tcomponent: RedisBrowserScreen,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser-B3ZulIJb.js","names":["RedisBrowserScreen","SplitComponent","component"],"sources":["../../src/features/redis-browser/components/redis-copy-button.tsx","../../src/features/redis-browser/components/redis-key-header.tsx","../../src/features/redis-browser/components/redis-value-editors.tsx","../../src/features/redis-browser/screens/redis-browser-screen.tsx","../../src/routes/_pathlessLayout/browser.tsx?tsr-split=component"],"sourcesContent":["import { Button } from \"@db-studio/ui/button\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@db-studio/ui/tooltip\";\nimport { Check, Copy } from \"lucide-react\";\nimport { useState } from \"react\";\n\n/**\n * Icon button that copies `text` (or its lazy producer) to the clipboard and\n * flashes a check mark. Used for key names, values, and JSON exports.\n */\nexport const RedisCopyButton = ({\n\ttext,\n\tlabel,\n\tclassName,\n}: {\n\ttext: string | (() => string);\n\tlabel: string;\n\tclassName?: string;\n}) => {\n\tconst [copied, setCopied] = useState(false);\n\treturn (\n\t\t<Tooltip>\n\t\t\t<TooltipTrigger asChild>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={className}\n\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\tawait navigator.clipboard.writeText(typeof text === \"function\" ? text() : text);\n\t\t\t\t\t\tsetCopied(true);\n\t\t\t\t\t\twindow.setTimeout(() => setCopied(false), 1_200);\n\t\t\t\t\t}}\n\t\t\t\t\taria-label={label}\n\t\t\t\t>\n\t\t\t\t\t{copied ? <Check /> : <Copy />}\n\t\t\t\t</Button>\n\t\t\t</TooltipTrigger>\n\t\t\t<TooltipContent>{copied ? \"Copied\" : label}</TooltipContent>\n\t\t</Tooltip>\n\t);\n};\n","import type {\n\tEncodedRedisValueSchemaType,\n\tKeyDetailsResultSchemaType,\n} from \"@db-studio/shared/types\";\nimport { Badge } from \"@db-studio/ui/badge\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Field, FieldDescription, FieldGroup, FieldLabel } from \"@db-studio/ui/field\";\nimport { Input } from \"@db-studio/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@db-studio/ui/popover\";\nimport { Tooltip, TooltipContent, TooltipTrigger } from \"@db-studio/ui/tooltip\";\nimport { cn } from \"@db-studio/ui/utils\";\nimport { ArrowRight, Pencil, RefreshCw, Terminal, Trash2 } from \"lucide-react\";\nimport { type ReactNode, useEffect, useReducer, useState } from \"react\";\nimport { RedisCopyButton } from \"./redis-copy-button\";\nimport { RedisValueInput } from \"./redis-value\";\nimport type { RedisAct } from \"./redis-value-editors\";\n\nconst TTL_PRESETS = [\n\t{ label: \"60s\", ttlMs: 60_000 },\n\t{ label: \"10m\", ttlMs: 600_000 },\n\t{ label: \"1h\", ttlMs: 3_600_000 },\n\t{ label: \"1d\", ttlMs: 86_400_000 },\n\t{ label: \"7d\", ttlMs: 604_800_000 },\n] as const;\n\nconst formatBytes = (bytes: number | null): string => {\n\tif (bytes === null) return \"—\";\n\tif (bytes < 1_024) return `${bytes} B`;\n\tif (bytes < 1_048_576) return `${(bytes / 1_024).toFixed(1)} KiB`;\n\treturn `${(bytes / 1_048_576).toFixed(1)} MiB`;\n};\n\nconst formatDuration = (ms: number): string => {\n\tconst seconds = Math.max(0, Math.ceil(ms / 1_000));\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tif (minutes < 60) return `${minutes}m ${seconds % 60}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tif (hours < 24) return `${hours}h ${minutes % 60}m`;\n\treturn `${Math.floor(hours / 24)}d ${hours % 24}h`;\n};\n\n/** Re-renders once a second while `active`, so TTLs count down live. */\nconst useSecondTick = (active: boolean) => {\n\tconst [, tick] = useReducer((count: number) => count + 1, 0);\n\tuseEffect(() => {\n\t\tif (!active) return;\n\t\tconst id = window.setInterval(tick, 1_000);\n\t\treturn () => window.clearInterval(id);\n\t}, [active]);\n};\n\n/**\n * Key toolbar: name + copy, type badge, live stats, refresh, open-in-runner,\n * rename/TTL popover, and delete. Mount with `key={detail.key.base64}` so the\n * rename/TTL drafts reset when the selected key changes.\n */\nexport const RedisKeyHeader = ({\n\tdetail,\n\tlabel,\n\tloadedCount,\n\tfetchedAt,\n\tisFetching,\n\tpending,\n\tcommand,\n\tact,\n\tonRefresh,\n\tonOpenRunner,\n\tonDelete,\n}: {\n\tdetail: KeyDetailsResultSchemaType;\n\tlabel: string;\n\tloadedCount: number | null;\n\tfetchedAt: number;\n\tisFetching: boolean;\n\tpending: boolean;\n\tcommand: string | null;\n\tact: RedisAct;\n\tonRefresh: () => void;\n\tonOpenRunner: () => void;\n\tonDelete: () => void;\n}) => {\n\tconst [rename, setRename] = useState<EncodedRedisValueSchemaType | null>(detail.key);\n\tconst [ttl, setTtl] = useState(\"\");\n\tuseSecondTick(detail.ttlMs > 0);\n\n\tconst remainingMs =\n\t\tdetail.ttlMs > 0 ? detail.ttlMs - (Date.now() - fetchedAt) : detail.ttlMs;\n\tconst persistent = detail.ttlMs === -1;\n\tconst expired = !persistent && remainingMs <= 0;\n\tconst stats: { name: string; value: ReactNode; hint: string; alert?: boolean }[] = [\n\t\t{\n\t\t\tname: \"TTL\",\n\t\t\tvalue: persistent ? \"∞\" : expired ? \"expired\" : formatDuration(remainingMs),\n\t\t\thint: persistent\n\t\t\t\t? \"No expiry — the key persists until deleted.\"\n\t\t\t\t: expired\n\t\t\t\t\t? \"The TTL elapsed; Redis has evicted or is about to evict this key.\"\n\t\t\t\t\t: `Expires ${new Date(fetchedAt + detail.ttlMs).toLocaleString()}`,\n\t\t\talert: expired,\n\t\t},\n\t\t{\n\t\t\tname: \"MEM\",\n\t\t\tvalue: formatBytes(detail.memoryBytes),\n\t\t\thint: \"Approximate memory usage reported by MEMORY USAGE.\",\n\t\t},\n\t\t{\n\t\t\tname: \"LEN\",\n\t\t\tvalue: detail.length?.toLocaleString() ?? \"—\",\n\t\t\thint:\n\t\t\t\tloadedCount !== null && detail.length !== null\n\t\t\t\t\t? `${loadedCount.toLocaleString()} of ${detail.length.toLocaleString()} entries loaded in this view.`\n\t\t\t\t\t: \"Length reported by Redis (entries, or bytes for strings).\",\n\t\t},\n\t\t{\n\t\t\tname: \"REV\",\n\t\t\tvalue: detail.revision.slice(0, 8),\n\t\t\thint: \"Optimistic-lock revision. Writes are rejected when the key changed since this fetch.\",\n\t\t},\n\t\t{\n\t\t\tname: \"AT\",\n\t\t\tvalue: new Date(fetchedAt).toLocaleTimeString(),\n\t\t\thint: \"When this snapshot was fetched from Redis.\",\n\t\t},\n\t];\n\n\treturn (\n\t\t<header className=\"border-b border-border\">\n\t\t\t<div className=\"flex min-h-10 items-stretch justify-between\">\n\t\t\t\t<div className=\"flex min-w-0 flex-1 items-stretch overflow-x-auto\">\n\t\t\t\t\t<div className=\"flex min-w-0 max-w-[min(36rem,45vw)] shrink items-center gap-1 px-3 py-1.5\">\n\t\t\t\t\t\t<code\n\t\t\t\t\t\t\tclassName=\"min-w-0 truncate text-sm text-foreground\"\n\t\t\t\t\t\t\ttitle={label}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{label || \"(empty key)\"}\n\t\t\t\t\t\t</code>\n\t\t\t\t\t\t<RedisCopyButton\n\t\t\t\t\t\t\ttext={detail.key.utf8 ?? detail.key.base64}\n\t\t\t\t\t\t\tlabel=\"Copy key name\"\n\t\t\t\t\t\t\tclassName=\"shrink-0 text-muted-foreground\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tclassName=\"h-4 shrink-0 rounded-sm px-1.5 text-[8px] font-normal uppercase text-muted-foreground\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{detail.type}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t</div>\n\t\t\t\t\t{stats.map(({ name, value, hint, alert }, index) => (\n\t\t\t\t\t\t<Tooltip key={name}>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex shrink-0 cursor-default items-center gap-1.5 border-s border-border px-2 text-[10px]\",\n\t\t\t\t\t\t\t\t\t\tindex === stats.length - 1 && \"border-e\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span className=\"text-muted-foreground\">{name}</span>\n\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"tabular-nums\",\n\t\t\t\t\t\t\t\t\t\t\talert ? \"text-destructive\" : \"text-foreground\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent side=\"bottom\">{hint}</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex shrink-0 items-stretch\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\tonClick={onRefresh}\n\t\t\t\t\t\tdisabled={isFetching}\n\t\t\t\t\t>\n\t\t\t\t\t\t<RefreshCw className={cn(isFetching && \"animate-spin\")} /> Refresh\n\t\t\t\t\t</Button>\n\t\t\t\t\t{command && (\n\t\t\t\t\t\t<Tooltip>\n\t\t\t\t\t\t\t<TooltipTrigger asChild>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\t\t\t\tonClick={onOpenRunner}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Terminal /> Runner\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</TooltipTrigger>\n\t\t\t\t\t\t\t<TooltipContent side=\"bottom\">\n\t\t\t\t\t\t\t\tOpen in the query runner as <code className=\"font-mono\">{command}</code>\n\t\t\t\t\t\t\t</TooltipContent>\n\t\t\t\t\t\t</Tooltip>\n\t\t\t\t\t)}\n\t\t\t\t\t<Popover>\n\t\t\t\t\t\t<PopoverTrigger asChild>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Pencil /> Edit\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</PopoverTrigger>\n\t\t\t\t\t\t<PopoverContent\n\t\t\t\t\t\t\talign=\"end\"\n\t\t\t\t\t\t\tclassName=\"w-72 rounded-sm p-3 font-mono\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<FieldGroup className=\"gap-4\">\n\t\t\t\t\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t\t\t\t\t<FieldLabel htmlFor=\"redis-key-rename\">Rename key</FieldLabel>\n\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex\"\n\t\t\t\t\t\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\tif (rename === null || rename.base64 === detail.key.base64 || pending)\n\t\t\t\t\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t\t\t\t\tact({ action: \"rename\", newKey: rename });\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\t\t\t\t\t\tid=\"redis-key-rename\"\n\t\t\t\t\t\t\t\t\t\t\tvalue={rename}\n\t\t\t\t\t\t\t\t\t\t\tonChange={setRename}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-e-none font-mono\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"icon\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-auto self-stretch rounded-s-none border-0 bg-clip-border\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\t\t\trename === null || rename.base64 === detail.key.base64 || pending\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Rename key\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<ArrowRight />\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t</Field>\n\t\t\t\t\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t\t\t\t\t<FieldLabel htmlFor=\"redis-key-ttl-operation\">TTL in seconds</FieldLabel>\n\t\t\t\t\t\t\t\t\t<form\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex\"\n\t\t\t\t\t\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\t\t\t\t\tif (pending) return;\n\t\t\t\t\t\t\t\t\t\t\tact({ action: \"setTtl\", ttlMs: ttl ? Number(ttl) * 1_000 : null });\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\t\tid=\"redis-key-ttl-operation\"\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\t\tvalue={ttl}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(event) => setTtl(event.target.value)}\n\t\t\t\t\t\t\t\t\t\t\tplaceholder={\n\t\t\t\t\t\t\t\t\t\t\t\tdetail.ttlMs > 0\n\t\t\t\t\t\t\t\t\t\t\t\t\t? String(Math.ceil(detail.ttlMs / 1_000))\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Persistent\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"rounded-e-none font-mono\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-auto self-stretch rounded-s-none border-0 bg-clip-border\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tApply\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t\t\t\t\t\t{TTL_PRESETS.map((preset) => (\n\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\tkey={preset.label}\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => act({ action: \"setTtl\", ttlMs: preset.ttlMs })}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t{preset.label}\n\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={pending || persistent}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => act({ action: \"setTtl\", ttlMs: null })}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tPersist\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<FieldDescription>Leave empty to persist the key.</FieldDescription>\n\t\t\t\t\t\t\t\t</Field>\n\t\t\t\t\t\t\t</FieldGroup>\n\t\t\t\t\t\t</PopoverContent>\n\t\t\t\t\t</Popover>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tclassName=\"h-auto rounded-none border-y-0 border-s border-e-0 border-border px-2\"\n\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\tonClick={onDelete}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash2 /> Delete\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</header>\n\t);\n};\n","import type {\n\tEncodedRedisValueSchemaType,\n\tKeyActionSchemaType,\n\tKeyDetailsResultSchemaType,\n} from \"@db-studio/shared/types\";\nimport { Alert } from \"@db-studio/ui/alert\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Field, FieldLabel } from \"@db-studio/ui/field\";\nimport { Input } from \"@db-studio/ui/input\";\nimport { ToggleGroup, ToggleGroupItem } from \"@db-studio/ui/toggle-group\";\nimport { Plus, Save, Search, Trash2 } from \"lucide-react\";\nimport { type FormEvent, type ReactNode, useState } from \"react\";\nimport { RedisCopyButton } from \"./redis-copy-button\";\nimport { encodeTextValue, RedisValue, RedisValueInput } from \"./redis-value\";\n\n/**\n * Runs one key operation against the server. Resolves `true` on success so\n * add-forms know when to clear; conflict handling lives in the screen.\n */\nexport type RedisAct = (\n\toperation: KeyActionSchemaType[\"operation\"],\n\tforce?: boolean,\n) => Promise<boolean>;\n\nexport type StreamDirection = \"forward\" | \"backward\";\n\ntype RedisValueUnion = KeyDetailsResultSchemaType[\"value\"];\ntype HashValue = Extract<RedisValueUnion, { kind: \"hash\" }>;\ntype ListValue = Extract<RedisValueUnion, { kind: \"list\" }>;\ntype SetValue = Extract<RedisValueUnion, { kind: \"set\" }>;\ntype ZsetValue = Extract<RedisValueUnion, { kind: \"zset\" }>;\ntype StreamValue = Extract<RedisValueUnion, { kind: \"stream\" }>;\n\nconst emptyValue = encodeTextValue(\"\");\n\n/** `null` = the input's visible draft is unparseable and MUST NOT be submitted. */\ntype DraftValue = EncodedRedisValueSchemaType | null;\n\nconst textOf = (value: EncodedRedisValueSchemaType): string =>\n\tvalue.utf8 ?? `base64:${value.base64}`;\n\nconst matches = (needle: string, ...haystack: (string | number)[]): boolean =>\n\thaystack.some((part) => String(part).toLowerCase().includes(needle));\n\n/** Serializes the loaded (client-side) entries so devs can yank them into code. */\nconst collectionJson = (value: RedisValueUnion): string => {\n\tswitch (value.kind) {\n\t\tcase \"string\":\n\t\t\treturn JSON.stringify(textOf(value.value), null, 2);\n\t\tcase \"hash\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tObject.fromEntries(\n\t\t\t\t\tvalue.entries.map((entry) => [textOf(entry.field), textOf(entry.value)]),\n\t\t\t\t),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"list\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => textOf(entry.value)),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"set\":\n\t\t\treturn JSON.stringify(value.members.map(textOf), null, 2);\n\t\tcase \"zset\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => ({ member: textOf(entry.member), score: entry.score })),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tcase \"stream\":\n\t\t\treturn JSON.stringify(\n\t\t\t\tvalue.entries.map((entry) => ({\n\t\t\t\t\tid: entry.id,\n\t\t\t\t\tfields: Object.fromEntries(\n\t\t\t\t\t\tentry.fields.map((field) => [textOf(field.field), textOf(field.value)]),\n\t\t\t\t\t),\n\t\t\t\t})),\n\t\t\t\tnull,\n\t\t\t\t2,\n\t\t\t);\n\t\tdefault:\n\t\t\treturn \"\";\n\t}\n};\n\n/**\n * Local draft that re-syncs whenever the server value changes (identified by\n * `revision`), without wiping unrelated drafts on every refetch.\n */\nconst useSyncedDraft = <T,>(server: T, revision: string): [T, (next: T) => void] => {\n\tconst [draft, setDraft] = useState(server);\n\tconst [synced, setSynced] = useState(revision);\n\tif (synced !== revision) {\n\t\tsetSynced(revision);\n\t\tsetDraft(server);\n\t}\n\treturn [draft, setDraft];\n};\n\nconst EmptyRow = ({ filtered }: { filtered: boolean }) => (\n\t<div className=\"flex h-10 items-center justify-center px-2 text-xs text-muted-foreground\">\n\t\t{filtered ? \"No loaded entries match the filter.\" : \"No entries loaded.\"}\n\t</div>\n);\n\n/**\n * Frame around every collection editor: a client-side filter over the loaded\n * entries, a loaded/total counter, copy-as-JSON, and an optional `extra` slot\n * (e.g. the stream direction toggle).\n */\nconst CollectionShell = ({\n\tfilter,\n\tonFilterChange,\n\tshownCount,\n\tloadedCount,\n\ttotalCount,\n\tcopyJson,\n\textra,\n\theader,\n\tchildren,\n\tfooter,\n}: {\n\tfilter: string;\n\tonFilterChange: (next: string) => void;\n\tshownCount: number;\n\tloadedCount: number;\n\ttotalCount: number | null;\n\tcopyJson: () => string;\n\textra?: ReactNode;\n\theader: ReactNode;\n\tchildren: ReactNode;\n\tfooter: ReactNode;\n}) => (\n\t<div className=\"overflow-hidden border border-border\">\n\t\t<div className=\"flex h-8 items-center gap-1 border-b border-border bg-muted/10 px-1\">\n\t\t\t<Search className=\"ms-1 size-3 shrink-0 text-muted-foreground\" />\n\t\t\t<Input\n\t\t\t\tvariant=\"outline\"\n\t\t\t\tvalue={filter}\n\t\t\t\tonChange={(event) => onFilterChange(event.target.value)}\n\t\t\t\tplaceholder=\"Filter loaded entries\"\n\t\t\t\taria-label=\"Filter loaded entries\"\n\t\t\t\tclassName=\"h-6 flex-1 rounded-none border-none px-1 font-mono focus-visible:ring-0\"\n\t\t\t/>\n\t\t\t{extra}\n\t\t\t<span className=\"shrink-0 px-1 text-[10px] tabular-nums text-muted-foreground\">\n\t\t\t\t{filter\n\t\t\t\t\t? `${shownCount} / ${loadedCount}`\n\t\t\t\t\t: totalCount !== null && totalCount > loadedCount\n\t\t\t\t\t\t? `${loadedCount.toLocaleString()} of ${totalCount.toLocaleString()}`\n\t\t\t\t\t\t: loadedCount.toLocaleString()}\n\t\t\t</span>\n\t\t\t<RedisCopyButton\n\t\t\t\ttext={copyJson}\n\t\t\t\tlabel=\"Copy loaded entries as JSON\"\n\t\t\t/>\n\t\t</div>\n\t\t{header}\n\t\t{children}\n\t\t{footer}\n\t</div>\n);\n\nconst AddPair = ({\n\tlabels,\n\tonSave,\n\tpending,\n\tscore = false,\n}: {\n\tlabels: [string, string];\n\tonSave: (\n\t\tfirst: EncodedRedisValueSchemaType,\n\t\tsecond: EncodedRedisValueSchemaType | number,\n\t) => Promise<boolean>;\n\tpending: boolean;\n\tscore?: boolean;\n}) => {\n\tconst [first, setFirst] = useState<DraftValue>(emptyValue);\n\tconst [second, setSecond] = useState<DraftValue>(emptyValue);\n\tconst [numeric, setNumeric] = useState(\"0\");\n\tconst invalid = first === null || (!score && second === null);\n\tconst submit = async (event: FormEvent) => {\n\t\tevent.preventDefault();\n\t\tif (first === null || second === null) return;\n\t\tconst saved = await onSave(first, score ? Number(numeric) : second);\n\t\tif (!saved) return;\n\t\tsetFirst(emptyValue);\n\t\tsetSecond(emptyValue);\n\t\tsetNumeric(\"0\");\n\t};\n\treturn (\n\t\t<form\n\t\t\tonSubmit={submit}\n\t\t\tclassName=\"grid grid-cols-[1fr_1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={first}\n\t\t\t\tonChange={setFirst}\n\t\t\t\tplaceholder={labels[0]}\n\t\t\t/>\n\t\t\t{score ? (\n\t\t\t\t<Input\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tstep=\"any\"\n\t\t\t\t\tvalue={numeric}\n\t\t\t\t\tonChange={(event) => setNumeric(event.target.value)}\n\t\t\t\t\tplaceholder={labels[1]}\n\t\t\t\t\tclassName=\"font-mono\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<RedisValueInput\n\t\t\t\t\tvalue={second}\n\t\t\t\t\tonChange={setSecond}\n\t\t\t\t\tplaceholder={labels[1]}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<Button\n\t\t\t\ttype=\"submit\"\n\t\t\t\tdisabled={pending || invalid}\n\t\t\t>\n\t\t\t\t<Plus /> Add\n\t\t\t</Button>\n\t\t</form>\n\t);\n};\n\nconst StringEditor = ({\n\tvalue,\n\ttruncated,\n\tact,\n\tpending,\n\tonLoadFull,\n\tonDownload,\n}: {\n\tvalue: EncodedRedisValueSchemaType;\n\ttruncated: boolean;\n\tact: RedisAct;\n\tpending: boolean;\n\tonLoadFull: () => void;\n\tonDownload: () => void;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, value.base64);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"space-y-3\">\n\t\t\t{truncated && (\n\t\t\t\t<div className=\"grid gap-2 border-b border-border pb-3 sm:grid-cols-[1fr_auto] sm:items-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\ttitle=\"Preview truncated\"\n\t\t\t\t\t\tmessage=\"Load the full value before editing (up to the 8 MiB response cap).\"\n\t\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={onLoadFull}\n\t\t\t\t\t>\n\t\t\t\t\t\tLoad full\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<RedisValue\n\t\t\t\tvalue={value}\n\t\t\t\tonDownload={onDownload}\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tif (draft !== null && dirty) act({ action: \"setString\", value: draft });\n\t\t\t\t}}\n\t\t\t\tclassName=\"border border-border p-3\"\n\t\t\t>\n\t\t\t\t<Field className=\"gap-1.5\">\n\t\t\t\t\t<FieldLabel htmlFor=\"redis-string-value\">Replace value</FieldLabel>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tid=\"redis-string-value\"\n\t\t\t\t\t\tvalue={draft}\n\t\t\t\t\t\tonChange={setDraft}\n\t\t\t\t\t/>\n\t\t\t\t</Field>\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\tclassName=\"mt-3\"\n\t\t\t\t\tdisabled={truncated || pending || !dirty}\n\t\t\t\t>\n\t\t\t\t\t<Save /> Save value\n\t\t\t\t</Button>\n\t\t\t</form>\n\t\t</div>\n\t);\n};\n\nconst HashRow = ({\n\tfield,\n\tvalue,\n\tact,\n\tpending,\n}: {\n\tfield: EncodedRedisValueSchemaType;\n\tvalue: EncodedRedisValueSchemaType;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, value.base64);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<RedisValue\n\t\t\t\tvalue={field}\n\t\t\t\tcompact\n\t\t\t/>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={setDraft}\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty}\n\t\t\t\t\tonClick={() => draft !== null && act({ action: \"upsertHash\", field, value: draft })}\n\t\t\t\t\taria-label=\"Save hash value\"\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"deleteHash\", field })}\n\t\t\t\t\taria-label=\"Delete hash field\"\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst HashEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: HashValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) =>\n\t\t\t\tmatches(needle, textOf(entry.field), textOf(entry.value)),\n\t\t\t)\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[minmax(10rem,0.7fr)_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Field</span>\n\t\t\t\t\t<span>Value</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tlabels={[\"field\", \"value\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(field, next) =>\n\t\t\t\t\t\tact({ action: \"upsertHash\", field, value: next as EncodedRedisValueSchemaType })\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<HashRow\n\t\t\t\t\t\tkey={entry.field.base64}\n\t\t\t\t\t\tfield={entry.field}\n\t\t\t\t\t\tvalue={entry.value}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst ListRow = ({\n\tindex,\n\tvalue,\n\trevision,\n\tact,\n\tpending,\n}: {\n\tindex: number;\n\tvalue: EncodedRedisValueSchemaType;\n\trevision: string;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft<DraftValue>(value, `${revision}:${value.base64}`);\n\tconst dirty = draft !== null && draft.base64 !== value.base64;\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[3rem_1fr_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<code className=\"text-xs text-muted-foreground\">{index}</code>\n\t\t\t<RedisValueInput\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={setDraft}\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty}\n\t\t\t\t\tonClick={() => draft !== null && act({ action: \"setList\", index, value: draft })}\n\t\t\t\t\taria-label={`Save list item ${index}`}\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"deleteList\", index })}\n\t\t\t\t\taria-label={`Delete list item ${index}`}\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst ListEditor = ({\n\tvalue,\n\trevision,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: ListValue;\n\trevision: string;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst [item, setItem] = useState<DraftValue>(emptyValue);\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) => matches(needle, entry.index, textOf(entry.value)))\n\t\t: value.entries;\n\tconst push = async (side: \"left\" | \"right\") => {\n\t\tif (item === null) return;\n\t\tconst saved = await act({ action: \"pushList\", side, value: item });\n\t\tif (saved) setItem(emptyValue);\n\t};\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[3rem_1fr_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Index</span>\n\t\t\t\t\t<span>Value</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<form\n\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tpush(\"right\");\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"grid grid-cols-[1fr_auto_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t\t\t>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tvalue={item}\n\t\t\t\t\t\tonChange={setItem}\n\t\t\t\t\t\tplaceholder=\"new item\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tdisabled={pending || item === null}\n\t\t\t\t\t\tonClick={() => push(\"left\")}\n\t\t\t\t\t>\n\t\t\t\t\t\tPush left\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tdisabled={pending || item === null}\n\t\t\t\t\t>\n\t\t\t\t\t\tPush right\n\t\t\t\t\t</Button>\n\t\t\t\t</form>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<ListRow\n\t\t\t\t\t\tkey={entry.index}\n\t\t\t\t\t\tindex={entry.index}\n\t\t\t\t\t\tvalue={entry.value}\n\t\t\t\t\t\trevision={revision}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst SetEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: SetValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst [member, setMember] = useState<DraftValue>(emptyValue);\n\tconst needle = filter.trim().toLowerCase();\n\tconst members = needle\n\t\t? value.members.filter((entry) => matches(needle, textOf(entry)))\n\t\t: value.members;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={members.length}\n\t\t\tloadedCount={value.members.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Member</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<form\n\t\t\t\t\tonSubmit={async (event) => {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\tif (member === null) return;\n\t\t\t\t\t\tconst saved = await act({ action: \"addSet\", member });\n\t\t\t\t\t\tif (saved) setMember(emptyValue);\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"grid grid-cols-[1fr_auto] items-center gap-2 border-t border-border bg-muted/10 p-2\"\n\t\t\t\t>\n\t\t\t\t\t<RedisValueInput\n\t\t\t\t\t\tvalue={member}\n\t\t\t\t\t\tonChange={setMember}\n\t\t\t\t\t\tplaceholder=\"new member\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\tdisabled={pending || member === null}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Plus /> Add member\n\t\t\t\t\t</Button>\n\t\t\t\t</form>\n\t\t\t}\n\t\t>\n\t\t\t{members.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tmembers.map((entry) => (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={entry.base64}\n\t\t\t\t\t\tclassName=\"flex min-h-8 items-center gap-2 border-b border-border px-2 py-1\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\tvalue={entry}\n\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\tonClick={() => act({ action: \"removeSet\", member: entry })}\n\t\t\t\t\t\t\taria-label=\"Remove set member\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst ZsetRow = ({\n\tmember,\n\tscore,\n\tact,\n\tpending,\n}: {\n\tmember: EncodedRedisValueSchemaType;\n\tscore: number | \"inf\" | \"-inf\";\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [draft, setDraft] = useSyncedDraft(String(score), String(score));\n\tconst parsedDraft = Number(draft);\n\tconst dirty = draft !== String(score);\n\tconst valid = Number.isFinite(parsedDraft);\n\treturn (\n\t\t<div className=\"grid min-h-8 grid-cols-[1fr_8rem_5rem] items-center gap-2 border-b border-border px-2 py-1\">\n\t\t\t<RedisValue\n\t\t\t\tvalue={member}\n\t\t\t\tcompact\n\t\t\t/>\n\t\t\t<Input\n\t\t\t\tinputMode=\"decimal\"\n\t\t\t\tvalue={draft}\n\t\t\t\tonChange={(event) => setDraft(event.target.value)}\n\t\t\t\taria-label=\"Sorted set score\"\n\t\t\t\tclassName=\"font-mono\"\n\t\t\t/>\n\t\t\t<div className=\"flex\">\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName={dirty ? \"text-primary\" : undefined}\n\t\t\t\t\tdisabled={pending || !dirty || !valid}\n\t\t\t\t\tonClick={() => act({ action: \"upsertZset\", member, score: parsedDraft })}\n\t\t\t\t\taria-label=\"Save sorted set score\"\n\t\t\t\t>\n\t\t\t\t\t<Save />\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\tclassName=\"text-muted-foreground hover:text-destructive\"\n\t\t\t\t\tdisabled={pending}\n\t\t\t\t\tonClick={() => act({ action: \"removeZset\", member })}\n\t\t\t\t\taria-label=\"Remove sorted set member\"\n\t\t\t\t>\n\t\t\t\t\t<Trash2 />\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n};\n\nconst ZsetEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n}: {\n\tvalue: ZsetValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) => matches(needle, textOf(entry.member), entry.score))\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[1fr_8rem_5rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Member</span>\n\t\t\t\t\t<span>Score</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tscore\n\t\t\t\t\tlabels={[\"member\", \"score\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(member, score) =>\n\t\t\t\t\t\tact({ action: \"upsertZset\", member, score: score as number })\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<ZsetRow\n\t\t\t\t\t\tkey={entry.member.base64}\n\t\t\t\t\t\tmember={entry.member}\n\t\t\t\t\t\tscore={entry.score}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={pending}\n\t\t\t\t\t/>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\nconst StreamEditor = ({\n\tvalue,\n\ttotal,\n\tact,\n\tpending,\n\tdirection,\n\tonDirectionChange,\n}: {\n\tvalue: StreamValue;\n\ttotal: number | null;\n\tact: RedisAct;\n\tpending: boolean;\n\tdirection: StreamDirection;\n\tonDirectionChange: (direction: StreamDirection) => void;\n}) => {\n\tconst [filter, setFilter] = useState(\"\");\n\tconst needle = filter.trim().toLowerCase();\n\tconst entries = needle\n\t\t? value.entries.filter((entry) =>\n\t\t\t\tmatches(\n\t\t\t\t\tneedle,\n\t\t\t\t\tentry.id,\n\t\t\t\t\t...entry.fields.flatMap((field) => [textOf(field.field), textOf(field.value)]),\n\t\t\t\t),\n\t\t\t)\n\t\t: value.entries;\n\treturn (\n\t\t<CollectionShell\n\t\t\tfilter={filter}\n\t\t\tonFilterChange={setFilter}\n\t\t\tshownCount={entries.length}\n\t\t\tloadedCount={value.entries.length}\n\t\t\ttotalCount={total}\n\t\t\tcopyJson={() => collectionJson(value)}\n\t\t\textra={\n\t\t\t\t<ToggleGroup\n\t\t\t\t\ttype=\"single\"\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tvalue={direction}\n\t\t\t\t\tonValueChange={(next) => next && onDirectionChange(next as StreamDirection)}\n\t\t\t\t\taria-label=\"Stream order\"\n\t\t\t\t\tclassName=\"shrink-0 rounded-none\"\n\t\t\t\t>\n\t\t\t\t\t<ToggleGroupItem\n\t\t\t\t\t\tvalue=\"forward\"\n\t\t\t\t\t\tclassName=\"h-6 rounded-none px-2 text-[10px]\"\n\t\t\t\t\t>\n\t\t\t\t\t\tOldest first\n\t\t\t\t\t</ToggleGroupItem>\n\t\t\t\t\t<ToggleGroupItem\n\t\t\t\t\t\tvalue=\"backward\"\n\t\t\t\t\t\tclassName=\"h-6 rounded-none px-2 text-[10px]\"\n\t\t\t\t\t>\n\t\t\t\t\t\tNewest first\n\t\t\t\t\t</ToggleGroupItem>\n\t\t\t\t</ToggleGroup>\n\t\t\t}\n\t\t\theader={\n\t\t\t\t<div className=\"grid h-8 grid-cols-[10rem_1fr_2rem] items-center border-b border-border bg-muted/20 px-2 text-[10px] uppercase tracking-wider text-muted-foreground\">\n\t\t\t\t\t<span>Entry ID</span>\n\t\t\t\t\t<span>Fields</span>\n\t\t\t\t\t<span />\n\t\t\t\t</div>\n\t\t\t}\n\t\t\tfooter={\n\t\t\t\t<AddPair\n\t\t\t\t\tlabels={[\"field\", \"value\"]}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonSave={(field, next) =>\n\t\t\t\t\t\tact({\n\t\t\t\t\t\t\taction: \"appendStream\",\n\t\t\t\t\t\t\tid: \"*\",\n\t\t\t\t\t\t\tfields: [{ field, value: next as EncodedRedisValueSchemaType }],\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t{entries.length === 0 ? (\n\t\t\t\t<EmptyRow filtered={needle.length > 0} />\n\t\t\t) : (\n\t\t\t\tentries.map((entry) => (\n\t\t\t\t\t<div\n\t\t\t\t\t\tkey={entry.id}\n\t\t\t\t\t\tclassName=\"grid grid-cols-[10rem_1fr_2rem] border-b border-border\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<code className=\"border-e border-border px-2 py-1.5 text-xs text-primary\">\n\t\t\t\t\t\t\t{entry.id}\n\t\t\t\t\t\t</code>\n\t\t\t\t\t\t<div className=\"grid gap-1 px-2 py-1.5\">\n\t\t\t\t\t\t\t{entry.fields.map((field) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={field.field.base64}\n\t\t\t\t\t\t\t\t\tclassName=\"grid grid-cols-[minmax(8rem,0.4fr)_1fr] gap-3\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\t\t\tvalue={field.field}\n\t\t\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<RedisValue\n\t\t\t\t\t\t\t\t\t\tvalue={field.value}\n\t\t\t\t\t\t\t\t\t\tcompact\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"icon-sm\"\n\t\t\t\t\t\t\tclassName=\"rounded-none text-muted-foreground hover:text-destructive\"\n\t\t\t\t\t\t\tdisabled={pending}\n\t\t\t\t\t\t\tonClick={() => act({ action: \"deleteStream\", id: entry.id })}\n\t\t\t\t\t\t\taria-label={`Delete stream entry ${entry.id}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash2 />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t))\n\t\t\t)}\n\t\t</CollectionShell>\n\t);\n};\n\n/** Dispatches the loaded key to the editor matching its Redis type. */\nexport const RedisValueEditor = ({\n\tdetail,\n\tact,\n\tpending,\n\tonLoadFull,\n\tonDownload,\n\tstreamDirection,\n\tonStreamDirectionChange,\n}: {\n\tdetail: KeyDetailsResultSchemaType;\n\tact: RedisAct;\n\tpending: boolean;\n\tonLoadFull: () => void;\n\tonDownload: () => void;\n\tstreamDirection: StreamDirection;\n\tonStreamDirectionChange: (direction: StreamDirection) => void;\n}) => {\n\tconst value = detail.value;\n\tswitch (value.kind) {\n\t\tcase \"string\":\n\t\t\treturn (\n\t\t\t\t<StringEditor\n\t\t\t\t\tvalue={value.value}\n\t\t\t\t\ttruncated={value.truncated}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonLoadFull={onLoadFull}\n\t\t\t\t\tonDownload={onDownload}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"hash\":\n\t\t\treturn (\n\t\t\t\t<HashEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"list\":\n\t\t\treturn (\n\t\t\t\t<ListEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\trevision={detail.revision}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"set\":\n\t\t\treturn (\n\t\t\t\t<SetEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"zset\":\n\t\t\treturn (\n\t\t\t\t<ZsetEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"stream\":\n\t\t\treturn (\n\t\t\t\t<StreamEditor\n\t\t\t\t\tvalue={value}\n\t\t\t\t\ttotal={detail.length}\n\t\t\t\t\tact={act}\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tdirection={streamDirection}\n\t\t\t\t\tonDirectionChange={onStreamDirectionChange}\n\t\t\t\t/>\n\t\t\t);\n\t\tdefault:\n\t\t\treturn (\n\t\t\t\t<Alert\n\t\t\t\t\tvariant=\"info\"\n\t\t\t\t\ttitle=\"Metadata only\"\n\t\t\t\t\tmessage=\"This Redis module type cannot be edited in this release.\"\n\t\t\t\t\tclassName=\"rounded-sm\"\n\t\t\t\t/>\n\t\t\t);\n\t}\n};\n","import type { KeyActionSchemaType, KeyDetailsResultSchemaType } from \"@db-studio/shared/types\";\nimport { Alert } from \"@db-studio/ui/alert\";\nimport {\n\tAlertDialog,\n\tAlertDialogAction,\n\tAlertDialogCancel,\n\tAlertDialogContent,\n\tAlertDialogDescription,\n\tAlertDialogFooter,\n\tAlertDialogHeader,\n\tAlertDialogTitle,\n} from \"@db-studio/ui/alert-dialog\";\nimport { Button } from \"@db-studio/ui/button\";\nimport { Spinner } from \"@db-studio/ui/spinner\";\nimport { useNavigate } from \"@tanstack/react-router\";\nimport { DatabaseZap, Plus } from \"lucide-react\";\nimport { useQueryState } from \"nuqs\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { getRedisStringChunk } from \"@/shared/api\";\nimport { useDatabaseStore } from \"@/stores/database.store\";\nimport { useOverlayStore } from \"@/stores/overlay.store\";\nimport { useQueriesStore } from \"@/stores/queries.store\";\nimport { RedisKeyHeader } from \"../components/redis-key-header\";\nimport { bytesFromBase64Url } from \"../components/redis-value\";\nimport {\n\ttype RedisAct,\n\tRedisValueEditor,\n\ttype StreamDirection,\n} from \"../components/redis-value-editors\";\nimport { useRedisKey, useRedisKeyMutations } from \"../hooks/use-redis-browser\";\n\n/** The read command a dev would run for this key, used by \"Open in runner\". */\nconst runnerCommand = (detail: KeyDetailsResultSchemaType): string | null => {\n\tif (detail.key.utf8 === undefined) return null;\n\tconst key = JSON.stringify(detail.key.utf8);\n\tswitch (detail.type) {\n\t\tcase \"string\":\n\t\t\treturn `GET ${key}`;\n\t\tcase \"hash\":\n\t\t\treturn `HGETALL ${key}`;\n\t\tcase \"list\":\n\t\t\treturn `LRANGE ${key} 0 -1`;\n\t\tcase \"set\":\n\t\t\treturn `SMEMBERS ${key}`;\n\t\tcase \"zset\":\n\t\t\treturn `ZRANGE ${key} 0 -1 WITHSCORES`;\n\t\tcase \"stream\":\n\t\t\treturn `XRANGE ${key} - + COUNT 100`;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\n\n/** Entries fetched so far across pages; null for scalar/unknown values. */\nconst countLoaded = (detail: KeyDetailsResultSchemaType): number | null => {\n\tswitch (detail.value.kind) {\n\t\tcase \"hash\":\n\t\tcase \"list\":\n\t\tcase \"zset\":\n\t\tcase \"stream\":\n\t\t\treturn detail.value.entries.length;\n\t\tcase \"set\":\n\t\t\treturn detail.value.members.length;\n\t\tdefault:\n\t\t\treturn null;\n\t}\n};\n\nexport const RedisBrowserScreen = () => {\n\tconst navigate = useNavigate();\n\tconst { selectedDatabase } = useDatabaseStore();\n\tconst { openOverlay } = useOverlayStore();\n\tconst { addQuery, updateQuery } = useQueriesStore();\n\tconst [selectedKey, setSelectedKey] = useQueryState(\"key\");\n\tconst [fullKey, setFullKey] = useState<string | null>(null);\n\tconst [reverseStreamKey, setReverseStreamKey] = useState<string | null>(null);\n\tconst streamDirection: StreamDirection =\n\t\treverseStreamKey === selectedKey ? \"backward\" : \"forward\";\n\tconst query = useRedisKey(selectedKey, fullKey === selectedKey, streamDirection);\n\tconst { applyAction, deleteKey, isPending } = useRedisKeyMutations();\n\tconst pages = query.data?.pages;\n\tconst detail = pages?.[0];\n\tconst [confirmingDelete, setConfirmingDelete] = useState(false);\n\tconst [conflictOperation, setConflictOperation] = useState<\n\t\tKeyActionSchemaType[\"operation\"] | null\n\t>(null);\n\tconst label = detail?.key.utf8 ?? (detail ? `binary:${detail.key.base64}` : \"\");\n\tconst command = detail ? runnerCommand(detail) : null;\n\n\tconst mergedDetail = useMemo(() => {\n\t\tif (!detail || !pages || pages.length === 1) return detail;\n\t\tconst next = structuredClone(detail);\n\t\tfor (const page of pages.slice(1)) {\n\t\t\tif (next.value.kind === \"hash\" && page.value.kind === \"hash\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"list\" && page.value.kind === \"list\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"set\" && page.value.kind === \"set\")\n\t\t\t\tnext.value.members.push(...page.value.members);\n\t\t\tif (next.value.kind === \"zset\" && page.value.kind === \"zset\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t\tif (next.value.kind === \"stream\" && page.value.kind === \"stream\")\n\t\t\t\tnext.value.entries.push(...page.value.entries);\n\t\t}\n\t\treturn next;\n\t}, [detail, pages]);\n\tconst loadedCount = mergedDetail ? countLoaded(mergedDetail) : null;\n\n\tconst act: RedisAct = async (operation, force = false) => {\n\t\tif (!selectedKey || !detail) return false;\n\t\ttry {\n\t\t\tconst result = await applyAction(selectedKey, {\n\t\t\t\texpectedRevision: detail.revision,\n\t\t\t\tforce,\n\t\t\t\toperation,\n\t\t\t});\n\t\t\tsetConflictOperation(null);\n\t\t\tif (result.deleted) setSelectedKey(null);\n\t\t\telse if ((result.key.base64 || \"-\") !== selectedKey)\n\t\t\t\tsetSelectedKey(result.key.base64 || \"-\");\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\t// toast.promise in the mutation hook already surfaced the failure.\n\t\t\tif ((error as Error & { status?: number }).status === 409)\n\t\t\t\tsetConflictOperation(operation);\n\t\t\treturn false;\n\t\t}\n\t};\n\n\tconst openInRunner = () => {\n\t\tif (!command) return;\n\t\tconst queryId = addQuery(`redis: ${label.slice(0, 40)}`);\n\t\tupdateQuery(queryId, { query: command });\n\t\tnavigate({ to: \"/runner/$queryId\", params: { queryId } });\n\t};\n\n\tconst downloadOriginalString = async () => {\n\t\tif (!selectedKey || !detail || detail.type !== \"string\") return;\n\t\tconst chunks: Uint8Array[] = [];\n\t\tlet offset = 0;\n\t\tlet hasMore = true;\n\t\twhile (hasMore) {\n\t\t\tconst response = await getRedisStringChunk({\n\t\t\t\tdb: selectedDatabase ?? \"\",\n\t\t\t\tkey: selectedKey,\n\t\t\t\toffset,\n\t\t\t\texpectedRevision: detail.revision,\n\t\t\t});\n\t\t\tconst page = response.data.data;\n\t\t\tchunks.push(bytesFromBase64Url(page.chunk.base64));\n\t\t\thasMore = page.hasMore;\n\t\t\tif (page.hasMore && (page.nextOffset === null || page.nextOffset <= offset)) {\n\t\t\t\tthrow new Error(\"Redis download made no forward progress\");\n\t\t\t}\n\t\t\toffset = page.nextOffset ?? offset;\n\t\t}\n\t\tconst size = chunks.reduce((total, chunk) => total + chunk.length, 0);\n\t\tconst bytes = new Uint8Array(size);\n\t\tlet position = 0;\n\t\tfor (const chunk of chunks) {\n\t\t\tbytes.set(chunk, position);\n\t\t\tposition += chunk.length;\n\t\t}\n\t\tconst url = URL.createObjectURL(new Blob([bytes.buffer as ArrayBuffer]));\n\t\tconst anchor = document.createElement(\"a\");\n\t\tanchor.href = url;\n\t\tanchor.download = `${detail.key.utf8?.replaceAll(/[^\\w.-]+/g, \"_\") || \"redis-value\"}.bin`;\n\t\tanchor.click();\n\t\tURL.revokeObjectURL(url);\n\t};\n\n\tif (!selectedKey)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center bg-background font-mono\">\n\t\t\t\t<div className=\"max-w-sm text-center\">\n\t\t\t\t\t<DatabaseZap className=\"mx-auto mb-3 size-5 text-muted-foreground\" />\n\t\t\t\t\t<h1 className=\"text-sm font-medium text-foreground\">No key selected</h1>\n\t\t\t\t\t<p className=\"mt-1 text-xs leading-5 text-muted-foreground\">\n\t\t\t\t\t\tSelect a key from the navigator or create one to inspect its value.\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"mt-3\"\n\t\t\t\t\t\tonClick={() => openOverlay(\"redis-browser.create-key\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Plus /> Create key\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</main>\n\t\t);\n\tif (query.isLoading)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center\">\n\t\t\t\t<Spinner />\n\t\t\t</main>\n\t\t);\n\tif (query.error || !detail || !mergedDetail)\n\t\treturn (\n\t\t\t<main className=\"flex flex-1 items-center justify-center bg-background p-4\">\n\t\t\t\t<div className=\"w-full max-w-md text-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"error\"\n\t\t\t\t\t\ttitle=\"Unable to load key\"\n\t\t\t\t\t\tmessage={query.error?.message ?? \"The key no longer exists.\"}\n\t\t\t\t\t\tclassName=\"rounded-sm text-start\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tclassName=\"mt-4\"\n\t\t\t\t\t\tonClick={() => setSelectedKey(null)}\n\t\t\t\t\t>\n\t\t\t\t\t\tBack to keyspace\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</main>\n\t\t);\n\n\tconst remaining =\n\t\tmergedDetail.length !== null && loadedCount !== null\n\t\t\t? Math.max(0, mergedDetail.length - loadedCount)\n\t\t\t: null;\n\n\treturn (\n\t\t<main className=\"flex min-h-0 flex-1 flex-col bg-background font-mono\">\n\t\t\t<RedisKeyHeader\n\t\t\t\tkey={detail.key.base64}\n\t\t\t\tdetail={detail}\n\t\t\t\tlabel={label}\n\t\t\t\tloadedCount={loadedCount}\n\t\t\t\tfetchedAt={query.dataUpdatedAt}\n\t\t\t\tisFetching={query.isFetching}\n\t\t\t\tpending={isPending}\n\t\t\t\tcommand={command}\n\t\t\t\tact={act}\n\t\t\t\tonRefresh={() => query.refetch()}\n\t\t\t\tonOpenRunner={openInRunner}\n\t\t\t\tonDelete={() => setConfirmingDelete(true)}\n\t\t\t/>\n\t\t\t<AlertDialog\n\t\t\t\topen={confirmingDelete}\n\t\t\t\tonOpenChange={setConfirmingDelete}\n\t\t\t>\n\t\t\t\t<AlertDialogContent>\n\t\t\t\t\t<AlertDialogHeader>\n\t\t\t\t\t\t<AlertDialogTitle>Delete key</AlertDialogTitle>\n\t\t\t\t\t\t<AlertDialogDescription>\n\t\t\t\t\t\t\tAre you sure you want to delete{\" \"}\n\t\t\t\t\t\t\t<code className=\"break-all text-foreground\">{label || \"(empty key)\"}</code>? This\n\t\t\t\t\t\t\taction cannot be undone.\n\t\t\t\t\t\t</AlertDialogDescription>\n\t\t\t\t\t</AlertDialogHeader>\n\t\t\t\t\t<AlertDialogFooter>\n\t\t\t\t\t\t<AlertDialogCancel>Cancel</AlertDialogCancel>\n\t\t\t\t\t\t<AlertDialogAction\n\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\t\t\tsetConfirmingDelete(false);\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait deleteKey(selectedKey, detail.revision);\n\t\t\t\t\t\t\t\t\tsetSelectedKey(null);\n\t\t\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t\t\t// toast.promise in the mutation hook already surfaced the failure.\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t</AlertDialogAction>\n\t\t\t\t\t</AlertDialogFooter>\n\t\t\t\t</AlertDialogContent>\n\t\t\t</AlertDialog>\n\t\t\t{conflictOperation && (\n\t\t\t\t<div className=\"grid gap-2 border-b border-border p-2 sm:grid-cols-[1fr_auto] sm:items-center\">\n\t\t\t\t\t<Alert\n\t\t\t\t\t\tvariant=\"warning\"\n\t\t\t\t\t\ttitle=\"Revision conflict\"\n\t\t\t\t\t\tmessage=\"This key changed on the server. Reload it or overwrite with the pending change.\"\n\t\t\t\t\t\tclassName=\"rounded-sm py-2\"\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tsetConflictOperation(null);\n\t\t\t\t\t\t\t\tquery.refetch();\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tReload\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tdisabled={isPending}\n\t\t\t\t\t\t\tonClick={() => act(conflictOperation, true)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tOverwrite\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"min-h-0 flex-1 overflow-auto\">\n\t\t\t\t<section className=\"p-3\">\n\t\t\t\t\t<RedisValueEditor\n\t\t\t\t\t\tkey={detail.key.base64}\n\t\t\t\t\t\tdetail={mergedDetail}\n\t\t\t\t\t\tact={act}\n\t\t\t\t\t\tpending={isPending}\n\t\t\t\t\t\tonLoadFull={() => setFullKey(selectedKey)}\n\t\t\t\t\t\tonDownload={() => {\n\t\t\t\t\t\t\ttoast.promise(downloadOriginalString(), {\n\t\t\t\t\t\t\t\tloading: \"Downloading original Redis bytes…\",\n\t\t\t\t\t\t\t\tsuccess: \"Redis value downloaded\",\n\t\t\t\t\t\t\t\terror: (error: Error) => error.message || \"Download failed\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tstreamDirection={streamDirection}\n\t\t\t\t\t\tonStreamDirectionChange={(direction) =>\n\t\t\t\t\t\t\tsetReverseStreamKey(direction === \"backward\" ? selectedKey : null)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t\t{query.hasNextPage && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tclassName=\"mt-3 w-full\"\n\t\t\t\t\t\t\tonClick={() => query.fetchNextPage()}\n\t\t\t\t\t\t\tdisabled={query.isFetchingNextPage}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{query.isFetchingNextPage\n\t\t\t\t\t\t\t\t? \"Loading…\"\n\t\t\t\t\t\t\t\t: detail.type === \"stream\" && streamDirection === \"backward\"\n\t\t\t\t\t\t\t\t\t? \"Load 100 older entries\"\n\t\t\t\t\t\t\t\t\t: `Load next 100${remaining !== null ? ` (${remaining.toLocaleString()} remaining)` : \"\"}`}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</main>\n\t);\n};\n","import { createFileRoute } from \"@tanstack/react-router\";\nimport { RedisBrowserScreen } from \"@/features/redis-browser\";\n\nexport const Route = createFileRoute(\"/_pathlessLayout/browser\")({\n\tcomponent: RedisBrowserScreen,\n});\n"],"mappings":"60BASa,GAAA,CAAmB,CAC/B,KAAA,EACA,MAAA,EACA,UAAA,CAAA,IAKK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAK,EAC1C,SACC,EAAA,MAAC,EAAD,CAAA,SAAA,IACC,EAAA,KAAC,EAAD,CAAgB,QAAA,GACf,YAAA,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACM,UAAA,EACX,QAAS,SAAY,CACpB,MAAM,UAAU,UAAU,UAAU,OAAO,GAAS,WAAa,EAAK,EAAI,CAAI,EAC9E,EAAU,EAAI,EACd,OAAO,WAAA,IAAiB,EAAU,EAAK,EAAG,IAAK,CAChD,EACA,aAAY,EAEX,SAAA,KAAS,EAAA,KAAC,GAAD,CAAQ,CAAA,KAAI,EAAA,KAAC,GAAD,CAAO,CAAA,CACtB,CAAA,CACO,CAAA,KAChB,EAAA,KAAC,EAAD,CAAA,SAAiB,EAAS,SAAW,CAAsB,CAAA,CACnD,CAAA,CAAA,CAEX,ECtBM,GAAc,CACnB,CAAE,MAAO,MAAO,MAAO,GAAO,EAC9B,CAAE,MAAO,MAAO,MAAO,GAAQ,EAC/B,CAAE,MAAO,KAAM,MAAO,IAAU,EAChC,CAAE,MAAO,KAAM,MAAO,KAAW,EACjC,CAAE,MAAO,KAAM,MAAO,MAAY,CACnC,EAEM,GAAe,GAChB,IAAU,KAAa,IACvB,EAAQ,KAAc,GAAG,CAAA,KACzB,EAAQ,QAAkB,IAAI,EAAQ,MAAO,QAAQ,CAAC,CAAA,OACnD,IAAI,EAAQ,SAAW,QAAQ,CAAC,CAAA,OAGlC,GAAkB,GAAuB,CAC9C,MAAM,EAAU,KAAK,IAAI,EAAG,KAAK,KAAK,EAAK,GAAK,CAAC,EACjD,GAAI,EAAU,GAAI,MAAO,GAAG,CAAA,IAC5B,MAAM,EAAU,KAAK,MAAM,EAAU,EAAE,EACvC,GAAI,EAAU,GAAI,MAAO,GAAG,CAAA,KAAY,EAAU,EAAA,IAClD,MAAM,EAAQ,KAAK,MAAM,EAAU,EAAE,EACrC,OAAI,EAAQ,GAAW,GAAG,CAAA,KAAU,EAAU,EAAA,IACvC,GAAG,KAAK,MAAM,EAAQ,EAAE,CAAA,KAAM,EAAQ,EAAA,GAC9C,EAGM,GAAiB,GAAoB,CAC1C,KAAM,CAAA,CAAG,CAAA,KAAQ,EAAA,YAAY,GAAkB,EAAQ,EAAG,CAAC,KAC3D,EAAA,WAAA,IAAgB,CACf,GAAI,CAAC,EAAQ,OACb,MAAM,EAAK,OAAO,YAAY,EAAM,GAAK,EACzC,MAAA,IAAa,OAAO,cAAc,CAAE,CACrC,EAAG,CAAC,CAAM,CAAC,CACZ,EAOa,GAAA,CAAkB,CAC9B,OAAA,EACA,MAAA,EACA,YAAA,EACA,UAAA,EACA,WAAA,EACA,QAAA,EACA,QAAA,EACA,IAAA,EACA,UAAA,EACA,aAAA,EACA,SAAA,CAAA,IAaK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAA6C,EAAO,GAAG,EAC7E,CAAC,EAAK,CAAA,KAAU,EAAA,UAAS,EAAE,EACjC,GAAc,EAAO,MAAQ,CAAC,EAE9B,MAAM,EACL,EAAO,MAAQ,EAAI,EAAO,OAAS,KAAK,IAAI,EAAI,GAAa,EAAO,MAC/D,EAAa,EAAO,QAAU,GAC9B,EAAU,CAAC,GAAc,GAAe,EACxC,EAA6E,CAClF,CACC,KAAM,MACN,MAAO,EAAa,IAAM,EAAU,UAAY,GAAe,CAAW,EAC1E,KAAM,EACH,8CACA,EACC,oEACA,WAAW,IAAI,KAAK,EAAY,EAAO,KAAK,EAAE,eAAe,CAAA,GACjE,MAAO,CACR,EACA,CACC,KAAM,MACN,MAAO,GAAY,EAAO,WAAW,EACrC,KAAM,oDACP,EACA,CACC,KAAM,MACN,MAAO,EAAO,QAAQ,eAAe,GAAK,IAC1C,KACC,IAAgB,MAAQ,EAAO,SAAW,KACvC,GAAG,EAAY,eAAe,CAAA,OAAQ,EAAO,OAAO,eAAe,CAAA,gCACnE,2DACL,EACA,CACC,KAAM,MACN,MAAO,EAAO,SAAS,MAAM,EAAG,CAAC,EACjC,KAAM,sFACP,EACA,CACC,KAAM,KACN,MAAO,IAAI,KAAK,CAAS,EAAE,mBAAmB,EAC9C,KAAM,4CACP,CACD,EAEA,SACC,EAAA,KAAC,SAAD,CAAQ,UAAU,yBACjB,YAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8CAAf,SAAA,IACC,EAAA,MAAC,MAAD,CAAK,UAAU,oDAAf,SAAA,IACC,EAAA,MAAC,MAAD,CAAK,UAAU,6EAAf,SAAA,IACC,EAAA,KAAC,OAAD,CACC,UAAU,2CACV,MAAO,EAEN,SAAA,GAAS,aACL,CAAA,KACN,EAAA,KAAC,GAAD,CACC,KAAM,EAAO,IAAI,MAAQ,EAAO,IAAI,OACpC,MAAM,gBACN,UAAU,gCACV,CAAA,KACD,EAAA,KAAC,GAAD,CACC,QAAQ,UACR,UAAU,wFAET,SAAA,EAAO,IACF,CAAA,CACH,CACJ,CAAA,EAAA,EAAM,IAAA,CAAK,CAAE,KAAA,EAAM,MAAA,EAAO,KAAA,EAAM,MAAA,CAAA,EAAS,OACzC,EAAA,MAAC,EAAD,CAAA,SAAA,IACC,EAAA,KAAC,EAAD,CAAgB,QAAA,GACf,YAAA,EAAA,MAAC,MAAD,CACC,UAAW,EACV,4FACA,IAAU,EAAM,OAAS,GAAK,UAC/B,EAJD,SAAA,IAMC,EAAA,KAAC,OAAD,CAAM,UAAU,wBAAyB,SAAA,CAAW,CAAA,KACpD,EAAA,KAAC,OAAD,CACC,UAAW,EACV,eACA,EAAQ,mBAAqB,iBAC9B,EAEC,SAAA,CACI,CAAA,CACF,GACU,CAAA,KAChB,EAAA,KAAC,EAAD,CAAgB,KAAK,SAAU,SAAA,CAAqB,CAAA,CAC5C,CAAA,EApBK,CAoBL,CACT,CACG,CACL,CAAA,KAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8BAAf,SAAA,IACC,EAAA,MAAC,EAAD,CACC,QAAQ,QACR,KAAK,KACL,UAAU,wEACV,QAAS,EACT,SAAU,EALX,SAAA,IAOC,EAAA,KAAC,GAAD,CAAW,UAAW,EAAG,GAAc,cAAc,CAAI,CAAA,EAAC,UACnD,IACP,MACA,EAAA,MAAC,EAAD,CAAA,SAAA,IACC,EAAA,KAAC,EAAD,CAAgB,QAAA,GACf,YAAA,EAAA,MAAC,EAAD,CACC,QAAQ,QACR,KAAK,KACL,UAAU,wEACV,QAAS,EAJV,SAAA,IAMC,EAAA,KAAC,GAAD,CAAW,CAAA,EAAC,SACL,GACO,CAAA,KAChB,EAAA,MAAC,EAAD,CAAgB,KAAK,SAArB,SAAA,CAA8B,kCACD,EAAA,KAAC,OAAD,CAAM,UAAU,YAAa,SAAA,CAAc,CAAA,CACxD,CACR,CAAA,CAAA,CAAA,CAAA,KAEV,EAAA,MAAC,GAAD,CAAA,SAAA,IACC,EAAA,KAAC,GAAD,CAAgB,QAAA,GACf,YAAA,EAAA,MAAC,EAAD,CACC,QAAQ,QACR,KAAK,KACL,UAAU,wEAHX,SAAA,IAKC,EAAA,KAAC,GAAD,CAAS,CAAA,EAAC,OACH,GACO,CAAA,KAChB,EAAA,KAAC,GAAD,CACC,MAAM,MACN,UAAU,gCAEV,YAAA,EAAA,MAAC,GAAD,CAAY,UAAU,QAAtB,SAAA,IACC,EAAA,MAAC,EAAD,CAAO,UAAU,UAAjB,SAAA,IACC,EAAA,KAAC,EAAD,CAAY,QAAQ,mBAAmB,SAAA,YAAsB,CAAA,KAC7D,EAAA,MAAC,OAAD,CACC,UAAU,OACV,SAAW,GAAU,CACpB,EAAM,eAAe,EACjB,EAAA,IAAW,MAAQ,EAAO,SAAW,EAAO,IAAI,QAAU,IAE9D,EAAI,CAAE,OAAQ,SAAU,OAAQ,CAAO,CAAC,CACzC,EAPD,SAAA,IASC,EAAA,KAAC,EAAD,CACC,GAAG,mBACH,MAAO,EACP,SAAU,EACV,UAAU,0BACV,CAAA,KACD,EAAA,KAAC,EAAD,CACC,KAAK,SACL,KAAK,OACL,UAAU,6DACV,SACC,IAAW,MAAQ,EAAO,SAAW,EAAO,IAAI,QAAU,EAE3D,aAAW,aAEX,YAAA,EAAA,KAAC,GAAD,CAAa,CAAA,CACN,CAAA,CACH,CACA,CAAA,CAAA,CACP,CAAA,KAAA,EAAA,MAAC,EAAD,CAAO,UAAU,UAAjB,SAAA,IACC,EAAA,KAAC,EAAD,CAAY,QAAQ,0BAA0B,SAAA,gBAA0B,CAAA,KACxE,EAAA,MAAC,OAAD,CACC,UAAU,OACV,SAAW,GAAU,CACpB,EAAM,eAAe,EACjB,CAAA,GACJ,EAAI,CAAE,OAAQ,SAAU,MAAO,EAAM,OAAO,CAAG,EAAI,IAAQ,IAAK,CAAC,CAClE,EAND,SAAA,IAQC,EAAA,KAAC,EAAD,CACC,GAAG,0BACH,KAAK,SACL,IAAK,EACL,MAAO,EACP,SAAW,GAAU,EAAO,EAAM,OAAO,KAAK,EAC9C,YACC,EAAO,MAAQ,EACZ,OAAO,KAAK,KAAK,EAAO,MAAQ,GAAK,CAAC,EACtC,aAEJ,UAAU,0BACV,CAAA,KACD,EAAA,KAAC,EAAD,CACC,KAAK,SACL,UAAU,6DACV,SAAU,EACV,SAAA,OAEO,CAAA,CACH,OACN,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,SAAA,CACE,GAAY,IAAK,MACjB,EAAA,KAAC,EAAD,CAEC,KAAK,SACL,QAAQ,UACR,KAAK,KACL,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,SAAU,MAAO,EAAO,KAAM,CAAC,EAE3D,SAAA,EAAO,KACD,EARF,EAAO,KAQL,CACR,KACD,EAAA,KAAC,EAAD,CACC,KAAK,SACL,QAAQ,UACR,KAAK,KACL,SAAU,GAAW,EACrB,QAAA,IAAe,EAAI,CAAE,OAAQ,SAAU,MAAO,IAAK,CAAC,EACpD,SAAA,SAEO,CAAA,CACJ,OACL,EAAA,KAAC,GAAD,CAAA,SAAkB,iCAAiD,CAAA,CAC7D,CACI,CAAA,CAAA,GACG,CAAA,CACR,CAAA,CAAA,KACT,EAAA,MAAC,EAAD,CACC,QAAQ,cACR,KAAK,KACL,UAAU,wEACV,SAAU,EACV,QAAS,EALV,SAAA,IAOC,EAAA,KAAC,EAAD,CAAS,CAAA,EAAC,SACH,GACJ,CACD,CAAA,CAAA,GACE,CAAA,CAEV,EC5RM,EAAa,GAAgB,EAAE,EAK/B,EAAU,GACf,EAAM,MAAQ,UAAU,EAAM,MAAA,GAEzB,EAAA,CAAW,KAAmB,IACnC,EAAS,KAAM,GAAS,OAAO,CAAI,EAAE,YAAY,EAAE,SAAS,CAAM,CAAC,EAG9D,EAAkB,GAAmC,CAC1D,OAAQ,EAAM,KAAd,CACC,IAAK,SACJ,OAAO,KAAK,UAAU,EAAO,EAAM,KAAK,EAAG,KAAM,CAAC,EACnD,IAAK,OACJ,OAAO,KAAK,UACX,OAAO,YACN,EAAM,QAAQ,IAAK,GAAU,CAAC,EAAO,EAAM,KAAK,EAAG,EAAO,EAAM,KAAK,CAAC,CAAC,CACxE,EACA,KACA,CACD,EACD,IAAK,OACJ,OAAO,KAAK,UACX,EAAM,QAAQ,IAAK,GAAU,EAAO,EAAM,KAAK,CAAC,EAChD,KACA,CACD,EACD,IAAK,MACJ,OAAO,KAAK,UAAU,EAAM,QAAQ,IAAI,CAAM,EAAG,KAAM,CAAC,EACzD,IAAK,OACJ,OAAO,KAAK,UACX,EAAM,QAAQ,IAAK,IAAW,CAAE,OAAQ,EAAO,EAAM,MAAM,EAAG,MAAO,EAAM,KAAM,EAAE,EACnF,KACA,CACD,EACD,IAAK,SACJ,OAAO,KAAK,UACX,EAAM,QAAQ,IAAK,IAAW,CAC7B,GAAI,EAAM,GACV,OAAQ,OAAO,YACd,EAAM,OAAO,IAAK,GAAU,CAAC,EAAO,EAAM,KAAK,EAAG,EAAO,EAAM,KAAK,CAAC,CAAC,CACvE,CACD,EAAE,EACF,KACA,CACD,EACD,QACC,MAAO,EACT,CACD,EAMM,EAAA,CAAsB,EAAW,IAA6C,CACnF,KAAM,CAAC,EAAO,CAAA,KAAY,EAAA,UAAS,CAAM,EACnC,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,CAAQ,EAC7C,OAAI,IAAW,IACd,EAAU,CAAQ,EAClB,EAAS,CAAM,GAET,CAAC,EAAO,CAAQ,CACxB,EAEM,EAAA,CAAY,CAAE,SAAA,CAAA,OACnB,EAAA,KAAC,MAAD,CAAK,UAAU,2EACb,SAAA,EAAW,sCAAwC,oBAChD,CAAA,EAQA,EAAA,CAAmB,CACxB,OAAA,EACA,eAAA,EACA,WAAA,EACA,YAAA,EACA,WAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,EACA,SAAA,EACA,OAAA,CAAA,OAaA,EAAA,MAAC,MAAD,CAAK,UAAU,uCAAf,SAAA,IACC,EAAA,MAAC,MAAD,CAAK,UAAU,sEAAf,SAAA,IACC,EAAA,KAAC,GAAD,CAAQ,UAAU,4CAA8C,CAAA,KAChE,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,MAAO,EACP,SAAW,GAAU,EAAe,EAAM,OAAO,KAAK,EACtD,YAAY,wBACZ,aAAW,wBACX,UAAU,yEACV,CAAA,EACA,KACD,EAAA,KAAC,OAAD,CAAM,UAAU,+DACd,SAAA,EACE,GAAG,CAAA,MAAgB,CAAA,GACnB,IAAe,MAAQ,EAAa,EACnC,GAAG,EAAY,eAAe,CAAA,OAAQ,EAAW,eAAe,CAAA,GAChE,EAAY,eAAe,CAC1B,CAAA,KACN,EAAA,KAAC,GAAD,CACC,KAAM,EACN,MAAM,6BACN,CAAA,CACG,IACJ,EACA,EACA,CACG,IAGA,GAAA,CAAW,CAChB,OAAA,EACA,OAAA,EACA,QAAA,EACA,MAAA,EAAQ,EAAA,IASH,CACL,KAAM,CAAC,EAAO,CAAA,KAAY,EAAA,UAAqB,CAAU,EACnD,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAqB,CAAU,EACrD,CAAC,EAAS,CAAA,KAAc,EAAA,UAAS,GAAG,EACpC,EAAU,IAAU,MAAS,CAAC,GAAS,IAAW,KAClD,EAAS,MAAO,GAAqB,CAC1C,EAAM,eAAe,EACjB,EAAA,IAAU,MAAQ,IAAW,OAE5B,MADe,EAAO,EAAO,EAAQ,OAAO,CAAO,EAAI,CAAM,IAElE,EAAS,CAAU,EACnB,EAAU,CAAU,EACpB,EAAW,GAAG,EACf,EACA,SACC,EAAA,MAAC,OAAD,CACC,SAAU,EACV,UAAU,0FAFX,SAAA,IAIC,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,EACV,YAAa,EAAO,CAAA,CACpB,CAAA,EACA,KACA,EAAA,KAAC,EAAD,CACC,KAAK,SACL,KAAK,MACL,MAAO,EACP,SAAW,GAAU,EAAW,EAAM,OAAO,KAAK,EAClD,YAAa,EAAO,CAAA,EACpB,UAAU,WACV,CAAA,KAED,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,EACV,YAAa,EAAO,CAAA,CACpB,CAAA,KAEF,EAAA,MAAC,EAAD,CACC,KAAK,SACL,SAAU,GAAW,EAFtB,SAAA,IAIC,EAAA,KAAC,GAAD,CAAO,CAAA,EAAC,MACD,GACH,GAER,EAEM,GAAA,CAAgB,CACrB,MAAA,EACA,UAAA,EACA,IAAA,EACA,QAAA,EACA,WAAA,EACA,WAAA,CAAA,IAQK,CACL,KAAM,CAAC,EAAO,CAAA,EAAY,EAA2B,EAAO,EAAM,MAAM,EAClE,EAAQ,IAAU,MAAQ,EAAM,SAAW,EAAM,OACvD,SACC,EAAA,MAAC,MAAD,CAAK,UAAU,YAAf,SAAA,CACE,MACA,EAAA,MAAC,MAAD,CAAK,UAAU,iFAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,MAAM,oBACN,QAAQ,qEACR,UAAU,YACV,CAAA,KACD,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,QAAS,EACT,SAAA,WAEO,CAAA,CACJ,OAEN,EAAA,KAAC,EAAD,CACQ,MAAA,EACK,WAAA,CACZ,CAAA,KACD,EAAA,MAAC,OAAD,CACC,SAAW,GAAU,CACpB,EAAM,eAAe,EACjB,IAAU,MAAQ,GAAO,EAAI,CAAE,OAAQ,YAAa,MAAO,CAAM,CAAC,CACvE,EACA,UAAU,2BALX,SAAA,IAOC,EAAA,MAAC,EAAD,CAAO,UAAU,UAAjB,SAAA,IACC,EAAA,KAAC,EAAD,CAAY,QAAQ,qBAAqB,SAAA,eAAyB,CAAA,KAClE,EAAA,KAAC,EAAD,CACC,GAAG,qBACH,MAAO,EACP,SAAU,CACV,CAAA,CACK,CACP,CAAA,KAAA,EAAA,MAAC,EAAD,CACC,KAAK,SACL,UAAU,OACV,SAAU,GAAa,GAAW,CAAC,EAHpC,SAAA,IAKC,EAAA,KAAC,EAAD,CAAO,CAAA,EAAC,aACD,CACH,CAAA,CAAA,GACF,GAEP,EAEM,GAAA,CAAW,CAChB,MAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAMK,CACL,KAAM,CAAC,EAAO,CAAA,EAAY,EAA2B,EAAO,EAAM,MAAM,EAClE,EAAQ,IAAU,MAAQ,EAAM,SAAW,EAAM,OACvD,SACC,EAAA,MAAC,MAAD,CAAK,UAAU,4GAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,MAAO,EACP,QAAA,EACA,CAAA,KACD,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,CACV,CAAA,KACD,EAAA,MAAC,MAAD,CAAK,UAAU,OAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAW,EAAQ,eAAiB,OACpC,SAAU,GAAW,CAAC,EACtB,QAAA,IAAe,IAAU,MAAQ,EAAI,CAAE,OAAQ,aAAc,MAAA,EAAO,MAAO,CAAM,CAAC,EAClF,aAAW,kBAEX,YAAA,EAAA,KAAC,EAAD,CAAO,CAAA,CACA,CAAA,KACR,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAU,+CACV,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,aAAc,MAAA,CAAM,CAAC,EAClD,aAAW,oBAEX,YAAA,EAAA,KAAC,EAAD,CAAS,CAAA,CACF,CAAA,CACJ,GACD,GAEP,EAEM,GAAA,CAAc,CACnB,MAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAMK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAE,EACjC,EAAS,EAAO,KAAK,EAAE,YAAY,EACnC,EAAU,EACb,EAAM,QAAQ,OAAQ,GACtB,EAAQ,EAAQ,EAAO,EAAM,KAAK,EAAG,EAAO,EAAM,KAAK,CAAC,CACzD,EACC,EAAM,QACT,SACC,EAAA,KAAC,EAAD,CACS,OAAA,EACR,eAAgB,EAChB,WAAY,EAAQ,OACpB,YAAa,EAAM,QAAQ,OAC3B,WAAY,EACZ,SAAA,IAAgB,EAAe,CAAK,EACpC,UACC,EAAA,MAAC,MAAD,CAAK,UAAU,oKAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAA,SAAM,OAAW,CAAA,KACjB,EAAA,KAAC,OAAD,CAAA,SAAM,OAAW,CAAA,KACjB,EAAA,KAAC,OAAD,CAAO,CAAA,CACH,IAEN,UACC,EAAA,KAAC,GAAD,CACC,OAAQ,CAAC,QAAS,OAAO,EAChB,QAAA,EACT,OAAA,CAAS,EAAO,IACf,EAAI,CAAE,OAAQ,aAAc,MAAA,EAAO,MAAO,CAAoC,CAAC,CAEhF,CAAA,EAGD,SAAA,EAAQ,SAAW,KACnB,EAAA,KAAC,EAAD,CAAU,SAAU,EAAO,OAAS,CAAI,CAAA,EAExC,EAAQ,IAAK,MACZ,EAAA,KAAC,GAAD,CAEC,MAAO,EAAM,MACb,MAAO,EAAM,MACR,IAAA,EACI,QAAA,CACT,EALK,EAAM,MAAM,MAKjB,CACD,CAEc,CAAA,CAEnB,EAEM,GAAA,CAAW,CAChB,MAAA,EACA,MAAA,EACA,SAAA,EACA,IAAA,EACA,QAAA,CAAA,IAOK,CACL,KAAM,CAAC,EAAO,CAAA,EAAY,EAA2B,EAAO,GAAG,CAAA,IAAY,EAAM,MAAA,EAAQ,EACnF,EAAQ,IAAU,MAAQ,EAAM,SAAW,EAAM,OACvD,SACC,EAAA,MAAC,MAAD,CAAK,UAAU,6FAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAM,UAAU,gCAAiC,SAAA,CAAY,CAAA,KAC7D,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,CACV,CAAA,KACD,EAAA,MAAC,MAAD,CAAK,UAAU,OAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAW,EAAQ,eAAiB,OACpC,SAAU,GAAW,CAAC,EACtB,QAAA,IAAe,IAAU,MAAQ,EAAI,CAAE,OAAQ,UAAW,MAAA,EAAO,MAAO,CAAM,CAAC,EAC/E,aAAY,kBAAkB,CAAA,GAE9B,YAAA,EAAA,KAAC,EAAD,CAAO,CAAA,CACA,CAAA,KACR,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAU,+CACV,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,aAAc,MAAA,CAAM,CAAC,EAClD,aAAY,oBAAoB,CAAA,GAEhC,YAAA,EAAA,KAAC,EAAD,CAAS,CAAA,CACF,CAAA,CACJ,GACD,GAEP,EAEM,GAAA,CAAc,CACnB,MAAA,EACA,SAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAOK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAE,EACjC,CAAC,EAAM,CAAA,KAAW,EAAA,UAAqB,CAAU,EACjD,EAAS,EAAO,KAAK,EAAE,YAAY,EACnC,EAAU,EACb,EAAM,QAAQ,OAAQ,GAAU,EAAQ,EAAQ,EAAM,MAAO,EAAO,EAAM,KAAK,CAAC,CAAC,EACjF,EAAM,QACH,EAAO,MAAO,GAA2B,CAC1C,IAAS,MAET,MADgB,EAAI,CAAE,OAAQ,WAAY,KAAA,EAAM,MAAO,CAAK,CAAC,GACtD,EAAQ,CAAU,CAC9B,EACA,SACC,EAAA,KAAC,EAAD,CACS,OAAA,EACR,eAAgB,EAChB,WAAY,EAAQ,OACpB,YAAa,EAAM,QAAQ,OAC3B,WAAY,EACZ,SAAA,IAAgB,EAAe,CAAK,EACpC,UACC,EAAA,MAAC,MAAD,CAAK,UAAU,qJAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAA,SAAM,OAAW,CAAA,KACjB,EAAA,KAAC,OAAD,CAAA,SAAM,OAAW,CAAA,KACjB,EAAA,KAAC,OAAD,CAAO,CAAA,CACH,IAEN,UACC,EAAA,MAAC,OAAD,CACC,SAAW,GAAU,CACpB,EAAM,eAAe,EACrB,EAAK,OAAO,CACb,EACA,UAAU,2FALX,SAAA,IAOC,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,EACV,YAAY,UACZ,CAAA,KACD,EAAA,KAAC,EAAD,CACC,KAAK,SACL,QAAQ,UACR,SAAU,GAAW,IAAS,KAC9B,QAAA,IAAe,EAAK,MAAM,EAC1B,SAAA,WAEO,CAAA,KACR,EAAA,KAAC,EAAD,CACC,KAAK,SACL,SAAU,GAAW,IAAS,KAC9B,SAAA,YAEO,CAAA,CACH,IAGN,SAAA,EAAQ,SAAW,KACnB,EAAA,KAAC,EAAD,CAAU,SAAU,EAAO,OAAS,CAAI,CAAA,EAExC,EAAQ,IAAK,MACZ,EAAA,KAAC,GAAD,CAEC,MAAO,EAAM,MACb,MAAO,EAAM,MACH,SAAA,EACL,IAAA,EACI,QAAA,CACT,EANK,EAAM,KAMX,CACD,CAEc,CAAA,CAEnB,EAEM,GAAA,CAAa,CAClB,MAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAMK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAE,EACjC,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAqB,CAAU,EACrD,EAAS,EAAO,KAAK,EAAE,YAAY,EACnC,EAAU,EACb,EAAM,QAAQ,OAAQ,GAAU,EAAQ,EAAQ,EAAO,CAAK,CAAC,CAAC,EAC9D,EAAM,QACT,SACC,EAAA,KAAC,EAAD,CACS,OAAA,EACR,eAAgB,EAChB,WAAY,EAAQ,OACpB,YAAa,EAAM,QAAQ,OAC3B,WAAY,EACZ,SAAA,IAAgB,EAAe,CAAK,EACpC,UACC,EAAA,MAAC,MAAD,CAAK,UAAU,gJAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAA,SAAM,QAAY,CAAA,KAClB,EAAA,KAAC,OAAD,CAAO,CAAA,CACH,IAEN,UACC,EAAA,MAAC,OAAD,CACC,SAAU,MAAO,GAAU,CAC1B,EAAM,eAAe,EACjB,IAAW,MAEX,MADgB,EAAI,CAAE,OAAQ,SAAU,OAAA,CAAO,CAAC,GACzC,EAAU,CAAU,CAChC,EACA,UAAU,sFAPX,SAAA,IASC,EAAA,KAAC,EAAD,CACC,MAAO,EACP,SAAU,EACV,YAAY,YACZ,CAAA,KACD,EAAA,MAAC,EAAD,CACC,KAAK,SACL,SAAU,GAAW,IAAW,KAFjC,SAAA,IAIC,EAAA,KAAC,GAAD,CAAO,CAAA,EAAC,aACD,CACH,CAAA,CAAA,IAGN,SAAA,EAAQ,SAAW,KACnB,EAAA,KAAC,EAAD,CAAU,SAAU,EAAO,OAAS,CAAI,CAAA,EAExC,EAAQ,IAAK,MACZ,EAAA,MAAC,MAAD,CAEC,UAAU,mEAFX,SAAA,IAIC,EAAA,KAAC,OAAD,CAAM,UAAU,iBACf,YAAA,EAAA,KAAC,EAAD,CACC,MAAO,EACP,QAAA,EACA,CAAA,CACI,CAAA,KACN,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAU,+CACV,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,YAAa,OAAQ,CAAM,CAAC,EACzD,aAAW,oBAEX,YAAA,EAAA,KAAC,EAAD,CAAS,CAAA,CACF,CAAA,CACJ,CAnBC,EAAA,EAAM,MAmBP,CACL,CAEc,CAAA,CAEnB,EAEM,GAAA,CAAW,CAChB,OAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAMK,CACL,KAAM,CAAC,EAAO,CAAA,EAAY,EAAe,OAAO,CAAK,EAAG,OAAO,CAAK,CAAC,EAC/D,EAAc,OAAO,CAAK,EAC1B,EAAQ,IAAU,OAAO,CAAK,EAEpC,SACC,EAAA,MAAC,MAAD,CAAK,UAAU,6FAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,MAAO,EACP,QAAA,EACA,CAAA,KACD,EAAA,KAAC,EAAD,CACC,UAAU,UACV,MAAO,EACP,SAAW,GAAU,EAAS,EAAM,OAAO,KAAK,EAChD,aAAW,mBACX,UAAU,WACV,CAAA,KACD,EAAA,MAAC,MAAD,CAAK,UAAU,OAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAW,EAAQ,eAAiB,OACpC,SAAU,GAAW,CAAC,GAAS,CAnBrB,OAAO,SAAS,CAmBM,EAChC,QAAA,IAAe,EAAI,CAAE,OAAQ,aAAc,OAAA,EAAQ,MAAO,CAAY,CAAC,EACvE,aAAW,wBAEX,YAAA,EAAA,KAAC,EAAD,CAAO,CAAA,CACA,CAAA,KACR,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAU,+CACV,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,aAAc,OAAA,CAAO,CAAC,EACnD,aAAW,2BAEX,YAAA,EAAA,KAAC,EAAD,CAAS,CAAA,CACF,CAAA,CACJ,GACD,GAEP,EAEM,GAAA,CAAc,CACnB,MAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,CAAA,IAMK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAE,EACjC,EAAS,EAAO,KAAK,EAAE,YAAY,EACnC,EAAU,EACb,EAAM,QAAQ,OAAQ,GAAU,EAAQ,EAAQ,EAAO,EAAM,MAAM,EAAG,EAAM,KAAK,CAAC,EAClF,EAAM,QACT,SACC,EAAA,KAAC,EAAD,CACS,OAAA,EACR,eAAgB,EAChB,WAAY,EAAQ,OACpB,YAAa,EAAM,QAAQ,OAC3B,WAAY,EACZ,SAAA,IAAgB,EAAe,CAAK,EACpC,UACC,EAAA,MAAC,MAAD,CAAK,UAAU,qJAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAA,SAAM,QAAY,CAAA,KAClB,EAAA,KAAC,OAAD,CAAA,SAAM,OAAW,CAAA,KACjB,EAAA,KAAC,OAAD,CAAO,CAAA,CACH,IAEN,UACC,EAAA,KAAC,GAAD,CACC,MAAA,GACA,OAAQ,CAAC,SAAU,OAAO,EACjB,QAAA,EACT,OAAA,CAAS,EAAQ,IAChB,EAAI,CAAE,OAAQ,aAAc,OAAA,EAAe,MAAA,CAAgB,CAAC,CAE7D,CAAA,EAGD,SAAA,EAAQ,SAAW,KACnB,EAAA,KAAC,EAAD,CAAU,SAAU,EAAO,OAAS,CAAI,CAAA,EAExC,EAAQ,IAAK,MACZ,EAAA,KAAC,GAAD,CAEC,OAAQ,EAAM,OACd,MAAO,EAAM,MACR,IAAA,EACI,QAAA,CACT,EALK,EAAM,OAAO,MAKlB,CACD,CAEc,CAAA,CAEnB,EAEM,GAAA,CAAgB,CACrB,MAAA,EACA,MAAA,EACA,IAAA,EACA,QAAA,EACA,UAAA,EACA,kBAAA,CAAA,IAQK,CACL,KAAM,CAAC,EAAQ,CAAA,KAAa,EAAA,UAAS,EAAE,EACjC,EAAS,EAAO,KAAK,EAAE,YAAY,EACnC,EAAU,EACb,EAAM,QAAQ,OAAQ,GACtB,EACC,EACA,EAAM,GACN,GAAG,EAAM,OAAO,QAAS,GAAU,CAAC,EAAO,EAAM,KAAK,EAAG,EAAO,EAAM,KAAK,CAAC,CAAC,CAC9E,CACD,EACC,EAAM,QACT,SACC,EAAA,KAAC,EAAD,CACS,OAAA,EACR,eAAgB,EAChB,WAAY,EAAQ,OACpB,YAAa,EAAM,QAAQ,OAC3B,WAAY,EACZ,SAAA,IAAgB,EAAe,CAAK,EACpC,SACC,EAAA,MAAC,GAAD,CACC,KAAK,SACL,QAAQ,QACR,KAAK,KACL,MAAO,EACP,cAAgB,GAAS,GAAQ,EAAkB,CAAuB,EAC1E,aAAW,eACX,UAAU,wBAPX,SAAA,IASC,EAAA,KAAC,GAAD,CACC,MAAM,UACN,UAAU,oCACV,SAAA,cAEgB,CAAA,KACjB,EAAA,KAAC,GAAD,CACC,MAAM,WACN,UAAU,oCACV,SAAA,cAEgB,CAAA,CACL,IAEd,UACC,EAAA,MAAC,MAAD,CAAK,UAAU,sJAAf,SAAA,IACC,EAAA,KAAC,OAAD,CAAA,SAAM,UAAc,CAAA,KACpB,EAAA,KAAC,OAAD,CAAA,SAAM,QAAY,CAAA,KAClB,EAAA,KAAC,OAAD,CAAO,CAAA,CACH,IAEN,UACC,EAAA,KAAC,GAAD,CACC,OAAQ,CAAC,QAAS,OAAO,EAChB,QAAA,EACT,OAAA,CAAS,EAAO,IACf,EAAI,CACH,OAAQ,eACR,GAAI,IACJ,OAAQ,CAAC,CAAE,MAAA,EAAO,MAAO,CAAoC,CAAC,CAC/D,CAAC,CAEF,CAAA,EAGD,SAAA,EAAQ,SAAW,KACnB,EAAA,KAAC,EAAD,CAAU,SAAU,EAAO,OAAS,CAAI,CAAA,EAExC,EAAQ,IAAK,MACZ,EAAA,MAAC,MAAD,CAEC,UAAU,yDAFX,SAAA,IAIC,EAAA,KAAC,OAAD,CAAM,UAAU,0DACd,SAAA,EAAM,EACF,CAAA,KACN,EAAA,KAAC,MAAD,CAAK,UAAU,yBACb,SAAA,EAAM,OAAO,IAAK,MAClB,EAAA,MAAC,MAAD,CAEC,UAAU,gDAFX,SAAA,IAIC,EAAA,KAAC,EAAD,CACC,MAAO,EAAM,MACb,QAAA,EACA,CAAA,KACD,EAAA,KAAC,EAAD,CACC,MAAO,EAAM,MACb,QAAA,EACA,CAAA,CACG,CAXC,EAAA,EAAM,MAAM,MAWb,CACL,CACG,CAAA,KACL,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,KAAK,UACL,UAAU,4DACV,SAAU,EACV,QAAA,IAAe,EAAI,CAAE,OAAQ,eAAgB,GAAI,EAAM,EAAG,CAAC,EAC3D,aAAY,uBAAuB,EAAM,EAAA,GAEzC,YAAA,EAAA,KAAC,EAAD,CAAS,CAAA,CACF,CAAA,CACJ,CAjCC,EAAA,EAAM,EAiCP,CACL,CAEc,CAAA,CAEnB,EAGa,GAAA,CAAoB,CAChC,OAAA,EACA,IAAA,EACA,QAAA,EACA,WAAA,EACA,WAAA,EACA,gBAAA,EACA,wBAAA,CAAA,IASK,CACL,MAAM,EAAQ,EAAO,MACrB,OAAQ,EAAM,KAAd,CACC,IAAK,SACJ,SACC,EAAA,KAAC,GAAD,CACC,MAAO,EAAM,MACb,UAAW,EAAM,UACZ,IAAA,EACI,QAAA,EACG,WAAA,EACA,WAAA,CACZ,CAAA,EAEH,IAAK,OACJ,SACC,EAAA,KAAC,GAAD,CACQ,MAAA,EACP,MAAO,EAAO,OACT,IAAA,EACI,QAAA,CACT,CAAA,EAEH,IAAK,OACJ,SACC,EAAA,KAAC,GAAD,CACQ,MAAA,EACP,SAAU,EAAO,SACjB,MAAO,EAAO,OACT,IAAA,EACI,QAAA,CACT,CAAA,EAEH,IAAK,MACJ,SACC,EAAA,KAAC,GAAD,CACQ,MAAA,EACP,MAAO,EAAO,OACT,IAAA,EACI,QAAA,CACT,CAAA,EAEH,IAAK,OACJ,SACC,EAAA,KAAC,GAAD,CACQ,MAAA,EACP,MAAO,EAAO,OACT,IAAA,EACI,QAAA,CACT,CAAA,EAEH,IAAK,SACJ,SACC,EAAA,KAAC,GAAD,CACQ,MAAA,EACP,MAAO,EAAO,OACT,IAAA,EACI,QAAA,EACT,UAAW,EACX,kBAAmB,CACnB,CAAA,EAEH,QACC,SACC,EAAA,KAAC,EAAD,CACC,QAAQ,OACR,MAAM,gBACN,QAAQ,2DACR,UAAU,YACV,CAAA,CAEJ,CACD,ECr5BM,GAAiB,GAAsD,CAC5E,GAAI,EAAO,IAAI,OAAS,OAAW,OAAO,KAC1C,MAAM,EAAM,KAAK,UAAU,EAAO,IAAI,IAAI,EAC1C,OAAQ,EAAO,KAAf,CACC,IAAK,SACJ,MAAO,OAAO,CAAA,GACf,IAAK,OACJ,MAAO,WAAW,CAAA,GACnB,IAAK,OACJ,MAAO,UAAU,CAAA,QAClB,IAAK,MACJ,MAAO,YAAY,CAAA,GACpB,IAAK,OACJ,MAAO,UAAU,CAAA,mBAClB,IAAK,SACJ,MAAO,UAAU,CAAA,iBAClB,QACC,OAAO,IACT,CACD,EAGM,GAAe,GAAsD,CAC1E,OAAQ,EAAO,MAAM,KAArB,CACC,IAAK,OACL,IAAK,OACL,IAAK,OACL,IAAK,SACJ,OAAO,EAAO,MAAM,QAAQ,OAC7B,IAAK,MACJ,OAAO,EAAO,MAAM,QAAQ,OAC7B,QACC,OAAO,IACT,CACD,EAEa,GAAA,IAA2B,CACvC,MAAM,EAAW,GAAY,EACvB,CAAE,iBAAA,CAAA,EAAqB,GAAiB,EACxC,CAAE,YAAA,CAAA,EAAgB,GAAgB,EAClC,CAAE,SAAA,EAAU,YAAA,CAAA,EAAgB,GAAgB,EAC5C,CAAC,EAAa,CAAA,EAAkB,GAAc,KAAK,EACnD,CAAC,EAAS,CAAA,KAAc,EAAA,UAAwB,IAAI,EACpD,CAAC,EAAkB,CAAA,KAAuB,EAAA,UAAwB,IAAI,EACtE,EACL,IAAqB,EAAc,WAAa,UAC3C,EAAQ,GAAY,EAAa,IAAY,EAAa,CAAe,EACzE,CAAE,YAAA,EAAa,UAAA,EAAW,UAAA,CAAA,EAAc,GAAqB,EAC7D,EAAQ,EAAM,MAAM,MACpB,EAAS,IAAQ,CAAA,EACjB,CAAC,EAAkB,CAAA,KAAuB,EAAA,UAAS,EAAK,EACxD,CAAC,EAAmB,CAAA,KAAwB,EAAA,UAEhD,IAAI,EACA,EAAQ,GAAQ,IAAI,OAAS,EAAS,UAAU,EAAO,IAAI,MAAA,GAAW,IACtE,EAAU,EAAS,GAAc,CAAM,EAAI,KAE3C,KAAe,EAAA,SAAA,IAAc,CAClC,GAAI,CAAC,GAAU,CAAC,GAAS,EAAM,SAAW,EAAG,OAAO,EACpD,MAAM,EAAO,gBAAgB,CAAM,EACnC,UAAW,KAAQ,EAAM,MAAM,CAAC,EAC3B,EAAK,MAAM,OAAS,QAAU,EAAK,MAAM,OAAS,QACrD,EAAK,MAAM,QAAQ,KAAK,GAAG,EAAK,MAAM,OAAO,EAC1C,EAAK,MAAM,OAAS,QAAU,EAAK,MAAM,OAAS,QACrD,EAAK,MAAM,QAAQ,KAAK,GAAG,EAAK,MAAM,OAAO,EAC1C,EAAK,MAAM,OAAS,OAAS,EAAK,MAAM,OAAS,OACpD,EAAK,MAAM,QAAQ,KAAK,GAAG,EAAK,MAAM,OAAO,EAC1C,EAAK,MAAM,OAAS,QAAU,EAAK,MAAM,OAAS,QACrD,EAAK,MAAM,QAAQ,KAAK,GAAG,EAAK,MAAM,OAAO,EAC1C,EAAK,MAAM,OAAS,UAAY,EAAK,MAAM,OAAS,UACvD,EAAK,MAAM,QAAQ,KAAK,GAAG,EAAK,MAAM,OAAO,EAE/C,OAAO,CACR,EAAG,CAAC,EAAQ,CAAK,CAAC,EACZ,EAAc,EAAe,GAAY,CAAY,EAAI,KAEzD,EAAgB,MAAO,EAAW,EAAQ,KAAU,CACzD,GAAI,CAAC,GAAe,CAAC,EAAQ,MAAO,GACpC,GAAI,CACH,MAAM,EAAS,MAAM,EAAY,EAAa,CAC7C,iBAAkB,EAAO,SACzB,MAAA,EACA,UAAA,CACD,CAAC,EACD,OAAA,EAAqB,IAAI,EACrB,EAAO,QAAS,EAAe,IAAI,GAC7B,EAAO,IAAI,QAAU,OAAS,GACvC,EAAe,EAAO,IAAI,QAAU,GAAG,EACjC,EACR,OAAS,EAAO,CAEf,OAAK,EAAsC,SAAW,KACrD,EAAqB,CAAS,EACxB,EACR,CACD,EAEM,GAAA,IAAqB,CAC1B,GAAI,CAAC,EAAS,OACd,MAAM,EAAU,EAAS,UAAU,EAAM,MAAM,EAAG,EAAE,CAAA,EAAG,EACvD,EAAY,EAAS,CAAE,MAAO,CAAQ,CAAC,EACvC,EAAS,CAAE,GAAI,mBAAoB,OAAQ,CAAE,QAAA,CAAQ,CAAE,CAAC,CACzD,EAEM,GAAyB,SAAY,CAC1C,GAAI,CAAC,GAAe,CAAC,GAAU,EAAO,OAAS,SAAU,OACzD,MAAM,EAAuB,CAAC,EAC9B,IAAI,EAAS,EACT,EAAU,GACd,KAAO,GAAS,CAOf,MAAM,GAAO,MANU,GAAoB,CAC1C,GAAI,GAAoB,GACxB,IAAK,EACL,OAAA,EACA,iBAAkB,EAAO,QAC1B,CAAC,GACqB,KAAK,KAG3B,GAFA,EAAO,KAAK,GAAmB,EAAK,MAAM,MAAM,CAAC,EACjD,EAAU,EAAK,QACX,EAAK,UAAY,EAAK,aAAe,MAAQ,EAAK,YAAc,GACnE,MAAM,IAAI,MAAM,yCAAyC,EAE1D,EAAS,EAAK,YAAc,CAC7B,CACA,MAAM,GAAO,EAAO,OAAA,CAAQ,EAAO,KAAU,EAAQ,GAAM,OAAQ,CAAC,EAC9D,GAAQ,IAAI,WAAW,EAAI,EACjC,IAAI,GAAW,EACf,UAAW,KAAS,EACnB,GAAM,IAAI,EAAO,EAAQ,EACzB,IAAY,EAAM,OAEnB,MAAM,GAAM,IAAI,gBAAgB,IAAI,KAAK,CAAC,GAAM,MAAqB,CAAC,CAAC,EACjE,EAAS,SAAS,cAAc,GAAG,EACzC,EAAO,KAAO,GACd,EAAO,SAAW,GAAG,EAAO,IAAI,MAAM,WAAW,YAAa,GAAG,GAAK,aAAA,OACtE,EAAO,MAAM,EACb,IAAI,gBAAgB,EAAG,CACxB,EAEA,GAAI,CAAC,EACJ,SACC,EAAA,KAAC,OAAD,CAAM,UAAU,kEACf,YAAA,EAAA,MAAC,MAAD,CAAK,UAAU,uBAAf,SAAA,IACC,EAAA,KAAC,GAAD,CAAa,UAAU,2CAA6C,CAAA,KACpE,EAAA,KAAC,KAAD,CAAI,UAAU,sCAAsC,SAAA,iBAAmB,CAAA,KACvE,EAAA,KAAC,IAAD,CAAG,UAAU,+CAA+C,SAAA,qEAEzD,CAAA,KACH,EAAA,MAAC,EAAD,CACC,QAAQ,UACR,UAAU,OACV,QAAA,IAAe,EAAY,0BAA0B,EAHtD,SAAA,IAKC,EAAA,KAAC,GAAD,CAAO,CAAA,EAAC,aACD,GACJ,GACA,CAAA,EAER,GAAI,EAAM,UACT,SACC,EAAA,KAAC,OAAD,CAAM,UAAU,0CACf,YAAA,EAAA,KAAC,GAAD,CAAU,CAAA,CACL,CAAA,EAER,GAAI,EAAM,OAAS,CAAC,GAAU,CAAC,EAC9B,SACC,EAAA,KAAC,OAAD,CAAM,UAAU,4DACf,YAAA,EAAA,MAAC,MAAD,CAAK,UAAU,8BAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,QACR,MAAM,qBACN,QAAS,EAAM,OAAO,SAAW,4BACjC,UAAU,uBACV,CAAA,KACD,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,UAAU,OACV,QAAA,IAAe,EAAe,IAAI,EAClC,SAAA,kBAEO,CAAA,CACJ,GACA,CAAA,EAGR,MAAM,GACL,EAAa,SAAW,MAAQ,IAAgB,KAC7C,KAAK,IAAI,EAAG,EAAa,OAAS,CAAW,EAC7C,KAEJ,SACC,EAAA,MAAC,OAAD,CAAM,UAAU,uDAAhB,SAAA,IACC,EAAA,KAAC,GAAD,CAES,OAAA,EACD,MAAA,EACM,YAAA,EACb,UAAW,EAAM,cACjB,WAAY,EAAM,WAClB,QAAS,EACA,QAAA,EACJ,IAAA,EACL,UAAA,IAAiB,EAAM,QAAQ,EAC/B,aAAc,GACd,SAAA,IAAgB,EAAoB,EAAI,CACxC,EAZK,EAAO,IAAI,MAYhB,KACD,EAAA,KAAC,GAAD,CACC,KAAM,EACN,aAAc,EAEd,YAAA,EAAA,MAAC,GAAD,CAAA,SAAA,IACC,EAAA,MAAC,GAAD,CAAA,SAAA,IACC,EAAA,KAAC,GAAD,CAAA,SAAkB,YAA4B,CAAA,KAC9C,EAAA,MAAC,GAAD,CAAA,SAAA,CAAwB,kCACS,OAChC,EAAA,KAAC,OAAD,CAAM,UAAU,4BAA6B,SAAA,GAAS,aAAoB,CAAA,EAAC,iCAEpD,CAAA,CAAA,CACN,CAAA,CAAA,KACnB,EAAA,MAAC,GAAD,CAAA,SAAA,IACC,EAAA,KAAC,GAAD,CAAA,SAAmB,QAAyB,CAAA,KAC5C,EAAA,KAAC,GAAD,CACC,QAAQ,cACR,QAAS,SAAY,CACpB,EAAoB,EAAK,EACzB,GAAI,CACH,MAAM,EAAU,EAAa,EAAO,QAAQ,EAC5C,EAAe,IAAI,CACpB,MAAQ,CAER,CACD,EACA,SAAA,QAEkB,CAAA,CACD,CAAA,CAAA,CACA,CAAA,CAAA,CACR,CAAA,EACZ,MACA,EAAA,MAAC,MAAD,CAAK,UAAU,gFAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,MAAM,oBACN,QAAQ,kFACR,UAAU,iBACV,CAAA,KACD,EAAA,MAAC,MAAD,CAAK,UAAU,aAAf,SAAA,IACC,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,QAAA,IAAe,CACd,EAAqB,IAAI,EACzB,EAAM,QAAQ,CACf,EACA,SAAA,QAEO,CAAA,KACR,EAAA,KAAC,EAAD,CACC,SAAU,EACV,QAAA,IAAe,EAAI,EAAmB,EAAI,EAC1C,SAAA,WAEO,CAAA,CACJ,CACD,CAAA,CAAA,OAEN,EAAA,KAAC,MAAD,CAAK,UAAU,+BACd,YAAA,EAAA,MAAC,UAAD,CAAS,UAAU,MAAnB,SAAA,IACC,EAAA,KAAC,GAAD,CAEC,OAAQ,EACH,IAAA,EACL,QAAS,EACT,WAAA,IAAkB,EAAW,CAAW,EACxC,WAAA,IAAkB,CACjB,GAAM,QAAQ,GAAuB,EAAG,CACvC,QAAS,oCACT,QAAS,yBACT,MAAQ,GAAiB,EAAM,SAAW,iBAC3C,CAAC,CACF,EACiB,gBAAA,EACjB,wBAA0B,GACzB,EAAoB,IAAc,WAAa,EAAc,IAAI,CAElE,EAhBK,EAAO,IAAI,MAgBhB,EACA,EAAM,gBACN,EAAA,KAAC,EAAD,CACC,QAAQ,UACR,UAAU,cACV,QAAA,IAAe,EAAM,cAAc,EACnC,SAAU,EAAM,mBAEf,SAAA,EAAM,mBACJ,WACA,EAAO,OAAS,UAAY,IAAoB,WAC/C,yBACA,gBAAgB,KAAc,KAAO,KAAK,GAAU,eAAe,CAAA,cAAiB,EAAA,EACjF,CAAA,CAED,GACL,CAAA,CACA,GAER,EC/U8DC,GAArDD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{r as G}from"./rolldown-runtime-CXHxssQy.js";import{ut as g}from"./icons-CtpwLWUI.js";import{kt as U}from"./radix-ui-CgJOuzDv.js";import{n as O}from"./dist-DBHqGKiG.js";import{i as e,n as R,r as L,t as p}from"./monaco-editor-CaQyRcyX.js";import{N as D,b as M}from"./index-XoXxezo4.js";var T=G(g(),1),P=["SELECT","FROM","WHERE","INSERT","UPDATE","DELETE","CREATE","DROP","ALTER","TABLE","INDEX","VIEW","DATABASE","SCHEMA","FUNCTION","PROCEDURE","TRIGGER","AND","OR","NOT","IN","BETWEEN","LIKE","IS","NULL","JOIN","LEFT","RIGHT","INNER","OUTER","CROSS","ON","USING","GROUP","BY","HAVING","ORDER","ASC","DESC","LIMIT","OFFSET","UNION","INTERSECT","EXCEPT","AS","DISTINCT","ALL","EXISTS","CASE","WHEN","THEN","ELSE","END","WITH","RECURSIVE","PRIMARY","KEY","FOREIGN","REFERENCES","UNIQUE","CHECK","DEFAULT","CASCADE","RESTRICT","NO","ACTION","SET","CONSTRAINT","BEGIN","COMMIT","ROLLBACK","SAVEPOINT","TRANSACTION","GRANT","REVOKE","PRIVILEGES","TO","RETURNING"],_=["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%",">>","<<","+=","-=","*=","/=","&=","|=","^=","%="],B=["COUNT","SUM","AVG","MIN","MAX","COALESCE","NULLIF","CAST","CONCAT","SUBSTRING","LOWER","UPPER","TRIM","LENGTH","NOW","CURRENT_DATE","CURRENT_TIME","CURRENT_TIMESTAMP","EXTRACT","DATE_TRUNC","AGE","ARRAY_AGG","JSON_BUILD_OBJECT","JSON_AGG","ROW_NUMBER","RANK","DENSE_RANK","LAG","LEAD","FIRST_VALUE","LAST_VALUE","STRING_AGG","GENERATE_SERIES"],x=["INTEGER","INT","SMALLINT","BIGINT","DECIMAL","NUMERIC","REAL","DOUBLE","PRECISION","SERIAL","BIGSERIAL","VARCHAR","CHAR","TEXT","BOOLEAN","BOOL","DATE","TIME","TIMESTAMP","TIMESTAMPTZ","INTERVAL","UUID","JSON","JSONB","ARRAY","BYTEA","INET","CIDR"],F=["SELECT","FROM","WHERE","INSERT INTO","UPDATE","DELETE FROM","CREATE TABLE","DROP TABLE","ALTER TABLE","JOIN","LEFT JOIN","RIGHT JOIN","INNER JOIN","GROUP BY","ORDER BY","HAVING","LIMIT"],k=["COUNT(*)","SUM()","AVG()","MIN()","MAX()","COALESCE()","CONCAT()","NOW()","CURRENT_DATE","CURRENT_TIMESTAMP","STRING_AGG()","ARRAY_AGG()","JSON_AGG()"],V=["INTEGER","VARCHAR","TEXT","BOOLEAN","TIMESTAMP","DATE","JSON","JSONB","UUID","SERIAL","BIGSERIAL"],H=U(),h=({initialQuery:c,queryId:I,savedQuery:m,language:N,onQueryChange:d,onUnsavedChanges:s,onExecuteQuery:C,onSaveQuery:f})=>{const u=(0,T.useRef)(null),a=(0,T.useRef)(null),{isDark:A}=M(),{editor:i}=D();return(0,T.useEffect)(()=>{a.current&&L.setTheme(A?"vs-dark":"vs")},[A]),(0,T.useEffect)(()=>{a.current?.updateOptions({fontSize:i.fontSize,wordWrap:i.wordWrap?"on":"off"}),a.current?.getModel()?.updateOptions({tabSize:i.tabSize})},[i.fontSize,i.wordWrap,i.tabSize]),(0,T.useEffect)(()=>{if(!u.current)return;const S=[];N==="pgsql"&&(e.register({id:"pgsql"}),S.push(e.registerDocumentFormattingEditProvider("pgsql",{provideDocumentFormattingEdits:t=>{const E=t.getValue().replace(/\s+/g," ").replace(/\s*,\s*/g,`,
|
|
2
|
+
`).replace(/\b(SELECT|FROM|WHERE|JOIN|LEFT JOIN|RIGHT JOIN|INNER JOIN|GROUP BY|ORDER BY|HAVING|LIMIT|OFFSET)\b/gi,`
|
|
3
|
+
$1`).replace(/\b(AND|OR)\b/gi,`
|
|
4
|
+
$1`).trim().split(`
|
|
5
|
+
`).map(l=>{const r=l.trim();return r.startsWith("AND")||r.startsWith("OR")||/^[^A-Z]/.test(r)?` ${r}`:r}).join(`
|
|
6
|
+
`);return[{range:t.getFullModelRange(),text:E}]}})),e.setMonarchTokensProvider("pgsql",{defaultToken:"",tokenPostfix:".sql",ignoreCase:!0,keywords:P,operators:_,builtinFunctions:B,builtinTypes:x,tokenizer:{root:[{include:"@comments"},{include:"@whitespace"},{include:"@numbers"},{include:"@strings"},{include:"@complexIdentifiers"},[/[;,.]/,"delimiter"],[/[()]/,"@brackets"],[/[\w@#$]+/,{cases:{"@keywords":"keyword","@operators":"operator","@builtinFunctions":"predefined","@builtinTypes":"type","@default":"identifier"}}],[/[<>=!%&+\-*/|~^]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[[/--+.*/,"comment"],[/\/\*/,{token:"comment.quote",next:"@comment"}]],comment:[[/[^*/]+/,"comment"],[/\*\//,{token:"comment.quote",next:"@pop"}],[/./,"comment"]],numbers:[[/0[xX][0-9a-fA-F]*/,"number"],[/[$][+-]*\d*(\.\d*)?/,"number"],[/((\d+(\.\d*)?)|(\.\d+))([eE][-+]?\d+)?/,"number"]],strings:[[/'/,{token:"string",next:"@string"}],[/"/,{token:"string.double",next:"@stringDouble"}]],string:[[/[^']+/,"string"],[/''/,"string"],[/'/,{token:"string",next:"@pop"}]],stringDouble:[[/[^"]+/,"string.double"],[/""/,"string.double"],[/"/,{token:"string.double",next:"@pop"}]],complexIdentifiers:[[/"/,{token:"identifier.quote",next:"@quotedIdentifier"}]],quotedIdentifier:[[/[^"]+/,"identifier"],[/""/,"identifier"],[/"/,{token:"identifier.quote",next:"@pop"}]]}}),S.push(e.registerCompletionItemProvider("pgsql",{provideCompletionItems:(t,E)=>{const l=t.getWordUntilPosition(E),r={startLineNumber:E.lineNumber,endLineNumber:E.lineNumber,startColumn:l.startColumn,endColumn:l.endColumn};return{suggestions:[...F.map(o=>({label:o,kind:e.CompletionItemKind.Keyword,insertText:o,range:r})),...k.map(o=>({label:o,kind:e.CompletionItemKind.Function,insertText:o.includes("(*)")?o:o.replace("()","($0)"),insertTextRules:e.CompletionItemInsertTextRule.InsertAsSnippet,range:r})),...V.map(o=>({label:o,kind:e.CompletionItemKind.TypeParameter,insertText:o,range:r})),{label:"select-template",kind:e.CompletionItemKind.Snippet,insertText:["SELECT ${1:columns}","FROM ${2:table}","WHERE ${3:condition};"].join(`
|
|
7
|
+
`),insertTextRules:e.CompletionItemInsertTextRule.InsertAsSnippet,documentation:"SELECT statement template",range:r},{label:"create-table-template",kind:e.CompletionItemKind.Snippet,insertText:["CREATE TABLE ${1:table_name} ("," ${2:id} SERIAL PRIMARY KEY,"," ${3:column_name} ${4:VARCHAR(255)} ${5:NOT NULL},"," created_at TIMESTAMP DEFAULT NOW()",");"].join(`
|
|
8
|
+
`),insertTextRules:e.CompletionItemInsertTextRule.InsertAsSnippet,documentation:"CREATE TABLE template",range:r},{label:"insert-template",kind:e.CompletionItemKind.Snippet,insertText:["INSERT INTO ${1:table} (${2:columns})","VALUES (${3:values})","RETURNING *;"].join(`
|
|
9
|
+
`),insertTextRules:e.CompletionItemInsertTextRule.InsertAsSnippet,documentation:"INSERT statement template",range:r}]}}})));const n=L.create(u.current,{value:c,language:N,theme:A?"vs-dark":"vs",fontSize:i.fontSize,minimap:{enabled:!1},lineNumbers:"on",roundedSelection:!0,scrollBeyondLastLine:!0,scrollbar:{horizontal:"hidden",vertical:"hidden",useShadows:!1,verticalHasArrows:!1,horizontalHasArrows:!1,arrowSize:0},automaticLayout:!0,tabSize:i.tabSize,insertSpaces:!0,wordWrap:i.wordWrap?"on":"off",folding:!0,bracketPairColorization:{enabled:!0},suggest:{showKeywords:!0,showSnippets:!0},quickSuggestions:{other:!0,comments:!1,strings:!1}});a.current=n,n.addCommand(R.CtrlCmd|p.Enter,()=>{const t=n.getValue();if(!t.trim()){O.error("Query is empty!");return}C(t)}),n.addCommand(R.CtrlCmd|R.Shift|p.KeyF,()=>{n.trigger("keyboard","editor.action.formatDocument",{}),O.success("Query formatted")}),n.addCommand(R.CtrlCmd|p.KeyS,()=>{f()}),d(n.getValue()),s(I?n.getValue()!==m:!1);const b=n.onDidChangeModelContent(()=>{const t=n.getValue();if(d(t),!I){s(!1);return}s(t!==m)});return()=>{for(const t of S)t.dispose();b.dispose(),n.dispose()}},[c,I,m,N,d,s,C]),(0,H.jsx)("div",{ref:u,className:"flex-1 min-h-0 overflow-y-scroll"})};export{h as CodeEditor};
|