@hienlh/ppm 0.13.44 → 0.13.45
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/CHANGELOG.md +5 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/ai-settings-section-Btix996C.js +2 -1
- package/dist/web/assets/ai-settings-section-Btix996C.js.map +1 -0
- package/dist/web/assets/api-client-DIhJ5qVW.js +2 -1
- package/dist/web/assets/api-client-DIhJ5qVW.js.map +1 -0
- package/dist/web/assets/api-settings-DnHv6JgF.js +2 -1
- package/dist/web/assets/api-settings-DnHv6JgF.js.map +1 -0
- package/dist/web/assets/arrow-up-Rcw6_KKu.js +2 -1
- package/dist/web/assets/arrow-up-Rcw6_KKu.js.map +1 -0
- package/dist/web/assets/audio-preview-B6a1Djtr.js +2 -1
- package/dist/web/assets/audio-preview-B6a1Djtr.js.map +1 -0
- package/dist/web/assets/chat-tab-Z5VNzYCz.js +2 -1
- package/dist/web/assets/chat-tab-Z5VNzYCz.js.map +1 -0
- package/dist/web/assets/chevron-right-DnHIvvcy.js +2 -1
- package/dist/web/assets/chevron-right-DnHIvvcy.js.map +1 -0
- package/dist/web/assets/code-DGBecc50.js +2 -1
- package/dist/web/assets/code-DGBecc50.js.map +1 -0
- package/dist/web/assets/code-editor-Clvs0e0Q.js +2 -1
- package/dist/web/assets/code-editor-Clvs0e0Q.js.map +1 -0
- package/dist/web/assets/conflict-editor-B6nO1gln.js +2 -1
- package/dist/web/assets/conflict-editor-B6nO1gln.js.map +1 -0
- package/dist/web/assets/createLucideIcon-BjHrJDVb.js +2 -1
- package/dist/web/assets/createLucideIcon-BjHrJDVb.js.map +1 -0
- package/dist/web/assets/csv-parser-Dly5nqE1.js +2 -1
- package/dist/web/assets/csv-parser-Dly5nqE1.js.map +1 -0
- package/dist/web/assets/csv-preview-CsqFmPzb.js +2 -1
- package/dist/web/assets/csv-preview-CsqFmPzb.js.map +1 -0
- package/dist/web/assets/data-grid-overlay-editor-aTzJjALy.js +2 -1
- package/dist/web/assets/data-grid-overlay-editor-aTzJjALy.js.map +1 -0
- package/dist/web/assets/data-grid-types-BISkUXAY.js +2 -1
- package/dist/web/assets/data-grid-types-BISkUXAY.js.map +1 -0
- package/dist/web/assets/database-DOWH9-Vv.js +2 -1
- package/dist/web/assets/database-DOWH9-Vv.js.map +1 -0
- package/dist/web/assets/database-viewer-Boup8MJ_.js +2 -1
- package/dist/web/assets/database-viewer-Boup8MJ_.js.map +1 -0
- package/dist/web/assets/diff-viewer-Je2KYGME.js +2 -1
- package/dist/web/assets/diff-viewer-Je2KYGME.js.map +1 -0
- package/dist/web/assets/dist-B1I_4Jtc.js +2 -1
- package/dist/web/assets/dist-B1I_4Jtc.js.map +1 -0
- package/dist/web/assets/dist-CcDNqGjt.js +2 -1
- package/dist/web/assets/dist-CcDNqGjt.js.map +1 -0
- package/dist/web/assets/dist-wf2npcsG.js +2 -1
- package/dist/web/assets/dist-wf2npcsG.js.map +1 -0
- package/dist/web/assets/esm-UZtw2QcY.js +2 -1
- package/dist/web/assets/esm-UZtw2QcY.js.map +1 -0
- package/dist/web/assets/extension-webview-T9TN70nb.js +2 -1
- package/dist/web/assets/extension-webview-T9TN70nb.js.map +1 -0
- package/dist/web/assets/file-exclamation-point-BwzaQ50n.js +2 -1
- package/dist/web/assets/file-exclamation-point-BwzaQ50n.js.map +1 -0
- package/dist/web/assets/file-store-DOxcU_7s.js +2 -1
- package/dist/web/assets/file-store-DOxcU_7s.js.map +1 -0
- package/dist/web/assets/glide-data-grid-CqT8WzTs.js +2 -1
- package/dist/web/assets/glide-data-grid-CqT8WzTs.js.map +1 -0
- package/dist/web/assets/image-preview-DeNUcGI9.js +2 -1
- package/dist/web/assets/image-preview-DeNUcGI9.js.map +1 -0
- package/dist/web/assets/index-DJtqbPFT.js +2 -1
- package/dist/web/assets/index-DJtqbPFT.js.map +1 -0
- package/dist/web/assets/input-_LFQwhzd.js +2 -1
- package/dist/web/assets/input-_LFQwhzd.js.map +1 -0
- package/dist/web/assets/katex-Bqvo_ZG0.js +2 -1
- package/dist/web/assets/katex-Bqvo_ZG0.js.map +1 -0
- package/dist/web/assets/lib-Bu71-TFS.js +2 -1
- package/dist/web/assets/lib-Bu71-TFS.js.map +1 -0
- package/dist/web/assets/markdown-renderer-CXPtICSx.js +2 -1
- package/dist/web/assets/markdown-renderer-CXPtICSx.js.map +1 -0
- package/dist/web/assets/number-overlay-editor-CewUR5pB.js +2 -1
- package/dist/web/assets/number-overlay-editor-CewUR5pB.js.map +1 -0
- package/dist/web/assets/pdf-preview-DI2JU2Lm.js +2 -1
- package/dist/web/assets/pdf-preview-DI2JU2Lm.js.map +1 -0
- package/dist/web/assets/port-forwarding-tab-Cuv_37LW.js +2 -1
- package/dist/web/assets/port-forwarding-tab-Cuv_37LW.js.map +1 -0
- package/dist/web/assets/postgres-viewer-D76ygOZo.js +2 -1
- package/dist/web/assets/postgres-viewer-D76ygOZo.js.map +1 -0
- package/dist/web/assets/react-DMIOAtcX.js +2 -1
- package/dist/web/assets/react-DMIOAtcX.js.map +1 -0
- package/dist/web/assets/refresh-cw-BjrAbUJe.js +2 -1
- package/dist/web/assets/refresh-cw-BjrAbUJe.js.map +1 -0
- package/dist/web/assets/scroll-area-BDi_FNzr.js +2 -1
- package/dist/web/assets/scroll-area-BDi_FNzr.js.map +1 -0
- package/dist/web/assets/search-tM8K5zWU.js +2 -1
- package/dist/web/assets/search-tM8K5zWU.js.map +1 -0
- package/dist/web/assets/settings-store-CVrIYYCB.js +2 -1
- package/dist/web/assets/settings-store-CVrIYYCB.js.map +1 -0
- package/dist/web/assets/sparkles-CulWHe4c.js +2 -1
- package/dist/web/assets/sparkles-CulWHe4c.js.map +1 -0
- package/dist/web/assets/sql-query-editor-lSlKMPlG.js +2 -1
- package/dist/web/assets/sql-query-editor-lSlKMPlG.js.map +1 -0
- package/dist/web/assets/sqlite-viewer-DwTatNwI.js +2 -1
- package/dist/web/assets/sqlite-viewer-DwTatNwI.js.map +1 -0
- package/dist/web/assets/tab-store-S9w6U5gm.js +2 -1
- package/dist/web/assets/tab-store-S9w6U5gm.js.map +1 -0
- package/dist/web/assets/table-BzjWcs87.js +2 -1
- package/dist/web/assets/table-BzjWcs87.js.map +1 -0
- package/dist/web/assets/terminal-tab-XKe1TZiV.js +2 -1
- package/dist/web/assets/terminal-tab-XKe1TZiV.js.map +1 -0
- package/dist/web/assets/text-wrap-DJz9Bgpa.js +2 -1
- package/dist/web/assets/text-wrap-DJz9Bgpa.js.map +1 -0
- package/dist/web/assets/use-blob-url-QX-XajU8.js +2 -1
- package/dist/web/assets/use-blob-url-QX-XajU8.js.map +1 -0
- package/dist/web/assets/use-monaco-theme-BePWbY58.js +2 -1
- package/dist/web/assets/use-monaco-theme-BePWbY58.js.map +1 -0
- package/dist/web/assets/utils-CQux7CsO.js +2 -1
- package/dist/web/assets/utils-CQux7CsO.js.map +1 -0
- package/dist/web/assets/vendor-markdown-0Mxgxy0L.js +2 -1
- package/dist/web/assets/vendor-markdown-0Mxgxy0L.js.map +1 -0
- package/dist/web/assets/vendor-mermaid-Cl50p6TB.js +2 -1
- package/dist/web/assets/vendor-mermaid-Cl50p6TB.js.map +1 -0
- package/dist/web/assets/vendor-ui-UXCWAcmi.js +2 -1
- package/dist/web/assets/vendor-ui-UXCWAcmi.js.map +1 -0
- package/dist/web/assets/vendor-xterm-K3_Xwigj.js +2 -1
- package/dist/web/assets/vendor-xterm-K3_Xwigj.js.map +1 -0
- package/dist/web/assets/video-preview-wHVbAYar.js +2 -1
- package/dist/web/assets/video-preview-wHVbAYar.js.map +1 -0
- package/dist/web/assets/x-BPReZWnP.js +2 -1
- package/dist/web/assets/x-BPReZWnP.js.map +1 -0
- package/dist/web/sw.js +2 -1
- package/dist/web/sw.js.map +1 -0
- package/package.json +1 -1
- package/vite.config.ts +1 -0
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t};
|
|
1
|
+
import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]);export{t};
|
|
2
|
+
//# sourceMappingURL=chevron-right-DnHIvvcy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chevron-right-DnHIvvcy.js","names":[],"sources":["../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/icons/chevron-right.js"],"sourcesContent":["/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [[\"path\", { d: \"m9 18 6-6-6-6\", key: \"mthhwq\" }]];\nconst ChevronRight = createLucideIcon(\"chevron-right\", __iconNode);\n\nexport { __iconNode, ChevronRight as default };\n//# sourceMappingURL=chevron-right.js.map\n"],"x_google_ignoreList":[0],"mappings":"mDAUA,IAAM,EAAe,EAAiB,gBADnB,CAAC,CAAC,OAAQ,CAAE,EAAG,gBAAiB,IAAK,SAAU,CAAC,CAAC,CACF"}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`code`,[[`path`,{d:`m16 18 6-6-6-6`,key:`eg8j8`}],[`path`,{d:`m8 6-6 6 6 6`,key:`ppft3o`}]]);export{t};
|
|
1
|
+
import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`code`,[[`path`,{d:`m16 18 6-6-6-6`,key:`eg8j8`}],[`path`,{d:`m8 6-6 6 6 6`,key:`ppft3o`}]]);export{t};
|
|
2
|
+
//# sourceMappingURL=code-DGBecc50.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-DGBecc50.js","names":[],"sources":["../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/icons/code.js"],"sourcesContent":["/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"m16 18 6-6-6-6\", key: \"eg8j8\" }],\n [\"path\", { d: \"m8 6-6 6 6 6\", key: \"ppft3o\" }]\n];\nconst Code = createLucideIcon(\"code\", __iconNode);\n\nexport { __iconNode, Code as default };\n//# sourceMappingURL=code.js.map\n"],"x_google_ignoreList":[0],"mappings":"mDAaA,IAAM,EAAO,EAAiB,OAJX,CACjB,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAS,CAAC,CAC/C,CAAC,OAAQ,CAAE,EAAG,eAAgB,IAAK,SAAU,CAAC,CAC/C,CACgD"}
|
|
@@ -5,4 +5,5 @@ $2$3`).split(`
|
|
|
5
5
|
`)),K(!0)}}},[a,o,G]),ke=(0,R.useCallback)(e=>{Ce(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),xe(e).catch(()=>{})},[n,xe]),q=(0,R.useMemo)(()=>{if(!I||!V)return;let e=(be.get(V)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>S.get(`/api/db/connections/${V}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[I,V,be]);(0,R.useEffect)(()=>{if(!(!H.current||!q))return U.current?.dispose(),v(),U.current=H.current.languages.registerCompletionItemProvider(`sql`,ee(H.current,q)),()=>{U.current?.dispose()}},[q]);let J=ie(e=>e.openTab),[Ue,We]=(0,R.useState)(null),[Ge,Ke]=(0,R.useState)(null),[qe,Je]=(0,R.useState)(!1),[Ye,Xe]=(0,R.useState)(``),X=(0,R.useCallback)(async e=>{if(W){Je(!0),Ke(null),Xe(e);try{We(await S.post(`/api/db/connections/${W.id}/query`,{sql:e}))}catch(e){Ke(e.message),We(null)}finally{Je(!1)}}},[W]),Ze=(0,R.useCallback)(()=>{!W||!Ye||J({type:`database`,title:`${W.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:W.id,connectionName:W.name,dbType:W.type,initialSql:Ye}})},[W,J,Ye]),Qe=(0,R.useCallback)(()=>{if(!x.current||!W)return;let e=x.current,t=e.getSelection();X(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[W,X]),$e=typeof window<`u`&&`ontouchstart`in window,et=(0,R.useRef)(null),[tt,nt]=(0,R.useState)(null);(0,R.useEffect)(()=>{if(!$e)return;let e=window.visualViewport;if(!e)return;let t=()=>{let t=et.current;if(!t)return;let n=t.getBoundingClientRect().top;nt(e.height-Math.max(0,n))};return e.addEventListener(`resize`,t),e.addEventListener(`scroll`,t),()=>{e.removeEventListener(`resize`,t),e.removeEventListener(`scroll`,t)}},[$e]);let Z=(0,R.useRef)([]),rt=(0,R.useRef)(X);rt.current=X,(0,R.useEffect)(()=>()=>{Z.current.forEach(e=>e.dispose()),Z.current=[]},[]),(0,R.useEffect)(()=>{fe&&t&&T(t,{type:`sqlite`})},[fe,t,T]);let Q=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,R.useEffect)(()=>{if(a!=null){f(!1);return}if(O){c(k??``),b.current=k??``,f(!1),k&&g(!0);return}if(!n||!Q&&!r)return;if(P||F||le||ue){f(!1);return}f(!0),m(null);let e=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${te(r)}/files/read?path=${encodeURIComponent(n)}`;return S.get(e).then(e=>{c(e.content),e.encoding&&u(e.encoding),b.current=e.content,f(!1)}).catch(e=>{m(e instanceof Error?e.message:`Failed to load file`),f(!1)}),()=>{y.current&&clearTimeout(y.current)}},[n,r,P,F,Q,O]);let it=(0,R.useRef)(h);it.current=h,(0,R.useEffect)(()=>{if(!n||!r||a!=null||O)return;let e=e=>{let t=e.detail;if(t.projectName!==r||t.path!==n||it.current)return;let i=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${te(r)}/files/read?path=${encodeURIComponent(n)}`;S.get(i).then(e=>{e.content!==b.current&&(c(e.content),b.current=e.content,e.encoding&&u(e.encoding))}).catch(()=>{})};return window.addEventListener(`file:changed`,e),()=>window.removeEventListener(`file:changed`,e)},[n,r,Q,a,O]),(0,R.useEffect)(()=>{if(!M||a!=null)return;let t=O?`Untitled-${e?.untitledNumber??1}`:n?w(n):`Untitled`,r=h?`${t} \u25CF`:t;M.title!==r&&T(M.id,{title:r})},[h]);let at=(0,R.useCallback)(async e=>{if(n&&!(!Q&&!r))try{Q?await S.put(`/api/fs/write`,{path:n,content:e}):await S.put(`${te(r)}/files/write`,{path:n,content:e}),g(!1)}catch{}},[n,r,Q]);function ot(n){let r=n??``;c(r),b.current=r,g(!0),y.current&&clearTimeout(y.current),O?y.current=setTimeout(()=>{t&&T(t,{metadata:{...e,unsavedContent:b.current}})},2e3):y.current=setTimeout(()=>at(b.current),1e3)}let st=(0,R.useCallback)(async(e,n)=>{try{if(y.current&&clearTimeout(y.current),await S.put(`/api/fs/write`,{path:e,content:n}),t){let{closeTab:n,openTab:r}=re.getState();n(t),r({type:`editor`,title:w(e),projectId:null,metadata:{filePath:e},closable:!0})}g(!1),j(!1)}catch{}},[t]),$=e?.lineNumber,ct=(0,R.useCallback)((e,t)=>{if(x.current=e,H.current=t,$&&$>0&&setTimeout(()=>{e.revealLineInCenter($),e.setPosition({lineNumber:$,column:1}),e.focus()},100),O&&e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>j(!0)),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>ne.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),q&&(U.current?.dispose(),U.current=t.languages.registerCompletionItemProvider(`sql`,ee(t,q))),I){Z.current.forEach(e=>e.dispose()),Z.current=[];let n=e.getModel(),r=e.addCommand(0,(e,t)=>{t&&rt.current(t)});if(r&&n){let e=t.languages.registerCodeLensProvider(`sql`,{provideCodeLenses:e=>{if(e!==n)return{lenses:[],dispose:()=>{}};let t=[],i=e.getValue().split(`
|
|
6
6
|
`),a=-1,o=[],s=!1,c=(e,n)=>{let i=n.trim();!i||i.startsWith(`--`)||t.push({range:{startLineNumber:e,startColumn:1,endLineNumber:e,endColumn:1},command:{id:r,title:`▷ Run`,arguments:[i]}})};for(let e=0;e<i.length;e++){let t=i[e].trim();if(a===-1){if(!t||t.startsWith(`--`))continue;a=e+1,o=[]}o.push(i[e]),(t.match(/\$\$/g)||[]).length%2==1&&(s=!s),!s&&t.endsWith(`;`)&&(c(a,o.join(`
|
|
7
7
|
`)),a=-1,o=[])}return a>0&&o.join(``).trim()&&c(a,o.join(`
|
|
8
|
-
`)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[q]);if(!a&&!O&&(!n||!Q&&!r))return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(d)return(0,z.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,z.jsx)(D,{className:`size-5 animate-spin`}),(0,z.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(p)return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:p});if(P)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(je,{filePath:n,projectName:r})});if(F)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Me,{filePath:n,projectName:r})});if(le)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Ne,{filePath:n,projectName:r})});if(ue)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Pe,{filePath:n,projectName:r})});if(l===`base64`)return(0,z.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,z.jsx)(_,{className:`size-10 text-text-subtle`}),(0,z.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,z.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let lt=I?(0,z.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,z.jsx)(i,{className:`size-3 text-muted-foreground`}),(0,z.jsxs)(`select`,{value:V??``,onChange:e=>{let t=Number(e.target.value);t&&ke(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,z.jsx)(`option`,{value:``,children:`Connection…`}),B.map(e=>(0,z.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,z.jsx)(`button`,{type:`button`,onClick:Qe,disabled:!W,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run SQL`,children:(0,z.jsx)(se,{className:`size-3.5`})})]}):null;return(0,z.jsxs)(`div`,{ref:et,className:`flex flex-col h-full w-full overflow-hidden`,style:tt?{height:`${tt}px`,maxHeight:`${tt}px`}:void 0,children:[a!=null&&Ee&&(0,z.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,z.jsx)(`button`,{type:`button`,onClick:De,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:G?`Raw`:`Beautify`})}),n&&r&&t&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,z.jsx)(Se,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),lt,(0,z.jsx)(we,{ext:N,mdMode:he,onMdModeChange:ge,csvMode:L,onCsvModeChange:ye,wordWrap:E,onToggleWordWrap:ae,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),I&&(!r||!t)&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,z.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?w(n):`SQL`}),lt]}),me&&L===`table`?(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,z.jsx)(Ae,{content:s??``,onContentChange:ot,wordWrap:E})}):pe&&he===`preview`?(0,z.jsx)(He,{content:s??``}):(0,z.jsx)(`div`,{className:`flex-1 overflow-hidden min-h-0`,children:(0,z.jsx)(_e,{height:`100%`,language:o??Be(n??``),value:s??``,onChange:a==null?ot:void 0,onMount:ct,theme:oe,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:E?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:a!=null},loading:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})})}),I&&(Ue||Ge||qe)&&(0,z.jsx)(Ve,{result:Ue,error:Ge,loading:qe,connName:W?.name,onClose:()=>{We(null),Ke(null),Je(!1)},onOpenInTab:Ze}),$e&&(0,z.jsx)(Oe,{editorRef:x,readOnly:a!=null}),A&&(0,z.jsx)(Te,{open:A,defaultName:`Untitled-${e?.untitledNumber??1}`,content:b.current,onSave:st,onCancel:()=>j(!1)})]})}),J=()=>{};function Ve({result:e,error:t,loading:n,connName:r,onClose:a,onOpenInTab:o}){let s=(0,R.useMemo)(()=>e?.changeType===`select`&&e.rows.length>0?{columns:e.columns,rows:e.rows,total:e.rows.length,limit:e.rows.length}:null,[e]),c=(0,R.useMemo)(()=>(e?.columns??[]).map(e=>({name:e,type:`text`,nullable:!0,pk:!1,defaultValue:null})),[e?.columns]),[l,u]=(0,R.useState)(250),d=(0,R.useCallback)(e=>{e.preventDefault();let t=e.clientY,n=l,r=e=>u(Math.max(80,n+(t-e.clientY))),i=()=>{document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)},[l]);return(0,z.jsxs)(`div`,{className:`shrink-0 border-t border-border flex flex-col`,style:{height:l},children:[(0,z.jsx)(`div`,{onMouseDown:d,className:`shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors`,children:(0,z.jsx)(k,{className:`size-3 text-muted-foreground/50`})}),(0,z.jsxs)(`div`,{className:`flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border shrink-0`,children:[(0,z.jsx)(i,{className:`size-3 text-muted-foreground`}),(0,z.jsxs)(`span`,{className:`text-xs font-medium text-foreground truncate flex-1`,children:[r?`${r} · Results`:`Query Results`,e?.executionTimeMs!=null&&(0,z.jsxs)(`span`,{className:`text-muted-foreground ml-1.5 font-normal`,children:[e.executionTimeMs,`ms`]})]}),(0,z.jsxs)(`button`,{type:`button`,onClick:o,title:`Open in DB Viewer tab`,className:`flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors`,children:[(0,z.jsx)(ae,{className:`size-3`}),(0,z.jsx)(`span`,{className:`hidden sm:inline`,children:`Open in Tab`})]}),(0,z.jsx)(`button`,{type:`button`,onClick:a,title:`Close results`,className:`p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors`,children:(0,z.jsx)(g,{className:`size-3`})})]}),(0,z.jsxs)(`div`,{className:`flex-1 overflow-hidden min-h-0`,children:[n&&(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-4 animate-spin text-muted-foreground`})}),t&&(0,z.jsx)(`div`,{className:`px-3 py-2 text-xs text-destructive bg-destructive/5`,children:t}),e?.changeType===`modify`&&(0,z.jsxs)(`div`,{className:`px-3 py-2 text-xs text-green-500`,children:[e.rowsAffected,` row(s) affected`]}),s&&(0,z.jsx)(y,{columns:s.columns,rows:s.rows,total:s.total,limit:s.limit,schema:c,loading:!1,page:1,onPageChange:J,onCellUpdate:J,orderBy:null,orderDir:`ASC`,onToggleSort:J,connectionName:r}),e?.changeType===`select`&&e.rows.length===0&&(0,z.jsx)(`div`,{className:`px-3 py-2 text-xs text-muted-foreground`,children:`No results`})]})]})}function Y(){return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})})}function He({content:e}){return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,z.jsx)(ke,{content:e,className:`flex-1 overflow-auto p-4`})})}export{q as CodeEditor};
|
|
8
|
+
`)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[q]);if(!a&&!O&&(!n||!Q&&!r))return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(d)return(0,z.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,z.jsx)(D,{className:`size-5 animate-spin`}),(0,z.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(p)return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:p});if(P)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(je,{filePath:n,projectName:r})});if(F)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Me,{filePath:n,projectName:r})});if(le)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Ne,{filePath:n,projectName:r})});if(ue)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(Y,{}),children:(0,z.jsx)(Pe,{filePath:n,projectName:r})});if(l===`base64`)return(0,z.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,z.jsx)(_,{className:`size-10 text-text-subtle`}),(0,z.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,z.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let lt=I?(0,z.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,z.jsx)(i,{className:`size-3 text-muted-foreground`}),(0,z.jsxs)(`select`,{value:V??``,onChange:e=>{let t=Number(e.target.value);t&&ke(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,z.jsx)(`option`,{value:``,children:`Connection…`}),B.map(e=>(0,z.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,z.jsx)(`button`,{type:`button`,onClick:Qe,disabled:!W,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run SQL`,children:(0,z.jsx)(se,{className:`size-3.5`})})]}):null;return(0,z.jsxs)(`div`,{ref:et,className:`flex flex-col h-full w-full overflow-hidden`,style:tt?{height:`${tt}px`,maxHeight:`${tt}px`}:void 0,children:[a!=null&&Ee&&(0,z.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,z.jsx)(`button`,{type:`button`,onClick:De,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:G?`Raw`:`Beautify`})}),n&&r&&t&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,z.jsx)(Se,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),lt,(0,z.jsx)(we,{ext:N,mdMode:he,onMdModeChange:ge,csvMode:L,onCsvModeChange:ye,wordWrap:E,onToggleWordWrap:ae,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),I&&(!r||!t)&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,z.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?w(n):`SQL`}),lt]}),me&&L===`table`?(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,z.jsx)(Ae,{content:s??``,onContentChange:ot,wordWrap:E})}):pe&&he===`preview`?(0,z.jsx)(He,{content:s??``}):(0,z.jsx)(`div`,{className:`flex-1 overflow-hidden min-h-0`,children:(0,z.jsx)(_e,{height:`100%`,language:o??Be(n??``),value:s??``,onChange:a==null?ot:void 0,onMount:ct,theme:oe,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:E?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:a!=null},loading:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})})}),I&&(Ue||Ge||qe)&&(0,z.jsx)(Ve,{result:Ue,error:Ge,loading:qe,connName:W?.name,onClose:()=>{We(null),Ke(null),Je(!1)},onOpenInTab:Ze}),$e&&(0,z.jsx)(Oe,{editorRef:x,readOnly:a!=null}),A&&(0,z.jsx)(Te,{open:A,defaultName:`Untitled-${e?.untitledNumber??1}`,content:b.current,onSave:st,onCancel:()=>j(!1)})]})}),J=()=>{};function Ve({result:e,error:t,loading:n,connName:r,onClose:a,onOpenInTab:o}){let s=(0,R.useMemo)(()=>e?.changeType===`select`&&e.rows.length>0?{columns:e.columns,rows:e.rows,total:e.rows.length,limit:e.rows.length}:null,[e]),c=(0,R.useMemo)(()=>(e?.columns??[]).map(e=>({name:e,type:`text`,nullable:!0,pk:!1,defaultValue:null})),[e?.columns]),[l,u]=(0,R.useState)(250),d=(0,R.useCallback)(e=>{e.preventDefault();let t=e.clientY,n=l,r=e=>u(Math.max(80,n+(t-e.clientY))),i=()=>{document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)},[l]);return(0,z.jsxs)(`div`,{className:`shrink-0 border-t border-border flex flex-col`,style:{height:l},children:[(0,z.jsx)(`div`,{onMouseDown:d,className:`shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors`,children:(0,z.jsx)(k,{className:`size-3 text-muted-foreground/50`})}),(0,z.jsxs)(`div`,{className:`flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border shrink-0`,children:[(0,z.jsx)(i,{className:`size-3 text-muted-foreground`}),(0,z.jsxs)(`span`,{className:`text-xs font-medium text-foreground truncate flex-1`,children:[r?`${r} · Results`:`Query Results`,e?.executionTimeMs!=null&&(0,z.jsxs)(`span`,{className:`text-muted-foreground ml-1.5 font-normal`,children:[e.executionTimeMs,`ms`]})]}),(0,z.jsxs)(`button`,{type:`button`,onClick:o,title:`Open in DB Viewer tab`,className:`flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors`,children:[(0,z.jsx)(ae,{className:`size-3`}),(0,z.jsx)(`span`,{className:`hidden sm:inline`,children:`Open in Tab`})]}),(0,z.jsx)(`button`,{type:`button`,onClick:a,title:`Close results`,className:`p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors`,children:(0,z.jsx)(g,{className:`size-3`})})]}),(0,z.jsxs)(`div`,{className:`flex-1 overflow-hidden min-h-0`,children:[n&&(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-4 animate-spin text-muted-foreground`})}),t&&(0,z.jsx)(`div`,{className:`px-3 py-2 text-xs text-destructive bg-destructive/5`,children:t}),e?.changeType===`modify`&&(0,z.jsxs)(`div`,{className:`px-3 py-2 text-xs text-green-500`,children:[e.rowsAffected,` row(s) affected`]}),s&&(0,z.jsx)(y,{columns:s.columns,rows:s.rows,total:s.total,limit:s.limit,schema:c,loading:!1,page:1,onPageChange:J,onCellUpdate:J,orderBy:null,orderDir:`ASC`,onToggleSort:J,connectionName:r}),e?.changeType===`select`&&e.rows.length===0&&(0,z.jsx)(`div`,{className:`px-3 py-2 text-xs text-muted-foreground`,children:`No results`})]})]})}function Y(){return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(D,{className:`size-5 animate-spin text-text-subtle`})})}function He({content:e}){return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,z.jsx)(ke,{content:e,className:`flex-1 overflow-auto p-4`})})}export{q as CodeEditor};
|
|
9
|
+
//# sourceMappingURL=code-editor-Clvs0e0Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"mappings":"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","names":[],"ignoreList":[0],"sources":["../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/icons/redo-2.js","../../../src/web/components/editor/editor-breadcrumb.tsx","../../../src/web/components/editor/editor-toolbar.tsx","../../../src/web/components/editor/save-as-dialog.tsx","../../../src/web/components/editor/editor-mobile-toolbar.tsx","../../../src/web/components/editor/code-editor.tsx"],"sourcesContent":["/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"m15 14 5-5-5-5\", key: \"12vg1m\" }],\n [\"path\", { d: \"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13\", key: \"6uklza\" }]\n];\nconst Redo2 = createLucideIcon(\"redo-2\", __iconNode);\n\nexport { __iconNode, Redo2 as default };\n//# sourceMappingURL=redo-2.js.map\n","import { useMemo, useRef, useEffect } from \"react\";\nimport { ChevronRight, Folder, File, FileCode, FileJson, FileText, FileType } from \"lucide-react\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n DropdownMenuSub,\n DropdownMenuSubTrigger,\n DropdownMenuSubContent,\n} from \"@/components/ui/dropdown-menu\";\nimport { useFileStore, type FileNode } from \"@/stores/file-store\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { useTabStore } from \"@/stores/tab-store\";\nimport { useProjectStore } from \"@/stores/project-store\";\nimport { basename } from \"@/lib/utils\";\n\nconst ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {\n ts: FileCode, tsx: FileCode, js: FileCode, jsx: FileCode,\n py: FileCode, rs: FileCode, go: FileCode, html: FileCode,\n css: FileCode, scss: FileCode,\n json: FileJson,\n md: FileText, txt: FileText,\n yaml: FileType, yml: FileType,\n};\n\nfunction getIcon(name: string, isDir: boolean) {\n if (isDir) return Folder;\n const ext = name.split(\".\").pop()?.toLowerCase() ?? \"\";\n return ICON_MAP[ext] ?? File;\n}\n\ninterface BreadcrumbSegment {\n name: string;\n fullPath: string;\n node: FileNode | null;\n siblings: FileNode[];\n /** Folder path whose children are the siblings (empty string = root) */\n parentPath: string;\n}\n\nfunction walkTree(tree: FileNode[], segments: string[]): BreadcrumbSegment[] {\n const result: BreadcrumbSegment[] = [];\n let current: FileNode[] = tree;\n let parentPath = \"\";\n\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]!;\n const fullPath = segments.slice(0, i + 1).join(\"/\");\n const match = current.find((n) => n.name === seg);\n result.push({\n name: seg,\n fullPath,\n node: match ?? null,\n siblings: current,\n parentPath,\n });\n if (match?.children) {\n parentPath = match.path;\n current = match.children;\n } else {\n // Remaining segments — parent children not loaded yet\n for (let j = i + 1; j < segments.length; j++) {\n result.push({\n name: segments[j]!,\n fullPath: segments.slice(0, j + 1).join(\"/\"),\n node: null,\n siblings: [],\n parentPath: segments.slice(0, j).join(\"/\"),\n });\n }\n break;\n }\n }\n return result;\n}\n\nfunction sortNodes(nodes: FileNode[]): FileNode[] {\n return [...nodes].sort((a, b) => {\n if (a.type !== b.type) return a.type === \"directory\" ? -1 : 1;\n return a.name.localeCompare(b.name);\n });\n}\n\ninterface EditorBreadcrumbProps {\n filePath: string;\n projectName: string;\n tabId: string;\n className?: string;\n}\n\nexport function EditorBreadcrumb({ filePath, projectName, tabId, className }: EditorBreadcrumbProps) {\n const tree = useFileStore((s) => s.tree);\n const { updateTab, openTab } = useTabStore(useShallow((s) => ({ updateTab: s.updateTab, openTab: s.openTab })));\n const projectPath = useProjectStore((s) => s.projects.find((p) => p.name === projectName)?.path ?? \"\");\n const scrollRef = useRef<HTMLDivElement>(null);\n\n // Strip project root prefix so segments align with the relative-path file tree\n const { prefixParts, relativePath } = useMemo(() => {\n const norm = filePath.startsWith(\"/\") ? filePath.slice(1) : filePath;\n const normRoot = projectPath.startsWith(\"/\") ? projectPath.slice(1) : projectPath;\n if (normRoot && norm.startsWith(normRoot + \"/\")) {\n const rel = norm.slice(normRoot.length + 1);\n return { prefixParts: normRoot.split(\"/\"), relativePath: rel };\n }\n return { prefixParts: [] as string[], relativePath: norm };\n }, [filePath, projectPath]);\n\n const segments = useMemo(\n () => walkTree(tree, relativePath.split(\"/\").filter(Boolean)),\n [tree, relativePath],\n );\n\n // Auto-scroll to rightmost segment\n useEffect(() => {\n if (scrollRef.current) {\n scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;\n }\n }, [segments]);\n\n function handleFileClick(path: string, e: React.MouseEvent) {\n const name = basename(path);\n if (e.metaKey || e.ctrlKey) {\n openTab({ type: \"editor\", title: name, metadata: { filePath: path, projectName }, projectId: projectName, closable: true });\n } else {\n updateTab(tabId, { title: name, metadata: { filePath: path, projectName } });\n }\n }\n\n return (\n <div ref={scrollRef} className={className}>\n {prefixParts.map((part, i) => (\n <div key={`prefix-${i}`} className=\"flex items-center shrink-0\">\n {i > 0 && <ChevronRight className=\"size-3 text-muted-foreground shrink-0 mx-0.5\" />}\n <span className=\"text-xs text-muted-foreground px-1 py-0.5\">{part}</span>\n </div>\n ))}\n {segments.map((seg, i) => (\n <div key={seg.fullPath} className=\"flex items-center shrink-0\">\n {(i > 0 || prefixParts.length > 0) && <ChevronRight className=\"size-3 text-muted-foreground shrink-0 mx-0.5\" />}\n <SegmentDropdown\n segment={seg}\n isLast={i === segments.length - 1}\n projectName={projectName}\n onFileClick={handleFileClick}\n />\n </div>\n ))}\n </div>\n );\n}\n\ninterface SegmentDropdownProps {\n segment: BreadcrumbSegment;\n isLast: boolean;\n projectName: string;\n onFileClick: (path: string, e: React.MouseEvent) => void;\n}\n\nfunction SegmentDropdown({ segment, isLast, projectName, onFileClick }: SegmentDropdownProps) {\n const loadChildren = useFileStore((s) => s.loadChildren);\n const loadedPaths = useFileStore((s) => s.loadedPaths);\n const sorted = useMemo(() => sortNodes(segment.siblings), [segment.siblings]);\n const isLoaded = loadedPaths.has(segment.parentPath);\n\n function handleOpenChange(open: boolean) {\n if (open && !isLoaded) {\n loadChildren(projectName, segment.parentPath);\n }\n }\n\n return (\n <DropdownMenu onOpenChange={handleOpenChange}>\n <DropdownMenuTrigger asChild>\n <button\n type=\"button\"\n className={`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${\n isLast ? \"text-foreground font-medium\" : \"text-muted-foreground\"\n }`}\n >\n {segment.name}\n </button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"start\" className=\"max-h-[300px] p-1\">\n {sorted.length === 0 ? (\n <DropdownMenuItem disabled className=\"text-xs text-muted-foreground\">\n Loading…\n </DropdownMenuItem>\n ) : (\n sorted.map((node) => (\n <NodeMenuItem\n key={node.path}\n node={node}\n projectName={projectName}\n activePath={segment.fullPath}\n onFileClick={onFileClick}\n />\n ))\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\ninterface NodeMenuItemProps {\n node: FileNode;\n projectName: string;\n activePath: string;\n onFileClick: (path: string, e: React.MouseEvent) => void;\n}\n\nfunction NodeMenuItem({ node, projectName, activePath, onFileClick }: NodeMenuItemProps) {\n const Icon = getIcon(node.name, node.type === \"directory\");\n const isActive = node.path === activePath;\n const loadChildren = useFileStore((s) => s.loadChildren);\n const loadedPaths = useFileStore((s) => s.loadedPaths);\n\n if (node.type === \"directory\") {\n const children = node.children ?? [];\n const isLoaded = loadedPaths.has(node.path);\n\n function handleSubOpen(open: boolean) {\n if (open && !isLoaded) {\n loadChildren(projectName, node.path);\n }\n }\n\n return (\n <DropdownMenuSub onOpenChange={handleSubOpen}>\n <DropdownMenuSubTrigger className={`text-xs gap-1.5 ${isActive ? \"bg-muted\" : \"\"}`}>\n <Icon className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{node.name}</span>\n </DropdownMenuSubTrigger>\n <DropdownMenuSubContent className=\"max-h-[300px] overflow-y-auto p-1\">\n {children.length === 0 ? (\n <DropdownMenuItem disabled className=\"text-xs text-muted-foreground\">\n Loading…\n </DropdownMenuItem>\n ) : (\n sortNodes(children).map((child) => (\n <NodeMenuItem\n key={child.path}\n node={child}\n projectName={projectName}\n activePath={activePath}\n onFileClick={onFileClick}\n />\n ))\n )}\n </DropdownMenuSubContent>\n </DropdownMenuSub>\n );\n }\n\n return (\n <DropdownMenuItem\n className={`text-xs gap-1.5 cursor-pointer ${isActive ? \"bg-muted\" : \"\"}`}\n onSelect={(e) => {\n // onSelect doesn't give MouseEvent, use click handler for Ctrl detection\n }}\n onClick={(e) => {\n onFileClick(node.path, e);\n }}\n >\n <Icon className=\"size-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{node.name}</span>\n </DropdownMenuItem>\n );\n}\n","import { Code, Eye, WrapText, Table, Download } from \"lucide-react\";\nimport { downloadFile } from \"@/lib/file-download\";\n\ninterface EditorToolbarProps {\n ext: string;\n mdMode?: \"edit\" | \"preview\";\n onMdModeChange?: (mode: \"edit\" | \"preview\") => void;\n csvMode?: \"table\" | \"raw\";\n onCsvModeChange?: (mode: \"table\" | \"raw\") => void;\n wordWrap: boolean;\n onToggleWordWrap: () => void;\n filePath?: string;\n projectName?: string;\n className?: string;\n}\n\nfunction ToolbarButton({\n active,\n onClick,\n icon: Icon,\n label,\n}: {\n active: boolean;\n onClick: () => void;\n icon: React.ComponentType<{ className?: string }>;\n label: string;\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n className={`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${\n active ? \"bg-muted text-foreground\" : \"text-muted-foreground hover:text-foreground\"\n }`}\n >\n <Icon className=\"size-3\" />\n <span className=\"hidden sm:inline\">{label}</span>\n </button>\n );\n}\n\nexport function EditorToolbar({\n ext,\n mdMode,\n onMdModeChange,\n csvMode,\n onCsvModeChange,\n wordWrap,\n onToggleWordWrap,\n filePath,\n projectName,\n className,\n}: EditorToolbarProps) {\n const isMarkdown = ext === \"md\" || ext === \"mdx\";\n const isCsv = ext === \"csv\";\n\n return (\n <div className={className}>\n {isMarkdown && onMdModeChange && (\n <>\n <ToolbarButton active={mdMode === \"edit\"} onClick={() => onMdModeChange(\"edit\")} icon={Code} label=\"Edit\" />\n <ToolbarButton active={mdMode === \"preview\"} onClick={() => onMdModeChange(\"preview\")} icon={Eye} label=\"Preview\" />\n </>\n )}\n {isCsv && onCsvModeChange && (\n <>\n <ToolbarButton active={csvMode === \"table\"} onClick={() => onCsvModeChange(\"table\")} icon={Table} label=\"Table\" />\n <ToolbarButton active={csvMode === \"raw\"} onClick={() => onCsvModeChange(\"raw\")} icon={Code} label=\"Raw\" />\n </>\n )}\n <ToolbarButton\n active={wordWrap}\n onClick={onToggleWordWrap}\n icon={WrapText}\n label=\"Wrap\"\n />\n {filePath && projectName && (\n <ToolbarButton\n active={false}\n onClick={() => downloadFile(projectName, filePath)}\n icon={Download}\n label=\"Download\"\n />\n )}\n </div>\n );\n}\n","import { useState, useCallback } from \"react\";\nimport {\n Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter,\n} from \"@/components/ui/dialog\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { FileBrowserPicker } from \"@/components/ui/file-browser-picker\";\nimport { useProjectStore } from \"@/stores/project-store\";\n\ninterface SaveAsDialogProps {\n open: boolean;\n defaultName: string;\n content: string;\n onSave: (fullPath: string, content: string) => void;\n onCancel: () => void;\n}\n\nexport function SaveAsDialog({ open, defaultName, content, onSave, onCancel }: SaveAsDialogProps) {\n const [filename, setFilename] = useState(defaultName);\n const [showPicker, setShowPicker] = useState(false);\n const [error, setError] = useState(\"\");\n const activeProject = useProjectStore((s) => s.activeProject);\n\n const validateAndProceed = useCallback(() => {\n const trimmed = filename.trim();\n if (!trimmed) { setError(\"Filename cannot be empty\"); return; }\n if (/[/\\\\]/.test(trimmed)) { setError(\"Filename cannot contain / or \\\\\"); return; }\n setError(\"\");\n setShowPicker(true);\n }, [filename]);\n\n const handleFolderSelect = useCallback((dirPath: string) => {\n const sep = dirPath.includes(\"\\\\\") ? \"\\\\\" : \"/\";\n const fullPath = dirPath.endsWith(sep) ? `${dirPath}${filename.trim()}` : `${dirPath}${sep}${filename.trim()}`;\n onSave(fullPath, content);\n }, [filename, content, onSave]);\n\n if (showPicker) {\n return (\n <FileBrowserPicker\n open\n mode=\"folder\"\n root={activeProject?.path}\n title={`Save \"${filename.trim()}\" to...`}\n onSelect={handleFolderSelect}\n onCancel={() => setShowPicker(false)}\n />\n );\n }\n\n return (\n <Dialog open={open} onOpenChange={(v) => { if (!v) onCancel(); }}>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>Save As</DialogTitle>\n </DialogHeader>\n <div className=\"flex flex-col gap-2 py-2\">\n <label className=\"text-sm text-muted-foreground\">Filename</label>\n <Input\n value={filename}\n onChange={(e) => { setFilename(e.target.value); setError(\"\"); }}\n onKeyDown={(e) => { if (e.key === \"Enter\") validateAndProceed(); }}\n placeholder=\"e.g. my-file.ts\"\n autoFocus\n />\n {error && <p className=\"text-xs text-destructive\">{error}</p>}\n </div>\n <DialogFooter>\n <Button variant=\"outline\" onClick={onCancel}>Cancel</Button>\n <Button onClick={validateAndProceed}>Choose Folder...</Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { useCallback, useRef, useState } from \"react\";\nimport { ClipboardPaste, Undo2, Redo2, X } from \"lucide-react\";\nimport type * as MonacoType from \"monaco-editor\";\n\n/** Clipboard API requires secure context (HTTPS / localhost) */\nconst isSecureContext = typeof window !== \"undefined\" && window.isSecureContext;\n\n/** Symbols commonly needed when coding on mobile — ordered by frequency */\nconst SYMBOL_KEYS = [\n \"(\", \")\", \"{\", \"}\", \"[\", \"]\",\n \"<\", \">\", \";\", \":\", \"=\",\n '\"', \"'\", \"`\", \"/\", \"\\\\\", \"_\", \"#\",\n];\n\nconst btnBase =\n \"px-2 py-1.5 rounded text-xs min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none\";\nconst btnSymbol =\n \"px-3 py-1.5 rounded text-xs font-mono min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none\";\nconst divider = \"w-px h-5 bg-border mx-0.5 shrink-0\";\n\ninterface EditorMobileToolbarProps {\n editorRef: React.RefObject<MonacoType.editor.IStandaloneCodeEditor | null>;\n readOnly?: boolean;\n}\n\nexport function EditorMobileToolbar({ editorRef, readOnly }: EditorMobileToolbarProps) {\n const getEditor = useCallback(() => editorRef.current, [editorRef]);\n\n /** Insert text at cursor position in Monaco */\n const insertText = useCallback((text: string) => {\n const editor = getEditor();\n if (!editor) return;\n editor.focus();\n const selection = editor.getSelection();\n if (selection) {\n editor.executeEdits(\"mobile-toolbar\", [{ range: selection, text }]);\n }\n }, [getEditor]);\n\n // --- Paste: two strategies based on secure context ---\n const [pasteMode, setPasteMode] = useState(false);\n const pasteRef = useRef<HTMLTextAreaElement | null>(null);\n\n // HTTPS: use Clipboard API directly (single tap)\n const handleClipboardPaste = useCallback(async () => {\n try {\n const text = await navigator.clipboard.readText();\n if (text) insertText(text);\n } catch { /* permission denied */ }\n }, [insertText]);\n\n // HTTP fallback: show textarea for native long-press paste\n const openPasteMode = useCallback(() => {\n setPasteMode(true);\n requestAnimationFrame(() => pasteRef.current?.focus());\n }, []);\n\n const handleNativePaste = useCallback((e: React.ClipboardEvent<HTMLTextAreaElement>) => {\n e.preventDefault();\n const text = e.clipboardData.getData(\"text/plain\");\n if (!text) return;\n setPasteMode(false);\n insertText(text);\n }, [insertText]);\n\n const handleUndo = useCallback(() => {\n const editor = getEditor();\n if (!editor) return;\n editor.focus();\n editor.trigger(\"mobile-toolbar\", \"undo\", null);\n }, [getEditor]);\n\n const handleRedo = useCallback(() => {\n const editor = getEditor();\n if (!editor) return;\n editor.focus();\n editor.trigger(\"mobile-toolbar\", \"redo\", null);\n }, [getEditor]);\n\n const handleTab = useCallback(() => {\n const editor = getEditor();\n if (!editor) return;\n editor.focus();\n editor.trigger(\"mobile-toolbar\", \"tab\", null);\n }, [getEditor]);\n\n if (readOnly) return null;\n\n return (\n <div className=\"shrink-0 border-t border-border bg-surface\">\n {/* HTTP-only: textarea for native paste via long-press */}\n {!isSecureContext && pasteMode && (\n <div className=\"flex items-center gap-2 px-2 py-1.5 border-b border-border bg-muted/50\">\n <textarea\n ref={pasteRef}\n onPaste={handleNativePaste}\n placeholder=\"Long-press here → Paste\"\n className=\"flex-1 h-8 rounded border border-border bg-background text-foreground text-xs px-2 py-1.5 resize-none focus:outline-none focus:ring-1 focus:ring-primary\"\n />\n <button\n type=\"button\"\n onClick={() => setPasteMode(false)}\n className=\"p-1.5 rounded text-muted-foreground active:bg-muted transition-colors\"\n >\n <X size={14} />\n </button>\n </div>\n )}\n\n {/* Toolbar buttons */}\n <div className=\"flex items-center gap-1 px-2 py-1.5 overflow-x-auto\">\n {/* Paste: Clipboard API on HTTPS, textarea fallback on HTTP */}\n <button\n type=\"button\"\n onClick={isSecureContext ? handleClipboardPaste : openPasteMode}\n className={btnBase}\n title=\"Paste\"\n >\n <ClipboardPaste size={14} />\n </button>\n <button type=\"button\" onClick={handleUndo} className={btnBase} title=\"Undo\">\n <Undo2 size={14} />\n </button>\n <button type=\"button\" onClick={handleRedo} className={btnBase} title=\"Redo\">\n <Redo2 size={14} />\n </button>\n\n <div className={divider} />\n\n <button type=\"button\" onClick={handleTab} className={btnSymbol}>\n Tab\n </button>\n\n <div className={divider} />\n\n {SYMBOL_KEYS.map((key) => (\n <button key={key} type=\"button\" onClick={() => insertText(key)} className={btnSymbol}>\n {key}\n </button>\n ))}\n </div>\n </div>\n );\n}\n","import { useEffect, useState, useCallback, useRef, useMemo, memo, lazy, Suspense } from \"react\";\nimport Editor, { type OnMount } from \"@monaco-editor/react\";\nimport type * as MonacoType from \"monaco-editor\";\nimport { api, projectUrl } from \"@/lib/api-client\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { useTabStore } from \"@/stores/tab-store\";\nimport { usePanelStore } from \"@/stores/panel-store\";\nimport { useSettingsStore } from \"@/stores/settings-store\";\nimport { basename } from \"@/lib/utils\";\nimport { useMonacoTheme } from \"@/lib/use-monaco-theme\";\nimport { Loader2, FileWarning, Play, Database, ExternalLink, X, GripHorizontal } from \"lucide-react\";\nimport { EditorBreadcrumb } from \"./editor-breadcrumb\";\nimport { EditorToolbar } from \"./editor-toolbar\";\nimport { SaveAsDialog } from \"./save-as-dialog\";\nimport { EditorMobileToolbar } from \"./editor-mobile-toolbar\";\nimport { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from \"../database/sql-completion-provider\";\nimport { useConnections, type Connection } from \"../database/use-connections\";\nimport { GlideDataGrid } from \"../database/glide-data-grid\";\nimport type { GridColumnSchema } from \"../database/glide-grid-types\";\nimport type { DbQueryResult } from \"../database/use-database\";\n\nconst MarkdownRenderer = lazy(() =>\n import(\"@/components/shared/markdown-renderer\").then((m) => ({ default: m.MarkdownRenderer }))\n);\nconst CsvPreview = lazy(() => import(\"./csv-preview\").then((m) => ({ default: m.CsvPreview })));\nconst ImagePreview = lazy(() => import(\"./image-preview\").then((m) => ({ default: m.ImagePreview })));\nconst PdfPreview = lazy(() => import(\"./pdf-preview\").then((m) => ({ default: m.PdfPreview })));\nconst VideoPreview = lazy(() => import(\"./video-preview\").then((m) => ({ default: m.VideoPreview })));\nconst AudioPreview = lazy(() => import(\"./audio-preview\").then((m) => ({ default: m.AudioPreview })));\n\n/** Image extensions renderable inline */\nconst IMAGE_EXTS = new Set([\"png\", \"jpg\", \"jpeg\", \"gif\", \"webp\", \"svg\", \"ico\"]);\n/** Video extensions playable inline */\nconst VIDEO_EXTS = new Set([\"mp4\", \"webm\", \"mov\", \"ogg\", \"avi\", \"mkv\"]);\n/** Audio extensions playable inline */\nconst AUDIO_EXTS = new Set([\"mp3\", \"wav\", \"flac\", \"aac\", \"m4a\", \"wma\"]);\n/** SQLite extensions — redirect to sqlite viewer */\nconst SQLITE_EXTS = new Set([\"db\", \"sqlite\", \"sqlite3\"]);\n\nfunction getFileExt(filename: string): string {\n return filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n}\n\nfunction getMonacoLanguage(filename: string): string {\n const ext = getFileExt(filename);\n const map: Record<string, string> = {\n js: \"javascript\", jsx: \"javascript\",\n ts: \"typescript\", tsx: \"typescript\",\n py: \"python\", html: \"html\",\n css: \"css\", scss: \"scss\",\n json: \"json\", md: \"markdown\", mdx: \"markdown\",\n yaml: \"yaml\", yml: \"yaml\",\n sh: \"shell\", bash: \"shell\",\n sql: \"sql\",\n };\n return map[ext] ?? \"plaintext\";\n}\n\ninterface CodeEditorProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nexport const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEditorProps) {\n const filePath = metadata?.filePath as string | undefined;\n const projectName = metadata?.projectName as string | undefined;\n // Inline content mode: read-only Monaco with pre-loaded content (e.g. cell viewer)\n const inlineContent = metadata?.inlineContent as string | undefined;\n const inlineLanguage = metadata?.inlineLanguage as string | undefined;\n const [content, setContent] = useState<string | null>(inlineContent ?? null);\n const [encoding, setEncoding] = useState<string>(\"utf-8\");\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [unsaved, setUnsaved] = useState(false);\n const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestContentRef = useRef<string>(\"\");\n const editorRef = useRef<MonacoType.editor.IStandaloneCodeEditor | null>(null);\n const { tabs, updateTab } = useTabStore(useShallow((s) => ({ tabs: s.tabs, updateTab: s.updateTab })));\n const { wordWrap, toggleWordWrap } = useSettingsStore(useShallow((s) => ({ wordWrap: s.wordWrap, toggleWordWrap: s.toggleWordWrap })));\n const monacoTheme = useMonacoTheme();\n\n const isUntitled = metadata?.isUntitled === true;\n const savedContent = metadata?.unsavedContent as string | undefined;\n const [showSaveAs, setShowSaveAs] = useState(false);\n\n const ownTab = tabs.find((t) => t.id === tabId);\n const ext = filePath ? getFileExt(filePath) : \"\";\n const isImage = IMAGE_EXTS.has(ext);\n const isPdf = ext === \"pdf\";\n const isVideo = VIDEO_EXTS.has(ext);\n const isAudio = AUDIO_EXTS.has(ext);\n const isSqlite = SQLITE_EXTS.has(ext);\n const isMarkdown = ext === \"md\" || ext === \"mdx\";\n const isCsv = ext === \"csv\";\n const isSql = ext === \"sql\";\n const [mdMode, setMdMode] = useState<\"edit\" | \"preview\">(\"preview\");\n const [csvMode, setCsvMode] = useState<\"table\" | \"raw\">(\"table\");\n\n // SQL file: connection picker + autocomplete + run in DB viewer\n const { connections, cachedTables, refreshTables } = useConnections();\n const [sqlConnId, setSqlConnId] = useState<number | null>(() => {\n if (!isSql || !filePath) return null;\n const stored = localStorage.getItem(`ppm:sql-conn:${filePath}`);\n return stored ? Number(stored) : null;\n });\n const monacoInstanceRef = useRef<typeof MonacoType | null>(null);\n const completionDisposable = useRef<MonacoType.IDisposable | null>(null);\n\n const selectedSqlConn = useMemo(() => connections.find((c) => c.id === sqlConnId) ?? null, [connections, sqlConnId]);\n\n // Beautify for inline content (must be before early returns to maintain hook order)\n const canBeautifyInline = inlineContent != null && (inlineLanguage === \"json\" || inlineLanguage === \"xml\");\n const [isBeautified, setIsBeautified] = useState(false);\n const handleBeautifyInline = useCallback(() => {\n if (!inlineContent) return;\n if (isBeautified) {\n setContent(inlineContent);\n setIsBeautified(false);\n } else {\n const trimmed = inlineContent.trimStart();\n if (inlineLanguage === \"json\") {\n try { setContent(JSON.stringify(JSON.parse(trimmed), null, 2)); setIsBeautified(true); } catch { /* not valid */ }\n } else if (inlineLanguage === \"xml\") {\n let indent = 0;\n const formatted = trimmed.replace(/(>)(<)(\\/*)/g, \"$1\\n$2$3\")\n .split(\"\\n\")\n .map((line) => {\n const l = line.trim();\n if (l.startsWith(\"</\")) indent = Math.max(0, indent - 1);\n const padded = \" \".repeat(indent) + l;\n if (l.startsWith(\"<\") && !l.startsWith(\"</\") && !l.endsWith(\"/>\") && !l.includes(\"</\")) indent++;\n return padded;\n })\n .join(\"\\n\");\n setContent(formatted);\n setIsBeautified(true);\n }\n }\n }, [inlineContent, inlineLanguage, isBeautified]);\n\n // Persist selected connection per file\n const handleSqlConnChange = useCallback((connId: number) => {\n setSqlConnId(connId);\n if (filePath) localStorage.setItem(`ppm:sql-conn:${filePath}`, String(connId));\n // Refresh tables for autocomplete\n refreshTables(connId).catch(() => {});\n }, [filePath, refreshTables]);\n\n // Build SchemaInfo for .sql file autocomplete\n const sqlSchemaInfo = useMemo<SchemaInfo | undefined>(() => {\n if (!isSql || !sqlConnId) return undefined;\n const tables = (cachedTables.get(sqlConnId) ?? []).map((t) => ({ name: t.tableName, schema: t.schemaName }));\n if (tables.length === 0) return undefined;\n return {\n tables,\n getColumns: async (table: string, schema?: string) => {\n return api.get<{ name: string; type: string }[]>(\n `/api/db/connections/${sqlConnId}/schema?table=${encodeURIComponent(table)}${schema ? `&schema=${encodeURIComponent(schema)}` : \"\"}`,\n );\n },\n };\n }, [isSql, sqlConnId, cachedTables]);\n\n // Register/dispose completion provider when connection changes\n useEffect(() => {\n if (!monacoInstanceRef.current || !sqlSchemaInfo) return;\n completionDisposable.current?.dispose();\n clearCompletionCache();\n completionDisposable.current = monacoInstanceRef.current.languages.registerCompletionItemProvider(\n \"sql\",\n createSqlCompletionProvider(monacoInstanceRef.current, sqlSchemaInfo),\n );\n return () => { completionDisposable.current?.dispose(); };\n }, [sqlSchemaInfo]);\n\n // Run SQL inline — execute query and show results in bottom panel\n const openTab = useTabStore((s) => s.openTab);\n const [sqlResult, setSqlResult] = useState<DbQueryResult | null>(null);\n const [sqlError, setSqlError] = useState<string | null>(null);\n const [sqlLoading, setSqlLoading] = useState(false);\n const [sqlResultSql, setSqlResultSql] = useState<string>(\"\");\n const runSqlInViewer = useCallback(async (sqlText: string) => {\n if (!selectedSqlConn) return;\n setSqlLoading(true);\n setSqlError(null);\n setSqlResultSql(sqlText);\n try {\n const result = await api.post<DbQueryResult>(`/api/db/connections/${selectedSqlConn.id}/query`, { sql: sqlText });\n setSqlResult(result);\n } catch (e) {\n setSqlError((e as Error).message);\n setSqlResult(null);\n } finally {\n setSqlLoading(false);\n }\n }, [selectedSqlConn]);\n const openSqlResultInTab = useCallback(() => {\n if (!selectedSqlConn || !sqlResultSql) return;\n openTab({\n type: \"database\",\n title: `${selectedSqlConn.name} · Query`,\n projectId: null,\n closable: true,\n metadata: { connectionId: selectedSqlConn.id, connectionName: selectedSqlConn.name, dbType: selectedSqlConn.type, initialSql: sqlResultSql },\n });\n }, [selectedSqlConn, openTab, sqlResultSql]);\n\n const handleRunInDbViewer = useCallback(() => {\n if (!editorRef.current || !selectedSqlConn) return;\n const editor = editorRef.current;\n const selection = editor.getSelection();\n const sqlText = selection && !selection.isEmpty()\n ? editor.getModel()?.getValueInRange(selection) ?? editor.getValue()\n : editor.getValue();\n runSqlInViewer(sqlText);\n }, [selectedSqlConn, runSqlInViewer]);\n\n // Touch device detection for mobile toolbar\n const isMobile = typeof window !== \"undefined\" && \"ontouchstart\" in window;\n\n // Track visual viewport so toolbar stays above mobile keyboard\n const containerRef = useRef<HTMLDivElement | null>(null);\n const [mobileHeight, setMobileHeight] = useState<number | null>(null);\n useEffect(() => {\n if (!isMobile) return;\n const vv = window.visualViewport;\n if (!vv) return;\n const handle = () => {\n const el = containerRef.current;\n if (!el) return;\n // Calculate available height = viewport height - element's top offset from viewport\n const top = el.getBoundingClientRect().top;\n setMobileHeight(vv.height - Math.max(0, top));\n };\n vv.addEventListener(\"resize\", handle);\n vv.addEventListener(\"scroll\", handle);\n return () => {\n vv.removeEventListener(\"resize\", handle);\n vv.removeEventListener(\"scroll\", handle);\n };\n }, [isMobile]);\n\n // CodeLens: inline Run buttons between SQL statements\n const codeLensDisposable = useRef<MonacoType.IDisposable[]>([]);\n const runSqlRef = useRef(runSqlInViewer);\n runSqlRef.current = runSqlInViewer;\n\n // Cleanup CodeLens providers on unmount to prevent duplicate \"Run\" buttons\n useEffect(() => {\n return () => {\n codeLensDisposable.current.forEach((d) => d.dispose());\n codeLensDisposable.current = [];\n };\n }, []);\n\n // Redirect .db files to sqlite viewer by changing tab type\n useEffect(() => {\n if (isSqlite && tabId) updateTab(tabId, { type: \"sqlite\" });\n }, [isSqlite, tabId, updateTab]);\n\n // Detect external (absolute) file path — not relative to project\n const isExternalFile = filePath ? /^(\\/|[A-Za-z]:[/\\\\])/.test(filePath) : false;\n\n // Load file content\n useEffect(() => {\n if (inlineContent != null) { setLoading(false); return; }\n if (isUntitled) {\n setContent(savedContent ?? \"\");\n latestContentRef.current = savedContent ?? \"\";\n setLoading(false);\n if (savedContent) setUnsaved(true);\n return;\n }\n if (!filePath) return;\n if (!isExternalFile && !projectName) return;\n if (isImage || isPdf || isVideo || isAudio) { setLoading(false); return; }\n\n setLoading(true);\n setError(null);\n\n const readUrl = isExternalFile\n ? `/api/fs/read?path=${encodeURIComponent(filePath)}`\n : `${projectUrl(projectName!)}/files/read?path=${encodeURIComponent(filePath)}`;\n\n api\n .get<{ content: string; encoding?: string }>(readUrl)\n .then((data) => {\n setContent(data.content);\n if (data.encoding) setEncoding(data.encoding);\n latestContentRef.current = data.content;\n setLoading(false);\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : \"Failed to load file\");\n setLoading(false);\n });\n\n return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current); };\n }, [filePath, projectName, isImage, isPdf, isExternalFile, isUntitled]);\n\n // Real-time reload: listen for file:changed WS events, re-fetch if editor is clean\n const unsavedRef = useRef(unsaved);\n unsavedRef.current = unsaved;\n useEffect(() => {\n if (!filePath || !projectName || inlineContent != null || isUntitled) return;\n const handler = (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (detail.projectName !== projectName || detail.path !== filePath) return;\n if (unsavedRef.current) return; // don't overwrite unsaved changes\n const readUrl = isExternalFile\n ? `/api/fs/read?path=${encodeURIComponent(filePath)}`\n : `${projectUrl(projectName)}/files/read?path=${encodeURIComponent(filePath)}`;\n api.get<{ content: string; encoding?: string }>(readUrl).then((data) => {\n if (data.content === latestContentRef.current) return; // skip if unchanged (e.g. self-save)\n setContent(data.content);\n latestContentRef.current = data.content;\n if (data.encoding) setEncoding(data.encoding);\n }).catch(() => {});\n };\n window.addEventListener(\"file:changed\", handler);\n return () => window.removeEventListener(\"file:changed\", handler);\n }, [filePath, projectName, isExternalFile, inlineContent, isUntitled]);\n\n // Update tab title unsaved indicator (skip for inline content — title set by caller)\n useEffect(() => {\n if (!ownTab || inlineContent != null) return;\n const baseName = isUntitled\n ? `Untitled-${metadata?.untitledNumber ?? 1}`\n : (filePath ? basename(filePath) : \"Untitled\");\n const newTitle = unsaved ? `${baseName} \\u25CF` : baseName;\n if (ownTab.title !== newTitle) updateTab(ownTab.id, { title: newTitle });\n }, [unsaved]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const saveFile = useCallback(\n async (text: string) => {\n if (!filePath) return;\n if (!isExternalFile && !projectName) return;\n try {\n if (isExternalFile) {\n await api.put(\"/api/fs/write\", { path: filePath, content: text });\n } else {\n await api.put(`${projectUrl(projectName!)}/files/write`, { path: filePath, content: text });\n }\n setUnsaved(false);\n } catch { /* Silent — unsaved indicator persists */ }\n },\n [filePath, projectName, isExternalFile],\n );\n\n function handleChange(value: string | undefined) {\n const val = value ?? \"\";\n setContent(val);\n latestContentRef.current = val;\n setUnsaved(true);\n if (saveTimerRef.current) clearTimeout(saveTimerRef.current);\n if (isUntitled) {\n // Persist to metadata for localStorage survival\n saveTimerRef.current = setTimeout(() => {\n if (tabId) updateTab(tabId, { metadata: { ...metadata, unsavedContent: latestContentRef.current } });\n }, 2000);\n } else {\n saveTimerRef.current = setTimeout(() => saveFile(latestContentRef.current), 1000);\n }\n }\n\n // Save As completion — transitions untitled → saved file\n const handleSaveAs = useCallback(async (targetPath: string, savedText: string) => {\n try {\n // Clear any pending metadata persistence timer to prevent race condition\n if (saveTimerRef.current) clearTimeout(saveTimerRef.current);\n await api.put(\"/api/fs/write\", { path: targetPath, content: savedText });\n if (tabId) {\n // Close old untitled tab and open as proper file tab\n const { closeTab, openTab } = usePanelStore.getState();\n closeTab(tabId);\n openTab({\n type: \"editor\",\n title: basename(targetPath),\n projectId: null,\n metadata: { filePath: targetPath },\n closable: true,\n });\n }\n setUnsaved(false);\n setShowSaveAs(false);\n } catch { /* silent — user can retry */ }\n }, [tabId]);\n\n // Jump to line when metadata.lineNumber is set (e.g. from search panel)\n const lineNumber = metadata?.lineNumber as number | undefined;\n const handleEditorMount: OnMount = useCallback((editor, monaco) => {\n editorRef.current = editor;\n monacoInstanceRef.current = monaco;\n if (lineNumber && lineNumber > 0) {\n setTimeout(() => {\n editor.revealLineInCenter(lineNumber);\n editor.setPosition({ lineNumber, column: 1 });\n editor.focus();\n }, 100);\n }\n // Ctrl+S → Save As for untitled tabs\n if (isUntitled) {\n editor.addCommand(\n monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,\n () => setShowSaveAs(true),\n );\n }\n editor.addCommand(\n monaco.KeyMod.Alt | monaco.KeyCode.KeyZ,\n () => useSettingsStore.getState().toggleWordWrap(),\n );\n monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({\n noSemanticValidation: true, noSyntaxValidation: true, noSuggestionDiagnostics: true,\n });\n monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({\n noSemanticValidation: true, noSyntaxValidation: true, noSuggestionDiagnostics: true,\n });\n // Register SQL completion if schema available\n if (sqlSchemaInfo) {\n completionDisposable.current?.dispose();\n completionDisposable.current = monaco.languages.registerCompletionItemProvider(\n \"sql\", createSqlCompletionProvider(monaco, sqlSchemaInfo),\n );\n }\n\n // Register CodeLens for inline Run buttons on .sql files (scoped to this editor's model)\n if (isSql) {\n codeLensDisposable.current.forEach((d) => d.dispose());\n codeLensDisposable.current = [];\n\n const thisModel = editor.getModel();\n const cmdId = editor.addCommand(0, (_accessor: unknown, sql: string) => {\n if (sql) runSqlRef.current(sql);\n });\n\n if (cmdId && thisModel) {\n const provider = monaco.languages.registerCodeLensProvider(\"sql\", {\n provideCodeLenses: (model: MonacoType.editor.ITextModel) => {\n // Only provide lenses for THIS editor's model, not all SQL models\n if (model !== thisModel) return { lenses: [], dispose: () => {} };\n\n const lenses: MonacoType.languages.CodeLens[] = [];\n const text = model.getValue();\n const lines = text.split(\"\\n\");\n let stmtStartLine = -1;\n let stmtLines: string[] = [];\n let dollarBlock = false; // Track DO $$ ... $$ blocks\n\n const addLens = (line: number, stmt: string) => {\n const trimmed = stmt.trim();\n if (!trimmed || trimmed.startsWith(\"--\")) return;\n lenses.push({\n range: { startLineNumber: line, startColumn: 1, endLineNumber: line, endColumn: 1 },\n command: { id: cmdId, title: \"\\u25B7 Run\", arguments: [trimmed] },\n });\n };\n\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (stmtStartLine === -1) {\n if (!trimmed || trimmed.startsWith(\"--\")) continue;\n stmtStartLine = i + 1;\n stmtLines = [];\n }\n stmtLines.push(lines[i]!);\n\n // Detect $$ dollar-quoted block start/end\n const dollarMatches = (trimmed.match(/\\$\\$/g) || []).length;\n if (dollarMatches % 2 === 1) dollarBlock = !dollarBlock;\n\n // Only split on ; when NOT inside a $$ block\n if (!dollarBlock && trimmed.endsWith(\";\")) {\n addLens(stmtStartLine, stmtLines.join(\"\\n\"));\n stmtStartLine = -1;\n stmtLines = [];\n }\n }\n if (stmtStartLine > 0 && stmtLines.join(\"\").trim()) {\n addLens(stmtStartLine, stmtLines.join(\"\\n\"));\n }\n return { lenses, dispose: () => {} };\n },\n });\n codeLensDisposable.current.push(provider);\n }\n }\n }, [sqlSchemaInfo]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!inlineContent && !isUntitled && (!filePath || (!isExternalFile && !projectName))) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n No file selected.\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full gap-2 text-text-secondary\">\n <Loader2 className=\"size-5 animate-spin\" />\n <span className=\"text-sm\">Loading file...</span>\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full text-error text-sm\">{error}</div>\n );\n }\n\n if (isImage) return <Suspense fallback={<LoadingSpinner />}><ImagePreview filePath={filePath!} projectName={projectName!} /></Suspense>;\n if (isPdf) return <Suspense fallback={<LoadingSpinner />}><PdfPreview filePath={filePath!} projectName={projectName!} /></Suspense>;\n if (isVideo) return <Suspense fallback={<LoadingSpinner />}><VideoPreview filePath={filePath!} projectName={projectName!} /></Suspense>;\n if (isAudio) return <Suspense fallback={<LoadingSpinner />}><AudioPreview filePath={filePath!} projectName={projectName!} /></Suspense>;\n\n if (encoding === \"base64\") {\n return (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-text-secondary\">\n <FileWarning className=\"size-10 text-text-subtle\" />\n <p className=\"text-sm\">This file is a binary format and cannot be displayed.</p>\n <p className=\"text-xs text-text-subtle\">{filePath}</p>\n </div>\n );\n }\n\n /** SQL connection picker bar (shared between breadcrumb and standalone) */\n const sqlPickerBar = isSql ? (\n <div className=\"shrink-0 flex items-center gap-1 px-2 border-l border-border\">\n <Database className=\"size-3 text-muted-foreground\" />\n <select\n value={sqlConnId ?? \"\"}\n onChange={(e) => { const v = Number(e.target.value); if (v) handleSqlConnChange(v); }}\n className=\"h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]\"\n title=\"Select connection for autocomplete\"\n >\n <option value=\"\">Connection…</option>\n {connections.map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}\n </select>\n <button\n type=\"button\"\n onClick={handleRunInDbViewer}\n disabled={!selectedSqlConn}\n className=\"p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors\"\n title=\"Run SQL\"\n >\n <Play className=\"size-3.5\" />\n </button>\n </div>\n ) : null;\n\n return (\n <div\n ref={containerRef}\n className=\"flex flex-col h-full w-full overflow-hidden\"\n style={mobileHeight ? { height: `${mobileHeight}px`, maxHeight: `${mobileHeight}px` } : undefined}\n >\n {/* Inline content toolbar (cell viewer mode) */}\n {inlineContent != null && canBeautifyInline && (\n <div className=\"flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2\">\n <button type=\"button\" onClick={handleBeautifyInline}\n className=\"text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground\">\n {isBeautified ? \"Raw\" : \"Beautify\"}\n </button>\n </div>\n )}\n {/* Breadcrumb + Toolbar bar — desktop only */}\n {filePath && projectName && tabId && (\n <div className=\"hidden md:flex items-center h-7 border-b border-border bg-background shrink-0\">\n <EditorBreadcrumb\n filePath={filePath}\n projectName={projectName}\n tabId={tabId}\n className=\"flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5\"\n />\n {sqlPickerBar}\n <EditorToolbar\n ext={ext}\n mdMode={mdMode}\n onMdModeChange={setMdMode}\n csvMode={csvMode}\n onCsvModeChange={setCsvMode}\n wordWrap={wordWrap}\n onToggleWordWrap={toggleWordWrap}\n filePath={filePath}\n projectName={projectName}\n className=\"shrink-0 flex items-center gap-1 px-2\"\n />\n </div>\n )}\n\n {/* Standalone SQL toolbar for external files (no breadcrumb available) */}\n {isSql && (!projectName || !tabId) && (\n <div className=\"hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2\">\n <span className=\"text-xs text-muted-foreground truncate flex-1\">{filePath ? basename(filePath) : \"SQL\"}</span>\n {sqlPickerBar}\n </div>\n )}\n\n {/* Content area */}\n {isCsv && csvMode === \"table\" ? (\n <Suspense fallback={<div className=\"flex items-center justify-center h-full\"><Loader2 className=\"size-5 animate-spin text-text-subtle\" /></div>}>\n <CsvPreview content={content ?? \"\"} onContentChange={handleChange} wordWrap={wordWrap} />\n </Suspense>\n ) : isMarkdown && mdMode === \"preview\" ? (\n <MarkdownPreview content={content ?? \"\"} />\n ) : (\n <div className=\"flex-1 overflow-hidden min-h-0\">\n <Editor\n height=\"100%\"\n language={inlineLanguage ?? getMonacoLanguage(filePath ?? \"\")}\n value={content ?? \"\"}\n onChange={inlineContent != null ? undefined : handleChange}\n onMount={handleEditorMount}\n theme={monacoTheme}\n options={{\n fontSize: 13,\n fontFamily: \"Menlo, Monaco, Consolas, monospace\",\n wordWrap: wordWrap ? \"on\" : \"off\",\n minimap: { enabled: false },\n scrollBeyondLastLine: false,\n automaticLayout: true,\n lineNumbers: \"on\",\n folding: true,\n bracketPairColorization: { enabled: true },\n readOnly: inlineContent != null,\n }}\n loading={<Loader2 className=\"size-5 animate-spin text-text-subtle\" />}\n />\n </div>\n )}\n\n {/* Inline SQL result panel */}\n {isSql && (sqlResult || sqlError || sqlLoading) && (\n <SqlResultPanel\n result={sqlResult} error={sqlError} loading={sqlLoading}\n connName={selectedSqlConn?.name}\n onClose={() => { setSqlResult(null); setSqlError(null); setSqlLoading(false); }}\n onOpenInTab={openSqlResultInTab}\n />\n )}\n\n {/* Mobile toolbar — bottom, like terminal */}\n {isMobile && <EditorMobileToolbar editorRef={editorRef} readOnly={inlineContent != null} />}\n\n {/* Save As dialog for untitled tabs */}\n {showSaveAs && (\n <SaveAsDialog\n open={showSaveAs}\n defaultName={`Untitled-${metadata?.untitledNumber ?? 1}`}\n content={latestContentRef.current}\n onSave={handleSaveAs}\n onCancel={() => setShowSaveAs(false)}\n />\n )}\n </div>\n );\n});\n\nconst NOOP = () => {};\n\n/** Inline SQL result panel — shows query results below the editor */\nfunction SqlResultPanel({ result, error, loading, connName, onClose, onOpenInTab }: {\n result: DbQueryResult | null;\n error: string | null;\n loading: boolean;\n connName?: string;\n onClose: () => void;\n onOpenInTab: () => void;\n}) {\n const tableData = useMemo(() => (\n result?.changeType === \"select\" && result.rows.length > 0\n ? { columns: result.columns, rows: result.rows, total: result.rows.length, limit: result.rows.length }\n : null\n ), [result]);\n\n const querySchema = useMemo<GridColumnSchema[]>(() => (\n (result?.columns ?? []).map((c) => ({ name: c, type: \"text\", nullable: true, pk: false, defaultValue: null }))\n ), [result?.columns]);\n\n const [panelHeight, setPanelHeight] = useState(250);\n const handleDrag = useCallback((e: React.MouseEvent) => {\n e.preventDefault();\n const startY = e.clientY;\n const startH = panelHeight;\n const onMove = (ev: MouseEvent) => setPanelHeight(Math.max(80, startH + (startY - ev.clientY)));\n const onUp = () => { document.removeEventListener(\"mousemove\", onMove); document.removeEventListener(\"mouseup\", onUp); };\n document.addEventListener(\"mousemove\", onMove);\n document.addEventListener(\"mouseup\", onUp);\n }, [panelHeight]);\n\n return (\n <div className=\"shrink-0 border-t border-border flex flex-col\" style={{ height: panelHeight }}>\n {/* Resize handle */}\n <div onMouseDown={handleDrag}\n className=\"shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors\">\n <GripHorizontal className=\"size-3 text-muted-foreground/50\" />\n </div>\n {/* Title bar */}\n <div className=\"flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border shrink-0\">\n <Database className=\"size-3 text-muted-foreground\" />\n <span className=\"text-xs font-medium text-foreground truncate flex-1\">\n {connName ? `${connName} · Results` : \"Query Results\"}\n {result?.executionTimeMs != null && <span className=\"text-muted-foreground ml-1.5 font-normal\">{result.executionTimeMs}ms</span>}\n </span>\n <button type=\"button\" onClick={onOpenInTab} title=\"Open in DB Viewer tab\"\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors\">\n <ExternalLink className=\"size-3\" />\n <span className=\"hidden sm:inline\">Open in Tab</span>\n </button>\n <button type=\"button\" onClick={onClose} title=\"Close results\"\n className=\"p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors\">\n <X className=\"size-3\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-hidden min-h-0\">\n {loading && (\n <div className=\"flex items-center justify-center h-full\">\n <Loader2 className=\"size-4 animate-spin text-muted-foreground\" />\n </div>\n )}\n {error && <div className=\"px-3 py-2 text-xs text-destructive bg-destructive/5\">{error}</div>}\n {result?.changeType === \"modify\" && (\n <div className=\"px-3 py-2 text-xs text-green-500\">\n {result.rowsAffected} row(s) affected\n </div>\n )}\n {tableData && (\n <GlideDataGrid\n columns={tableData.columns} rows={tableData.rows} total={tableData.total} limit={tableData.limit}\n schema={querySchema} loading={false}\n page={1} onPageChange={NOOP} onCellUpdate={NOOP}\n orderBy={null} orderDir=\"ASC\" onToggleSort={NOOP}\n connectionName={connName}\n />\n )}\n {result?.changeType === \"select\" && result.rows.length === 0 && (\n <div className=\"px-3 py-2 text-xs text-muted-foreground\">No results</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction LoadingSpinner() {\n return <div className=\"flex items-center justify-center h-full\"><Loader2 className=\"size-5 animate-spin text-text-subtle\" /></div>;\n}\n\nfunction MarkdownPreview({ content }: { content: string }) {\n return (\n <Suspense fallback={<div className=\"animate-pulse h-4 bg-muted rounded m-4\" />}>\n <MarkdownRenderer content={content} className=\"flex-1 overflow-auto p-4\" />\n </Suspense>\n );\n}\n\n"],"file":"code-editor-Clvs0e0Q.js"}
|
|
@@ -16,4 +16,5 @@ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./v
|
|
|
16
16
|
.conflict-btn-current { color: #22c55e; background: rgba(34, 197, 94, 0.15); }
|
|
17
17
|
.conflict-btn-incoming { color: #3b82f6; background: rgba(59, 130, 246, 0.15); }
|
|
18
18
|
.conflict-btn-both { color: #a855f7; background: rgba(168, 85, 247, 0.15); }
|
|
19
|
-
`,n.head?.appendChild(e)}M()},I=t?.split(/[\\/]/).pop()??`unknown`,L=f(I);return _?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,d.jsx)(o,{className:`size-6 animate-spin text-primary`})}):y?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full text-destructive`,children:y}):(0,d.jsxs)(`div`,{className:`h-full w-full flex flex-col`,children:[(0,d.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1.5 text-xs border-b border-border bg-muted/50 flex-shrink-0`,children:[(0,d.jsx)(`span`,{className:`font-medium`,children:I}),(0,d.jsx)(`span`,{className:`text-muted-foreground`,children:`—`}),x>0?(0,d.jsxs)(`span`,{className:`text-destructive font-medium`,children:[x,` conflict`,x===1?``:`s`,` remaining`]}):(0,d.jsx)(`span`,{className:`text-green-500 font-medium`,children:`All conflicts resolved`})]}),(0,d.jsx)(`div`,{ref:k,className:`flex-1 min-h-0`,children:m!==null&&A&&(0,d.jsx)(c,{height:A,language:L,value:m,onMount:F,theme:O,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:D?`on`:`off`,glyphMargin:!0,readOnly:!1,automaticLayout:!0,scrollBeyondLastLine:!1,minimap:{enabled:!1}}})})]})}function h(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}export{m as ConflictEditor};
|
|
19
|
+
`,n.head?.appendChild(e)}M()},I=t?.split(/[\\/]/).pop()??`unknown`,L=f(I);return _?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,d.jsx)(o,{className:`size-6 animate-spin text-primary`})}):y?(0,d.jsx)(`div`,{className:`flex items-center justify-center h-full text-destructive`,children:y}):(0,d.jsxs)(`div`,{className:`h-full w-full flex flex-col`,children:[(0,d.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1.5 text-xs border-b border-border bg-muted/50 flex-shrink-0`,children:[(0,d.jsx)(`span`,{className:`font-medium`,children:I}),(0,d.jsx)(`span`,{className:`text-muted-foreground`,children:`—`}),x>0?(0,d.jsxs)(`span`,{className:`text-destructive font-medium`,children:[x,` conflict`,x===1?``:`s`,` remaining`]}):(0,d.jsx)(`span`,{className:`text-green-500 font-medium`,children:`All conflicts resolved`})]}),(0,d.jsx)(`div`,{ref:k,className:`flex-1 min-h-0`,children:m!==null&&A&&(0,d.jsx)(c,{height:A,language:L,value:m,onMount:F,theme:O,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:D?`on`:`off`,glyphMargin:!0,readOnly:!1,automaticLayout:!0,scrollBeyondLastLine:!1,minimap:{enabled:!1}}})})]})}function h(e){return e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`)}export{m as ConflictEditor};
|
|
20
|
+
//# sourceMappingURL=conflict-editor-B6nO1gln.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-editor-B6nO1gln.js","names":[],"sources":["../../../src/web/components/editor/conflict-editor.tsx"],"sourcesContent":["import { useEffect, useState, useRef, useCallback } from \"react\";\nimport Editor, { type OnMount } from \"@monaco-editor/react\";\nimport type * as MonacoType from \"monaco-editor\";\nimport { api, projectUrl } from \"@/lib/api-client\";\nimport { useShallow } from \"zustand/react/shallow\";\nimport { useSettingsStore } from \"@/stores/settings-store\";\nimport { useMonacoTheme } from \"@/lib/use-monaco-theme\";\nimport { Loader2 } from \"lucide-react\";\n\nfunction getMonacoLanguage(filename: string): string {\n const ext = filename.split(\".\").pop()?.toLowerCase() ?? \"\";\n const map: Record<string, string> = {\n js: \"javascript\", jsx: \"javascript\",\n ts: \"typescript\", tsx: \"typescript\",\n py: \"python\", html: \"html\",\n css: \"css\", scss: \"scss\",\n json: \"json\", md: \"markdown\", mdx: \"markdown\",\n yaml: \"yaml\", yml: \"yaml\",\n sh: \"shell\", bash: \"shell\",\n go: \"go\", rs: \"rust\", java: \"java\",\n rb: \"ruby\", php: \"php\", swift: \"swift\",\n sql: \"sql\", xml: \"xml\", toml: \"toml\",\n };\n return map[ext] ?? \"plaintext\";\n}\n\ninterface ConflictRegion {\n id: number;\n startLine: number; // 1-indexed, line of <<<<<<< marker\n separatorLine: number; // line of =======\n endLine: number; // line of >>>>>>> marker\n currentContent: string;\n incomingContent: string;\n currentLabel: string;\n incomingLabel: string;\n}\n\nfunction parseConflicts(content: string): ConflictRegion[] {\n const lines = content.split(\"\\n\");\n const regions: ConflictRegion[] = [];\n let i = 0;\n let id = 0;\n\n while (i < lines.length) {\n const line = lines[i]!;\n if (line.startsWith(\"<<<<<<<\")) {\n const startLine = i;\n const currentLabel = line.substring(7).trim();\n const currentLines: string[] = [];\n i++;\n\n while (i < lines.length && !lines[i]!.startsWith(\"=======\")) {\n currentLines.push(lines[i]!);\n i++;\n }\n if (i >= lines.length) break;\n\n const separatorLine = i;\n const incomingLines: string[] = [];\n i++;\n\n while (i < lines.length && !lines[i]!.startsWith(\">>>>>>>\")) {\n incomingLines.push(lines[i]!);\n i++;\n }\n if (i >= lines.length) break;\n\n const incomingLabel = lines[i]!.substring(7).trim();\n\n regions.push({\n id: id++,\n startLine: startLine + 1,\n separatorLine: separatorLine + 1,\n endLine: i + 1,\n currentContent: currentLines.join(\"\\n\"),\n incomingContent: incomingLines.join(\"\\n\"),\n currentLabel,\n incomingLabel,\n });\n }\n i++;\n }\n return regions;\n}\n\ninterface ConflictEditorProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nexport function ConflictEditor({ metadata }: ConflictEditorProps) {\n const filePath = metadata?.filePath as string | undefined;\n const projectName = metadata?.projectName as string | undefined;\n\n const [content, setContent] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [conflictCount, setConflictCount] = useState(0);\n const editorRef = useRef<MonacoType.editor.IStandaloneCodeEditor | null>(null);\n const monacoRef = useRef<typeof MonacoType | null>(null);\n const widgetsRef = useRef<MonacoType.editor.IContentWidget[]>([]);\n const decorationsRef = useRef<MonacoType.editor.IEditorDecorationsCollection | null>(null);\n\n const { wordWrap } = useSettingsStore(useShallow((s) => ({ wordWrap: s.wordWrap })));\n const monacoTheme = useMonacoTheme();\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerHeight, setContainerHeight] = useState<number | undefined>();\n\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const ro = new ResizeObserver(([entry]) => {\n if (entry) setContainerHeight(Math.floor(entry.contentRect.height));\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, []);\n\n // Load file content\n useEffect(() => {\n if (!filePath || !projectName) return;\n setLoading(true);\n api\n .get<{ content: string }>(`${projectUrl(projectName)}/files/read?path=${encodeURIComponent(filePath)}`)\n .then((data) => {\n setContent(data.content);\n setLoading(false);\n })\n .catch((e: Error) => {\n setError(e.message || \"Failed to load file\");\n setLoading(false);\n });\n }, [filePath, projectName]);\n\n const refreshConflicts = useCallback(() => {\n const editor = editorRef.current;\n const monaco = monacoRef.current;\n if (!editor || !monaco) return;\n\n const value = editor.getModel()?.getValue() || \"\";\n const regions = parseConflicts(value);\n setConflictCount(regions.length);\n\n // Clear old widgets\n for (const w of widgetsRef.current) {\n editor.removeContentWidget(w);\n }\n widgetsRef.current = [];\n\n // Clear old decorations\n if (decorationsRef.current) {\n decorationsRef.current.clear();\n }\n\n if (regions.length === 0) return;\n\n // Apply decorations\n const decos: MonacoType.editor.IModelDeltaDecoration[] = [];\n for (const region of regions) {\n // Marker lines\n decos.push({\n range: new monaco.Range(region.startLine, 1, region.startLine, 1),\n options: { isWholeLine: true, className: \"conflict-marker-line\", glyphMarginClassName: \"conflict-glyph-current\" },\n });\n decos.push({\n range: new monaco.Range(region.separatorLine, 1, region.separatorLine, 1),\n options: { isWholeLine: true, className: \"conflict-marker-line\" },\n });\n decos.push({\n range: new monaco.Range(region.endLine, 1, region.endLine, 1),\n options: { isWholeLine: true, className: \"conflict-marker-line\", glyphMarginClassName: \"conflict-glyph-incoming\" },\n });\n // Current content (green)\n if (region.separatorLine - region.startLine > 1) {\n decos.push({\n range: new monaco.Range(region.startLine + 1, 1, region.separatorLine - 1, 1),\n options: { isWholeLine: true, className: \"conflict-current-content\" },\n });\n }\n // Incoming content (blue)\n if (region.endLine - region.separatorLine > 1) {\n decos.push({\n range: new monaco.Range(region.separatorLine + 1, 1, region.endLine - 1, 1),\n options: { isWholeLine: true, className: \"conflict-incoming-content\" },\n });\n }\n }\n\n decorationsRef.current = editor.createDecorationsCollection(decos);\n\n // Add accept widgets above each conflict\n for (const region of regions) {\n const widgetId = `conflict-widget-${region.id}`;\n\n const domNode = document.createElement(\"div\");\n domNode.className = \"conflict-actions\";\n domNode.innerHTML =\n `<span class=\"conflict-label\">Current Change (${escHtml(region.currentLabel || \"HEAD\")})</span>` +\n `<button class=\"conflict-btn conflict-btn-current\" data-action=\"current\">Accept Current</button>` +\n `<button class=\"conflict-btn conflict-btn-incoming\" data-action=\"incoming\">Accept Incoming</button>` +\n `<button class=\"conflict-btn conflict-btn-both\" data-action=\"both\">Accept Both</button>`;\n\n domNode.addEventListener(\"click\", (e) => {\n const btn = (e.target as HTMLElement).closest(\"[data-action]\");\n if (!btn) return;\n const action = btn.getAttribute(\"data-action\") as \"current\" | \"incoming\" | \"both\";\n acceptConflict(region.id, action);\n });\n\n const widget: MonacoType.editor.IContentWidget = {\n getId: () => widgetId,\n getDomNode: () => domNode,\n getPosition: () => ({\n position: { lineNumber: region.startLine, column: 1 },\n preference: [monaco.editor.ContentWidgetPositionPreference.ABOVE],\n }),\n };\n editor.addContentWidget(widget);\n widgetsRef.current.push(widget);\n }\n }, []);\n\n const acceptConflict = useCallback(\n (regionId: number, action: \"current\" | \"incoming\" | \"both\") => {\n const editor = editorRef.current;\n const monaco = monacoRef.current;\n if (!editor || !monaco) return;\n\n const model = editor.getModel();\n if (!model) return;\n\n const value = model.getValue();\n const regions = parseConflicts(value);\n const region = regions.find((r) => r.id === regionId);\n if (!region) return;\n\n let replacement: string;\n switch (action) {\n case \"current\":\n replacement = region.currentContent;\n break;\n case \"incoming\":\n replacement = region.incomingContent;\n break;\n case \"both\":\n replacement = region.currentContent + \"\\n\" + region.incomingContent;\n break;\n }\n\n const range = new monaco.Range(region.startLine, 1, region.endLine + 1, 1);\n model.pushEditOperations(\n [],\n [{ range, text: replacement + \"\\n\" }],\n () => null,\n );\n\n // Save and refresh\n saveFile(model.getValue());\n // Small delay to let the model update before refreshing decorations\n setTimeout(() => refreshConflicts(), 50);\n },\n [refreshConflicts],\n );\n\n const saveFile = useCallback(\n async (newContent: string) => {\n if (!filePath || !projectName) return;\n try {\n await api.put<{ written: boolean }>(`${projectUrl(projectName)}/files/write`, {\n path: filePath,\n content: newContent,\n });\n } catch (e) {\n console.error(\"[conflict-editor] save failed:\", e);\n }\n },\n [filePath, projectName],\n );\n\n const handleMount: OnMount = (editor, monaco) => {\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Inject conflict editor styles (idempotent)\n const doc = editor.getDomNode()?.ownerDocument ?? document;\n if (!doc.getElementById(\"conflict-editor-styles\")) {\n const styleEl = doc.createElement(\"style\");\n styleEl.id = \"conflict-editor-styles\";\n styleEl.textContent = `\n .conflict-current-content { background: rgba(34, 197, 94, 0.1) !important; }\n .conflict-incoming-content { background: rgba(59, 130, 246, 0.1) !important; }\n .conflict-marker-line { background: rgba(100, 100, 100, 0.15) !important; font-style: italic; }\n .conflict-glyph-current { background: #22c55e !important; }\n .conflict-glyph-incoming { background: #3b82f6 !important; }\n .conflict-actions { display: flex; gap: 8px; align-items: center; padding: 2px 0; font-size: 12px; font-family: system-ui; }\n .conflict-label { color: #22c55e; font-weight: 600; margin-right: 8px; }\n .conflict-btn { padding: 1px 8px; border-radius: 3px; border: none; cursor: pointer; font-size: 11px; opacity: 0.9; }\n .conflict-btn:hover { opacity: 1; }\n .conflict-btn-current { color: #22c55e; background: rgba(34, 197, 94, 0.15); }\n .conflict-btn-incoming { color: #3b82f6; background: rgba(59, 130, 246, 0.15); }\n .conflict-btn-both { color: #a855f7; background: rgba(168, 85, 247, 0.15); }\n `;\n doc.head?.appendChild(styleEl);\n }\n\n refreshConflicts();\n };\n\n const fileName = filePath?.split(/[\\\\/]/).pop() ?? \"unknown\";\n const language = getMonacoLanguage(fileName);\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <Loader2 className=\"size-6 animate-spin text-primary\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full text-destructive\">\n {error}\n </div>\n );\n }\n\n return (\n <div className=\"h-full w-full flex flex-col\">\n <div className=\"flex items-center gap-2 px-3 py-1.5 text-xs border-b border-border bg-muted/50 flex-shrink-0\">\n <span className=\"font-medium\">{fileName}</span>\n <span className=\"text-muted-foreground\">—</span>\n {conflictCount > 0 ? (\n <span className=\"text-destructive font-medium\">\n {conflictCount} conflict{conflictCount !== 1 ? \"s\" : \"\"} remaining\n </span>\n ) : (\n <span className=\"text-green-500 font-medium\">All conflicts resolved</span>\n )}\n </div>\n <div ref={containerRef} className=\"flex-1 min-h-0\">\n {content !== null && containerHeight && (\n <Editor\n height={containerHeight}\n language={language}\n value={content}\n onMount={handleMount}\n theme={monacoTheme}\n options={{\n fontSize: 13,\n fontFamily: \"Menlo, Monaco, Consolas, monospace\",\n wordWrap: wordWrap ? \"on\" : \"off\",\n glyphMargin: true,\n readOnly: false,\n automaticLayout: true,\n scrollBeyondLastLine: false,\n minimap: { enabled: false },\n }}\n />\n )}\n </div>\n </div>\n );\n}\n\nfunction escHtml(s: string): string {\n return s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\").replace(/\"/g, \""\");\n}\n"],"mappings":"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"}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{x as t}from"./vendor-markdown-0Mxgxy0L.js";var n=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),r=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),i=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),a=e=>{let t=i(e);return t.charAt(0).toUpperCase()+t.slice(1)},o={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},s=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},c=e(t()),l=(0,c.forwardRef)(({color:e=`currentColor`,size:t=24,strokeWidth:r=2,absoluteStrokeWidth:i,className:a=``,children:l,iconNode:u,...d},f)=>(0,c.createElement)(`svg`,{ref:f,...o,width:t,height:t,stroke:e,strokeWidth:i?Number(r)*24/Number(t):r,className:n(`lucide`,a),...!l&&!s(d)&&{"aria-hidden":`true`},...d},[...u.map(([e,t])=>(0,c.createElement)(e,t)),...Array.isArray(l)?l:[l]])),u=(e,t)=>{let i=(0,c.forwardRef)(({className:i,...o},s)=>(0,c.createElement)(l,{ref:s,iconNode:t,className:n(`lucide-${r(a(e))}`,`lucide-${e}`,i),...o}));return i.displayName=a(e),i};export{u as t};
|
|
1
|
+
import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{x as t}from"./vendor-markdown-0Mxgxy0L.js";var n=(...e)=>e.filter((e,t,n)=>!!e&&e.trim()!==``&&n.indexOf(e)===t).join(` `).trim(),r=e=>e.replace(/([a-z0-9])([A-Z])/g,`$1-$2`).toLowerCase(),i=e=>e.replace(/^([A-Z])|[\s-_]+(\w)/g,(e,t,n)=>n?n.toUpperCase():t.toLowerCase()),a=e=>{let t=i(e);return t.charAt(0).toUpperCase()+t.slice(1)},o={xmlns:`http://www.w3.org/2000/svg`,width:24,height:24,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:2,strokeLinecap:`round`,strokeLinejoin:`round`},s=e=>{for(let t in e)if(t.startsWith(`aria-`)||t===`role`||t===`title`)return!0;return!1},c=e(t()),l=(0,c.forwardRef)(({color:e=`currentColor`,size:t=24,strokeWidth:r=2,absoluteStrokeWidth:i,className:a=``,children:l,iconNode:u,...d},f)=>(0,c.createElement)(`svg`,{ref:f,...o,width:t,height:t,stroke:e,strokeWidth:i?Number(r)*24/Number(t):r,className:n(`lucide`,a),...!l&&!s(d)&&{"aria-hidden":`true`},...d},[...u.map(([e,t])=>(0,c.createElement)(e,t)),...Array.isArray(l)?l:[l]])),u=(e,t)=>{let i=(0,c.forwardRef)(({className:i,...o},s)=>(0,c.createElement)(l,{ref:s,iconNode:t,className:n(`lucide-${r(a(e))}`,`lucide-${e}`,i),...o}));return i.displayName=a(e),i};export{u as t};
|
|
2
|
+
//# sourceMappingURL=createLucideIcon-BjHrJDVb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createLucideIcon-BjHrJDVb.js","names":[],"sources":["../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/defaultAttributes.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/Icon.js","../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/createLucideIcon.js"],"sourcesContent":["/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\n\nexport { mergeClasses };\n//# sourceMappingURL=mergeClasses.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n\nexport { toKebabCase };\n//# sourceMappingURL=toKebabCase.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\n\nexport { toCamelCase };\n//# sourceMappingURL=toCamelCase.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { toCamelCase } from './toCamelCase.js';\n\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\n\nexport { toPascalCase };\n//# sourceMappingURL=toPascalCase.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n return false;\n};\n\nexport { hasA11yProp };\n//# sourceMappingURL=hasA11yProp.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.js';\nimport { hasA11yProp } from './shared/src/utils/hasA11yProp.js';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.js';\n\nconst Icon = forwardRef(\n ({\n color = \"currentColor\",\n size = 24,\n strokeWidth = 2,\n absoluteStrokeWidth,\n className = \"\",\n children,\n iconNode,\n ...rest\n }, ref) => createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size,\n height: size,\n stroke: color,\n strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,\n className: mergeClasses(\"lucide\", className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n )\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.js.map\n","/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.js';\nimport { toKebabCase } from './shared/src/utils/toKebabCase.js';\nimport { toPascalCase } from './shared/src/utils/toPascalCase.js';\nimport Icon from './Icon.js';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.js.map\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7],"mappings":"qGAOA,IAAM,GAAgB,GAAG,IAAY,EAAQ,QAAQ,EAAW,EAAO,IAC9D,EAAQ,GAAc,EAAU,MAAM,GAAK,IAAM,EAAM,QAAQ,EAAU,GAAK,EACrF,CAAC,KAAK,IAAI,CAAC,MAAM,CCFb,EAAe,GAAW,EAAO,QAAQ,qBAAsB,QAAQ,CAAC,aAAa,CCArF,EAAe,GAAW,EAAO,QACrC,yBACC,EAAO,EAAI,IAAO,EAAK,EAAG,aAAa,CAAG,EAAG,aAAa,CAC5D,CCDK,EAAgB,GAAW,CAC/B,IAAM,EAAY,EAAY,EAAO,CACrC,OAAO,EAAU,OAAO,EAAE,CAAC,aAAa,CAAG,EAAU,MAAM,EAAE,ECJ3D,EAAoB,CACtB,MAAO,6BACP,MAAO,GACP,OAAQ,GACR,QAAS,YACT,KAAM,OACN,OAAQ,eACR,YAAa,EACb,cAAe,QACf,eAAgB,QACjB,CCVK,EAAe,GAAU,CAC7B,IAAK,IAAM,KAAQ,EACjB,GAAI,EAAK,WAAW,QAAQ,EAAI,IAAS,QAAU,IAAS,QAC1D,MAAO,GAGX,MAAO,aCDH,GAAA,EAAA,EAAA,aACH,CACC,QAAQ,eACR,OAAO,GACP,cAAc,EACd,sBACA,YAAY,GACZ,WACA,WACA,GAAG,GACF,KAAA,EAAA,EAAA,eACD,MACA,CACE,MACA,GAAG,EACH,MAAO,EACP,OAAQ,EACR,OAAQ,EACR,YAAa,EAAsB,OAAO,EAAY,CAAG,GAAK,OAAO,EAAK,CAAG,EAC7E,UAAW,EAAa,SAAU,EAAU,CAC5C,GAAG,CAAC,GAAY,CAAC,EAAY,EAAK,EAAI,CAAE,cAAe,OAAQ,CAC/D,GAAG,EACJ,CACD,CACE,GAAG,EAAS,KAAK,CAAC,EAAK,MAAA,EAAA,EAAA,eAAyB,EAAK,EAAM,CAAC,CAC5D,GAAG,MAAM,QAAQ,EAAS,CAAG,EAAW,CAAC,EAAS,CACnD,CACF,CACF,CC3BK,GAAoB,EAAU,IAAa,CAC/C,IAAM,GAAA,EAAA,EAAA,aACH,CAAE,YAAW,GAAG,GAAS,KAAA,EAAA,EAAA,eAAsB,EAAM,CACpD,MACA,WACA,UAAW,EACT,UAAU,EAAY,EAAa,EAAS,CAAC,GAC7C,UAAU,IACV,EACD,CACD,GAAG,EACJ,CAAC,CACH,CAED,MADA,GAAU,YAAc,EAAa,EAAS,CACvC"}
|
|
@@ -3,4 +3,5 @@ import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`arrow-down`,[[`path`
|
|
|
3
3
|
`?(r.push(i),i=``,t.push(r),r=[],a=n.FIELD_START):i+=s);break;case n.QUOTED:s===`"`?a=n.QUOTE_IN_QUOTED:i+=s;break;case n.QUOTE_IN_QUOTED:s===`"`?(i+=`"`,a=n.QUOTED):s===`,`?(r.push(i),i=``,a=n.FIELD_START):s===`\r`||(s===`
|
|
4
4
|
`?(r.push(i),i=``,t.push(r),r=[],a=n.FIELD_START):(i+=s,a=n.UNQUOTED));break}}if((i||r.length>0)&&(r.push(i),t.push(r)),t.length===0)return{headers:[],rows:[]};let o=t[0],s=t.slice(1),c=o.length;for(let e=0;e<s.length;e++){let t=s[e];if(t.length<c)for(;t.length<c;)t.push(``);else t.length>c&&(s[e]=t.slice(0,c))}return{headers:o,rows:s}}function i(e,t){let n=e=>e.includes(`,`)||e.includes(`"`)||e.includes(`
|
|
5
5
|
`)?`"${e.replace(/"/g,`""`)}"`:e,r=[e.map(n).join(`,`)];for(let e of t)r.push(e.map(n).join(`,`));return r.join(`
|
|
6
|
-
`)}export{i as n,t as r,r as t};
|
|
6
|
+
`)}export{i as n,t as r,r as t};
|
|
7
|
+
//# sourceMappingURL=csv-parser-Dly5nqE1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv-parser-Dly5nqE1.js","names":[],"sources":["../../../node_modules/.bun/lucide-react@0.577.0+b1ab299f0a400331/node_modules/lucide-react/dist/esm/icons/arrow-down.js","../../../src/web/lib/csv-parser.ts"],"sourcesContent":["/**\n * @license lucide-react v0.577.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\"path\", { d: \"M12 5v14\", key: \"s699le\" }],\n [\"path\", { d: \"m19 12-7 7-7-7\", key: \"1idqje\" }]\n];\nconst ArrowDown = createLucideIcon(\"arrow-down\", __iconNode);\n\nexport { __iconNode, ArrowDown as default };\n//# sourceMappingURL=arrow-down.js.map\n","/** Simple state-machine CSV parser handling quoted fields, embedded commas, newlines */\n\nconst enum State {\n FIELD_START,\n UNQUOTED,\n QUOTED,\n QUOTE_IN_QUOTED,\n}\n\nexport interface CsvData {\n headers: string[];\n rows: string[][];\n}\n\nexport function parseCsv(content: string): CsvData {\n const rows: string[][] = [];\n let row: string[] = [];\n let field = \"\";\n let state: State = State.FIELD_START;\n\n for (let i = 0; i < content.length; i++) {\n const ch = content[i]!;\n\n switch (state) {\n case State.FIELD_START:\n if (ch === '\"') {\n state = State.QUOTED;\n } else if (ch === \",\") {\n row.push(field);\n field = \"\";\n } else if (ch === \"\\r\") {\n // skip \\r, handle \\n next\n } else if (ch === \"\\n\") {\n row.push(field);\n field = \"\";\n rows.push(row);\n row = [];\n } else {\n field += ch;\n state = State.UNQUOTED;\n }\n break;\n\n case State.UNQUOTED:\n if (ch === \",\") {\n row.push(field);\n field = \"\";\n state = State.FIELD_START;\n } else if (ch === \"\\r\") {\n // skip\n } else if (ch === \"\\n\") {\n row.push(field);\n field = \"\";\n rows.push(row);\n row = [];\n state = State.FIELD_START;\n } else {\n field += ch;\n }\n break;\n\n case State.QUOTED:\n if (ch === '\"') {\n state = State.QUOTE_IN_QUOTED;\n } else {\n field += ch;\n }\n break;\n\n case State.QUOTE_IN_QUOTED:\n if (ch === '\"') {\n // Escaped quote \"\"\n field += '\"';\n state = State.QUOTED;\n } else if (ch === \",\") {\n row.push(field);\n field = \"\";\n state = State.FIELD_START;\n } else if (ch === \"\\r\") {\n // skip\n } else if (ch === \"\\n\") {\n row.push(field);\n field = \"\";\n rows.push(row);\n row = [];\n state = State.FIELD_START;\n } else {\n // Malformed — treat closing quote as literal\n field += ch;\n state = State.UNQUOTED;\n }\n break;\n }\n }\n\n // Flush last field/row\n if (field || row.length > 0) {\n row.push(field);\n rows.push(row);\n }\n\n if (rows.length === 0) return { headers: [], rows: [] };\n\n const headers = rows[0]!;\n const dataRows = rows.slice(1);\n\n // Normalize column count — pad short rows, truncate long ones\n const colCount = headers.length;\n for (let i = 0; i < dataRows.length; i++) {\n const r = dataRows[i]!;\n if (r.length < colCount) {\n while (r.length < colCount) r.push(\"\");\n } else if (r.length > colCount) {\n dataRows[i] = r.slice(0, colCount);\n }\n }\n\n return { headers, rows: dataRows };\n}\n\nexport function serializeCsv(headers: string[], rows: string[][]): string {\n const escape = (val: string): string => {\n if (val.includes(\",\") || val.includes('\"') || val.includes(\"\\n\")) {\n return `\"${val.replace(/\"/g, '\"\"')}\"`;\n }\n return val;\n };\n\n const lines = [headers.map(escape).join(\",\")];\n for (const row of rows) {\n lines.push(row.map(escape).join(\",\"));\n }\n return lines.join(\"\\n\");\n}\n"],"x_google_ignoreList":[0],"mappings":"mDAaA,IAAM,EAAY,EAAiB,aAJhB,CACjB,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,SAAU,CAAC,CAC1C,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,SAAU,CAAC,CACjD,CAC2D,CCXjD,EAAX,SAAA,EAAA,OACE,GAAA,EAAA,YAAA,GAAA,cACA,EAAA,EAAA,SAAA,GAAA,WACA,EAAA,EAAA,OAAA,GAAA,SACA,EAAA,EAAA,gBAAA,GAAA,qBAJS,GAAA,EAAA,CAKV,CAOD,SAAgB,EAAS,EAA0B,CACjD,IAAM,EAAmB,EAAE,CACvB,EAAgB,EAAE,CAClB,EAAQ,GACR,EAAe,EAAM,YAEzB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,OAAQ,IAAK,CACvC,IAAM,EAAK,EAAQ,GAEnB,OAAQ,EAAR,CACE,KAAK,EAAM,YACL,IAAO,IACT,EAAQ,EAAM,OACL,IAAO,KAChB,EAAI,KAAK,EAAM,CACf,EAAQ,IACC,IAAO,OAEP,IAAO;GAChB,EAAI,KAAK,EAAM,CACf,EAAQ,GACR,EAAK,KAAK,EAAI,CACd,EAAM,EAAE,GAER,GAAS,EACT,EAAQ,EAAM,WAEhB,MAEF,KAAK,EAAM,SACL,IAAO,KACT,EAAI,KAAK,EAAM,CACf,EAAQ,GACR,EAAQ,EAAM,aACL,IAAO,OAEP,IAAO;GAChB,EAAI,KAAK,EAAM,CACf,EAAQ,GACR,EAAK,KAAK,EAAI,CACd,EAAM,EAAE,CACR,EAAQ,EAAM,aAEd,GAAS,GAEX,MAEF,KAAK,EAAM,OACL,IAAO,IACT,EAAQ,EAAM,gBAEd,GAAS,EAEX,MAEF,KAAK,EAAM,gBACL,IAAO,KAET,GAAS,IACT,EAAQ,EAAM,QACL,IAAO,KAChB,EAAI,KAAK,EAAM,CACf,EAAQ,GACR,EAAQ,EAAM,aACL,IAAO,OAEP,IAAO;GAChB,EAAI,KAAK,EAAM,CACf,EAAQ,GACR,EAAK,KAAK,EAAI,CACd,EAAM,EAAE,CACR,EAAQ,EAAM,cAGd,GAAS,EACT,EAAQ,EAAM,WAEhB,OAUN,IALI,GAAS,EAAI,OAAS,KACxB,EAAI,KAAK,EAAM,CACf,EAAK,KAAK,EAAI,EAGZ,EAAK,SAAW,EAAG,MAAO,CAAE,QAAS,EAAE,CAAE,KAAM,EAAE,CAAE,CAEvD,IAAM,EAAU,EAAK,GACf,EAAW,EAAK,MAAM,EAAE,CAGxB,EAAW,EAAQ,OACzB,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IAAK,CACxC,IAAM,EAAI,EAAS,GACnB,GAAI,EAAE,OAAS,EACb,KAAO,EAAE,OAAS,GAAU,EAAE,KAAK,GAAG,MAC7B,EAAE,OAAS,IACpB,EAAS,GAAK,EAAE,MAAM,EAAG,EAAS,EAItC,MAAO,CAAE,UAAS,KAAM,EAAU,CAGpC,SAAgB,EAAa,EAAmB,EAA0B,CACxE,IAAM,EAAU,GACV,EAAI,SAAS,IAAI,EAAI,EAAI,SAAS,IAAI,EAAI,EAAI,SAAS;EAAK,CACvD,IAAI,EAAI,QAAQ,KAAM,KAAK,CAAC,GAE9B,EAGH,EAAQ,CAAC,EAAQ,IAAI,EAAO,CAAC,KAAK,IAAI,CAAC,CAC7C,IAAK,IAAM,KAAO,EAChB,EAAM,KAAK,EAAI,IAAI,EAAO,CAAC,KAAK,IAAI,CAAC,CAEvC,OAAO,EAAM,KAAK;EAAK"}
|
|
@@ -2,4 +2,5 @@ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./v
|
|
|
2
2
|
font-size: .6rem;
|
|
3
3
|
font-weight: bold;
|
|
4
4
|
color: hsl(${Math.max(0,Math.min(120-120*r,120))}deg 100% 31%);`,n?.key)}return n?.onChange&&!(a&&n.skipInitialOnChange)&&n.onChange(i),a=!1,i}return o.updateDeps=e=>{r=e},o}function h(e,t){if(e===void 0)throw Error(`Unexpected undefined${t?`: ${t}`:``}`);return e}var g=(e,t)=>Math.abs(e-t)<1.01,_=(e,t,n)=>{let r;return function(...i){e.clearTimeout(r),r=e.setTimeout(()=>t.apply(this,i),n)}},v=e=>{let{offsetWidth:t,offsetHeight:n}=e;return{width:t,height:n}},y=e=>e,b=e=>{let t=Math.max(e.startIndex-e.overscan,0),n=Math.min(e.endIndex+e.overscan,e.count-1),r=[];for(let e=t;e<=n;e++)r.push(e);return r},x=(e,t)=>{let n=e.scrollElement;if(!n)return;let r=e.targetWindow;if(!r)return;let i=e=>{let{width:n,height:r}=e;t({width:Math.round(n),height:Math.round(r)})};if(i(v(n)),!r.ResizeObserver)return()=>{};let a=new r.ResizeObserver(t=>{let r=()=>{let e=t[0];if(e?.borderBoxSize){let t=e.borderBoxSize[0];if(t){i({width:t.inlineSize,height:t.blockSize});return}}i(v(n))};e.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(r):r()});return a.observe(n,{box:`border-box`}),()=>{a.unobserve(n)}},S={passive:!0},C=typeof window>`u`?!0:`onscrollend`in window,w=(e,t)=>{let n=e.scrollElement;if(!n)return;let r=e.targetWindow;if(!r)return;let i=0,a=e.options.useScrollendEvent&&C?()=>void 0:_(r,()=>{t(i,!1)},e.options.isScrollingResetDelay),o=r=>()=>{let{horizontal:o,isRtl:s}=e.options;i=o?n.scrollLeft*(s&&-1||1):n.scrollTop,a(),t(i,r)},s=o(!0),c=o(!1);n.addEventListener(`scroll`,s,S);let l=e.options.useScrollendEvent&&C;return l&&n.addEventListener(`scrollend`,c,S),()=>{n.removeEventListener(`scroll`,s),l&&n.removeEventListener(`scrollend`,c)}},T=(e,t,n)=>{if(t?.borderBoxSize){let e=t.borderBoxSize[0];if(e)return Math.round(e[n.options.horizontal?`inlineSize`:`blockSize`])}return e[n.options.horizontal?`offsetWidth`:`offsetHeight`]},E=(e,{adjustments:t=0,behavior:n},r)=>{var i,a;let o=e+t;(a=(i=r.scrollElement)?.scrollTo)==null||a.call(i,{[r.options.horizontal?`left`:`top`]:o,behavior:n})},D=class{constructor(e){this.unsubs=[],this.scrollElement=null,this.targetWindow=null,this.isScrolling=!1,this.scrollState=null,this.measurementsCache=[],this.itemSizeCache=new Map,this.laneAssignments=new Map,this.pendingMeasuredCacheIndexes=[],this.prevLanes=void 0,this.lanesChangedFlag=!1,this.lanesSettling=!1,this.scrollRect=null,this.scrollOffset=null,this.scrollDirection=null,this.scrollAdjustments=0,this.elementsCache=new Map,this.now=()=>{var e;return((e=this.targetWindow?.performance)?.now)?.call(e)??Date.now()},this.observer=(()=>{let e=null,t=()=>e||(!this.targetWindow||!this.targetWindow.ResizeObserver?null:e=new this.targetWindow.ResizeObserver(e=>{e.forEach(e=>{let t=()=>{let t=e.target,n=this.indexFromElement(t);if(!t.isConnected){this.observer.unobserve(t);return}this.shouldMeasureDuringScroll(n)&&this.resizeItem(n,this.options.measureElement(t,e,this))};this.options.useAnimationFrameWithResizeObserver?requestAnimationFrame(t):t()})}));return{disconnect:()=>{var n;(n=t())==null||n.disconnect(),e=null},observe:e=>t()?.observe(e,{box:`border-box`}),unobserve:e=>t()?.unobserve(e)}})(),this.range=null,this.setOptions=e=>{Object.entries(e).forEach(([t,n])=>{n===void 0&&delete e[t]}),this.options={debug:!1,initialOffset:0,overscan:1,paddingStart:0,paddingEnd:0,scrollPaddingStart:0,scrollPaddingEnd:0,horizontal:!1,getItemKey:y,rangeExtractor:b,onChange:()=>{},measureElement:T,initialRect:{width:0,height:0},scrollMargin:0,gap:0,indexAttribute:`data-index`,initialMeasurementsCache:[],lanes:1,isScrollingResetDelay:150,enabled:!0,isRtl:!1,useScrollendEvent:!1,useAnimationFrameWithResizeObserver:!1,...e}},this.notify=e=>{var t,n;(n=(t=this.options).onChange)==null||n.call(t,this,e)},this.maybeNotify=m(()=>(this.calculateRange(),[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]),e=>{this.notify(e)},{key:!1,debug:()=>this.options.debug,initialDeps:[this.isScrolling,this.range?this.range.startIndex:null,this.range?this.range.endIndex:null]}),this.cleanup=()=>{this.unsubs.filter(Boolean).forEach(e=>e()),this.unsubs=[],this.observer.disconnect(),this.rafId!=null&&this.targetWindow&&(this.targetWindow.cancelAnimationFrame(this.rafId),this.rafId=null),this.scrollState=null,this.scrollElement=null,this.targetWindow=null},this._didMount=()=>()=>{this.cleanup()},this._willUpdate=()=>{let e=this.options.enabled?this.options.getScrollElement():null;if(this.scrollElement!==e){if(this.cleanup(),!e){this.maybeNotify();return}this.scrollElement=e,this.scrollElement&&`ownerDocument`in this.scrollElement?this.targetWindow=this.scrollElement.ownerDocument.defaultView:this.targetWindow=this.scrollElement?.window??null,this.elementsCache.forEach(e=>{this.observer.observe(e)}),this.unsubs.push(this.options.observeElementRect(this,e=>{this.scrollRect=e,this.maybeNotify()})),this.unsubs.push(this.options.observeElementOffset(this,(e,t)=>{this.scrollAdjustments=0,this.scrollDirection=t?this.getScrollOffset()<e?`forward`:`backward`:null,this.scrollOffset=e,this.isScrolling=t,this.scrollState&&this.scheduleScrollReconcile(),this.maybeNotify()})),this._scrollToOffset(this.getScrollOffset(),{adjustments:void 0,behavior:void 0})}},this.rafId=null,this.getSize=()=>this.options.enabled?(this.scrollRect=this.scrollRect??this.options.initialRect,this.scrollRect[this.options.horizontal?`width`:`height`]):(this.scrollRect=null,0),this.getScrollOffset=()=>this.options.enabled?(this.scrollOffset=this.scrollOffset??(typeof this.options.initialOffset==`function`?this.options.initialOffset():this.options.initialOffset),this.scrollOffset):(this.scrollOffset=null,0),this.getFurthestMeasurement=(e,t)=>{let n=new Map,r=new Map;for(let i=t-1;i>=0;i--){let t=e[i];if(n.has(t.lane))continue;let a=r.get(t.lane);if(a==null||t.end>a.end?r.set(t.lane,t):t.end<a.end&&n.set(t.lane,!0),n.size===this.options.lanes)break}return r.size===this.options.lanes?Array.from(r.values()).sort((e,t)=>e.end===t.end?e.index-t.index:e.end-t.end)[0]:void 0},this.getMeasurementOptions=m(()=>[this.options.count,this.options.paddingStart,this.options.scrollMargin,this.options.getItemKey,this.options.enabled,this.options.lanes],(e,t,n,r,i,a)=>(this.prevLanes!==void 0&&this.prevLanes!==a&&(this.lanesChangedFlag=!0),this.prevLanes=a,this.pendingMeasuredCacheIndexes=[],{count:e,paddingStart:t,scrollMargin:n,getItemKey:r,enabled:i,lanes:a}),{key:!1}),this.getMeasurements=m(()=>[this.getMeasurementOptions(),this.itemSizeCache],({count:e,paddingStart:t,scrollMargin:n,getItemKey:r,enabled:i,lanes:a},o)=>{if(!i)return this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),[];if(this.laneAssignments.size>e)for(let t of this.laneAssignments.keys())t>=e&&this.laneAssignments.delete(t);this.lanesChangedFlag&&(this.lanesChangedFlag=!1,this.lanesSettling=!0,this.measurementsCache=[],this.itemSizeCache.clear(),this.laneAssignments.clear(),this.pendingMeasuredCacheIndexes=[]),this.measurementsCache.length===0&&!this.lanesSettling&&(this.measurementsCache=this.options.initialMeasurementsCache,this.measurementsCache.forEach(e=>{this.itemSizeCache.set(e.key,e.size)}));let s=this.lanesSettling?0:this.pendingMeasuredCacheIndexes.length>0?Math.min(...this.pendingMeasuredCacheIndexes):0;this.pendingMeasuredCacheIndexes=[],this.lanesSettling&&this.measurementsCache.length===e&&(this.lanesSettling=!1);let c=this.measurementsCache.slice(0,s),l=Array(a).fill(void 0);for(let e=0;e<s;e++){let t=c[e];t&&(l[t.lane]=e)}for(let i=s;i<e;i++){let e=r(i),a=this.laneAssignments.get(i),s,u;if(a!==void 0&&this.options.lanes>1){s=a;let e=l[s],r=e===void 0?void 0:c[e];u=r?r.end+this.options.gap:t+n}else{let e=this.options.lanes===1?c[i-1]:this.getFurthestMeasurement(c,i);u=e?e.end+this.options.gap:t+n,s=e?e.lane:i%this.options.lanes,this.options.lanes>1&&this.laneAssignments.set(i,s)}let d=o.get(e),f=typeof d==`number`?d:this.options.estimateSize(i),p=u+f;c[i]={index:i,start:u,size:f,end:p,key:e,lane:s},l[s]=i}return this.measurementsCache=c,c},{key:!1,debug:()=>this.options.debug}),this.calculateRange=m(()=>[this.getMeasurements(),this.getSize(),this.getScrollOffset(),this.options.lanes],(e,t,n,r)=>this.range=e.length>0&&t>0?k({measurements:e,outerSize:t,scrollOffset:n,lanes:r}):null,{key:!1,debug:()=>this.options.debug}),this.getVirtualIndexes=m(()=>{let e=null,t=null,n=this.calculateRange();return n&&(e=n.startIndex,t=n.endIndex),this.maybeNotify.updateDeps([this.isScrolling,e,t]),[this.options.rangeExtractor,this.options.overscan,this.options.count,e,t]},(e,t,n,r,i)=>r===null||i===null?[]:e({startIndex:r,endIndex:i,overscan:t,count:n}),{key:!1,debug:()=>this.options.debug}),this.indexFromElement=e=>{let t=this.options.indexAttribute,n=e.getAttribute(t);return n?parseInt(n,10):(console.warn(`Missing attribute name '${t}={index}' on measured element.`),-1)},this.shouldMeasureDuringScroll=e=>{if(!this.scrollState||this.scrollState.behavior!==`smooth`)return!0;let t=this.scrollState.index??this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)?.index;if(t!==void 0&&this.range){let n=Math.max(this.options.overscan,Math.ceil((this.range.endIndex-this.range.startIndex)/2)),r=Math.max(0,t-n),i=Math.min(this.options.count-1,t+n);return e>=r&&e<=i}return!0},this.measureElement=e=>{if(!e){this.elementsCache.forEach((e,t)=>{e.isConnected||(this.observer.unobserve(e),this.elementsCache.delete(t))});return}let t=this.indexFromElement(e),n=this.options.getItemKey(t),r=this.elementsCache.get(n);r!==e&&(r&&this.observer.unobserve(r),this.observer.observe(e),this.elementsCache.set(n,e)),(!this.isScrolling||this.scrollState)&&this.shouldMeasureDuringScroll(t)&&this.resizeItem(t,this.options.measureElement(e,void 0,this))},this.resizeItem=(e,t)=>{let n=this.measurementsCache[e];if(!n)return;let r=t-(this.itemSizeCache.get(n.key)??n.size);r!==0&&(this.scrollState?.behavior!==`smooth`&&(this.shouldAdjustScrollPositionOnItemSizeChange===void 0?n.start<this.getScrollOffset()+this.scrollAdjustments:this.shouldAdjustScrollPositionOnItemSizeChange(n,r,this))&&this._scrollToOffset(this.getScrollOffset(),{adjustments:this.scrollAdjustments+=r,behavior:void 0}),this.pendingMeasuredCacheIndexes.push(n.index),this.itemSizeCache=new Map(this.itemSizeCache.set(n.key,t)),this.notify(!1))},this.getVirtualItems=m(()=>[this.getVirtualIndexes(),this.getMeasurements()],(e,t)=>{let n=[];for(let r=0,i=e.length;r<i;r++){let i=t[e[r]];n.push(i)}return n},{key:!1,debug:()=>this.options.debug}),this.getVirtualItemForOffset=e=>{let t=this.getMeasurements();if(t.length!==0)return h(t[O(0,t.length-1,e=>h(t[e]).start,e)])},this.getMaxScrollOffset=()=>{if(!this.scrollElement)return 0;if(`scrollHeight`in this.scrollElement)return this.options.horizontal?this.scrollElement.scrollWidth-this.scrollElement.clientWidth:this.scrollElement.scrollHeight-this.scrollElement.clientHeight;{let e=this.scrollElement.document.documentElement;return this.options.horizontal?e.scrollWidth-this.scrollElement.innerWidth:e.scrollHeight-this.scrollElement.innerHeight}},this.getOffsetForAlignment=(e,t,n=0)=>{if(!this.scrollElement)return 0;let r=this.getSize(),i=this.getScrollOffset();t===`auto`&&(t=e>=i+r?`end`:`start`),t===`center`?e+=(n-r)/2:t===`end`&&(e-=r);let a=this.getMaxScrollOffset();return Math.max(Math.min(a,e),0)},this.getOffsetForIndex=(e,t=`auto`)=>{e=Math.max(0,Math.min(e,this.options.count-1));let n=this.getSize(),r=this.getScrollOffset(),i=this.measurementsCache[e];if(!i)return;if(t===`auto`)if(i.end>=r+n-this.options.scrollPaddingEnd)t=`end`;else if(i.start<=r+this.options.scrollPaddingStart)t=`start`;else return[r,t];if(t===`end`&&e===this.options.count-1)return[this.getMaxScrollOffset(),t];let a=t===`end`?i.end+this.options.scrollPaddingEnd:i.start-this.options.scrollPaddingStart;return[this.getOffsetForAlignment(a,t,i.size),t]},this.scrollToOffset=(e,{align:t=`start`,behavior:n=`auto`}={})=>{let r=this.getOffsetForAlignment(e,t);this.scrollState={index:null,align:t,behavior:n,startedAt:this.now(),lastTargetOffset:r,stableFrames:0},this._scrollToOffset(r,{adjustments:void 0,behavior:n}),this.scheduleScrollReconcile()},this.scrollToIndex=(e,{align:t=`auto`,behavior:n=`auto`}={})=>{e=Math.max(0,Math.min(e,this.options.count-1));let r=this.getOffsetForIndex(e,t);if(!r)return;let[i,a]=r,o=this.now();this.scrollState={index:e,align:a,behavior:n,startedAt:o,lastTargetOffset:i,stableFrames:0},this._scrollToOffset(i,{adjustments:void 0,behavior:n}),this.scheduleScrollReconcile()},this.scrollBy=(e,{behavior:t=`auto`}={})=>{let n=this.getScrollOffset()+e;this.scrollState={index:null,align:`start`,behavior:t,startedAt:this.now(),lastTargetOffset:n,stableFrames:0},this._scrollToOffset(n,{adjustments:void 0,behavior:t}),this.scheduleScrollReconcile()},this.getTotalSize=()=>{let e=this.getMeasurements(),t;if(e.length===0)t=this.options.paddingStart;else if(this.options.lanes===1)t=e[e.length-1]?.end??0;else{let n=Array(this.options.lanes).fill(null),r=e.length-1;for(;r>=0&&n.some(e=>e===null);){let t=e[r];n[t.lane]===null&&(n[t.lane]=t.end),r--}t=Math.max(...n.filter(e=>e!==null))}return Math.max(t-this.options.scrollMargin+this.options.paddingEnd,0)},this._scrollToOffset=(e,{adjustments:t,behavior:n})=>{this.options.scrollToFn(e,{behavior:n,adjustments:t},this)},this.measure=()=>{this.itemSizeCache=new Map,this.laneAssignments=new Map,this.notify(!1)},this.setOptions(e)}scheduleScrollReconcile(){if(!this.targetWindow){this.scrollState=null;return}this.rafId??=this.targetWindow.requestAnimationFrame(()=>{this.rafId=null,this.reconcileScroll()})}reconcileScroll(){if(!this.scrollState||!this.scrollElement)return;if(this.now()-this.scrollState.startedAt>5e3){this.scrollState=null;return}let e=this.scrollState.index==null?void 0:this.getOffsetForIndex(this.scrollState.index,this.scrollState.align),t=e?e[0]:this.scrollState.lastTargetOffset,n=t!==this.scrollState.lastTargetOffset;if(!n&&g(t,this.getScrollOffset())){if(this.scrollState.stableFrames++,this.scrollState.stableFrames>=1){this.scrollState=null;return}}else this.scrollState.stableFrames=0,n&&(this.scrollState.lastTargetOffset=t,this.scrollState.behavior=`auto`,this._scrollToOffset(t,{adjustments:void 0,behavior:`auto`}));this.scheduleScrollReconcile()}},O=(e,t,n,r)=>{for(;e<=t;){let i=(e+t)/2|0,a=n(i);if(a<r)e=i+1;else if(a>r)t=i-1;else return i}return e>0?e-1:0};function k({measurements:e,outerSize:t,scrollOffset:n,lanes:r}){let i=e.length-1,a=t=>e[t].start;if(e.length<=r)return{startIndex:0,endIndex:i};let o=O(0,i,a,n),s=o;if(r===1)for(;s<i&&e[s].end<n+t;)s++;else if(r>1){let a=Array(r).fill(0);for(;s<i&&a.some(e=>e<n+t);){let t=e[s];a[t.lane]=t.end,s++}let c=Array(r).fill(n+t);for(;o>=0&&c.some(e=>e>=n);){let t=e[o];c[t.lane]=t.start,o--}o=Math.max(0,o-o%r),s=Math.min(i,s+(r-1-s%r))}return{startIndex:o,endIndex:s}}var A=typeof document<`u`?p.useLayoutEffect:p.useEffect;function j({useFlushSync:e=!0,...t}){let n=p.useReducer(()=>({}),{})[1],r={...t,onChange:(r,i)=>{var a;e&&i?(0,f.flushSync)(n):n(),(a=t.onChange)==null||a.call(t,r,i)}},[i]=p.useState(()=>new D(r));return i.setOptions(r),A(()=>i._didMount(),[]),A(()=>i._willUpdate()),i}function M(e){return j({observeElementRect:x,observeElementOffset:w,scrollToFn:E,...e})}var N=t();function P({content:e,onContentChange:t,wordWrap:n}){let r=(0,p.useMemo)(()=>o(e),[e]),[f,m]=(0,p.useState)(()=>r.rows),[h,g]=(0,p.useState)([]),_=(0,p.useRef)(null),v=(0,p.useRef)(!1);(0,p.useEffect)(()=>{if(v.current){v.current=!1;return}m(r.rows)},[r.rows]);let y=r.headers,b=(0,p.useCallback)((e,n,r)=>{m(a=>{let o=a.map((t,n)=>n===e?[...t]:t);return o[e][n]=r,v.current=!0,t(i(y,o)),o})},[y,t]),x=l({data:f,columns:(0,p.useMemo)(()=>y.map((e,t)=>({id:`col-${t}`,header:e||`Column ${t+1}`,accessorFn:e=>e[t]??``,cell:({row:e,getValue:r})=>(0,N.jsx)(F,{value:r(),onSave:n=>b(e.index,t,n),wordWrap:n}),size:150,minSize:80})),[y,b,n]),state:{sorting:h},onSortingChange:g,getCoreRowModel:u(),getSortedRowModel:c(),enableColumnResizing:!0,columnResizeMode:`onChange`}),{rows:S}=x.getRowModel(),C=M({count:S.length,getScrollElement:()=>_.current,estimateSize:()=>32,overscan:20});return y.length===0?(0,N.jsx)(`div`,{className:`flex items-center justify-center h-full text-muted-foreground text-sm`,children:`Empty CSV file`}):(0,N.jsx)(`div`,{ref:_,className:`flex-1 overflow-auto`,children:(0,N.jsxs)(`table`,{className:`w-full text-xs font-mono border-collapse`,children:[(0,N.jsx)(`thead`,{className:`sticky top-0 bg-background z-10 border-b border-border block`,children:x.getHeaderGroups().map(e=>(0,N.jsx)(`tr`,{className:`flex w-full`,children:e.headers.map(e=>(0,N.jsxs)(`th`,{className:`relative text-left px-2 py-1.5 font-medium text-muted-foreground select-none cursor-pointer hover:bg-muted/50 border-r border-border last:border-r-0`,style:{width:e.getSize(),minWidth:e.getSize()},onClick:e.column.getToggleSortingHandler(),children:[(0,N.jsxs)(`div`,{className:`flex items-center gap-1`,children:[(0,N.jsx)(`span`,{className:`truncate`,children:d(e.column.columnDef.header,e.getContext())}),e.column.getIsSorted()===`asc`&&(0,N.jsx)(s,{className:`size-3 shrink-0`}),e.column.getIsSorted()===`desc`&&(0,N.jsx)(a,{className:`size-3 shrink-0`})]}),(0,N.jsx)(`div`,{onMouseDown:e.getResizeHandler(),onTouchStart:e.getResizeHandler(),onClick:e=>e.stopPropagation(),className:`absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary/50 active:bg-primary`})]},e.id))},e.id))}),(0,N.jsx)(`tbody`,{style:{height:C.getTotalSize(),position:`relative`,display:`block`},children:C.getVirtualItems().map(e=>{let t=S[e.index];return(0,N.jsx)(`tr`,{"data-index":e.index,ref:e=>C.measureElement(e),style:{position:`absolute`,top:0,left:0,width:`100%`,transform:`translateY(${e.start}px)`,display:`flex`},children:t.getVisibleCells().map(e=>(0,N.jsx)(`td`,{className:`px-2 py-1 border-b border-border/50 border-r border-r-border/30 last:border-r-0 ${n?`whitespace-pre-wrap break-words`:`truncate`}`,style:{width:e.column.getSize(),minWidth:e.column.getSize()},children:d(e.column.columnDef.cell,e.getContext())},e.id))},t.id)})})]})})}function F({value:e,onSave:t,wordWrap:n}){let[r,i]=(0,p.useState)(!1),[a,o]=(0,p.useState)(e),s=(0,p.useRef)(null),c=(0,p.useCallback)(e=>{e&&(e.style.height=`auto`,e.style.height=`${e.scrollHeight}px`)},[]);return(0,p.useEffect)(()=>{r&&s.current&&(s.current.focus(),c(s.current))},[r,c]),r?(0,N.jsx)(`textarea`,{ref:s,className:`w-full bg-transparent outline-none border border-primary/50 rounded text-xs font-mono resize-none p-0.5`,style:{minHeight:a.includes(`
|
|
5
|
-
`)?48:20},rows:1,value:a,onChange:e=>{o(e.target.value),c(e.target)},onBlur:()=>{i(!1),a!==e&&t(a)},onKeyDown:n=>{n.key===`Enter`&&!n.shiftKey?(n.preventDefault(),i(!1),a!==e&&t(a)):n.key===`Escape`&&(i(!1),o(e))}}):(0,N.jsx)(`span`,{className:`block cursor-text ${n?`whitespace-pre-wrap break-words`:`truncate`}`,onClick:()=>{o(e),i(!0)},children:e||`\xA0`})}export{P as CsvPreview};
|
|
5
|
+
`)?48:20},rows:1,value:a,onChange:e=>{o(e.target.value),c(e.target)},onBlur:()=>{i(!1),a!==e&&t(a)},onKeyDown:n=>{n.key===`Enter`&&!n.shiftKey?(n.preventDefault(),i(!1),a!==e&&t(a)):n.key===`Escape`&&(i(!1),o(e))}}):(0,N.jsx)(`span`,{className:`block cursor-text ${n?`whitespace-pre-wrap break-words`:`truncate`}`,onClick:()=>{o(e),i(!0)},children:e||`\xA0`})}export{P as CsvPreview};
|
|
6
|
+
//# sourceMappingURL=csv-preview-CsqFmPzb.js.map
|