@hienlh/ppm 0.11.4 → 0.11.6
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 +12 -0
- package/dist/web/assets/{chat-tab-BNOBNzph.js → chat-tab-2znvnxzD.js} +3 -3
- package/dist/web/assets/code-editor-CUyFYiwJ.js +8 -0
- package/dist/web/assets/{conflict-editor-BGoXs-1S.js → conflict-editor-DXnqiS4c.js} +1 -1
- package/dist/web/assets/{database-viewer-B9H3s0Xv.js → database-viewer-DK4PfwVJ.js} +1 -1
- package/dist/web/assets/{diff-viewer-CwL9hvFQ.js → diff-viewer-Cy4oX3mb.js} +1 -1
- package/dist/web/assets/{extension-webview-dVgax0Uo.js → extension-webview-DIVrmdfu.js} +1 -1
- package/dist/web/assets/{index--W7nYakj.js → index-oqP6ldWs.js} +3 -3
- package/dist/web/assets/{markdown-renderer-BLksGuhd.js → markdown-renderer-BLhcNL42.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-CezzkDTK.js → port-forwarding-tab-iZost0RL.js} +1 -1
- package/dist/web/assets/{postgres-viewer-DkZHOBg1.js → postgres-viewer-CabnbH06.js} +1 -1
- package/dist/web/assets/{settings-tab-plcYxjwZ.js → settings-tab-D2th4Hfj.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-BmKCNAih.js → sqlite-viewer-cGe2bGlF.js} +1 -1
- package/dist/web/assets/{terminal-tab-B0GxiD3-.js → terminal-tab-CPAMJoNK.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/server/ws/chat.ts +10 -0
- package/src/services/file-watcher.service.ts +72 -0
- package/src/web/components/editor/code-editor.tsx +23 -0
- package/src/web/components/explorer/file-tree.tsx +19 -7
- package/src/web/hooks/use-chat.ts +20 -0
- package/dist/web/assets/code-editor--pNv4j1V.js +0 -8
|
@@ -240,6 +240,29 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
|
|
|
240
240
|
return () => { if (saveTimerRef.current) clearTimeout(saveTimerRef.current); };
|
|
241
241
|
}, [filePath, projectName, isImage, isPdf, isExternalFile, isUntitled]);
|
|
242
242
|
|
|
243
|
+
// Real-time reload: listen for file:changed WS events, re-fetch if editor is clean
|
|
244
|
+
const unsavedRef = useRef(unsaved);
|
|
245
|
+
unsavedRef.current = unsaved;
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
if (!filePath || !projectName || inlineContent != null || isUntitled) return;
|
|
248
|
+
const handler = (e: Event) => {
|
|
249
|
+
const detail = (e as CustomEvent).detail;
|
|
250
|
+
if (detail.projectName !== projectName || detail.path !== filePath) return;
|
|
251
|
+
if (unsavedRef.current) return; // don't overwrite unsaved changes
|
|
252
|
+
const readUrl = isExternalFile
|
|
253
|
+
? `/api/fs/read?path=${encodeURIComponent(filePath)}`
|
|
254
|
+
: `${projectUrl(projectName)}/files/read?path=${encodeURIComponent(filePath)}`;
|
|
255
|
+
api.get<{ content: string; encoding?: string }>(readUrl).then((data) => {
|
|
256
|
+
if (data.content === latestContentRef.current) return; // skip if unchanged (e.g. self-save)
|
|
257
|
+
setContent(data.content);
|
|
258
|
+
latestContentRef.current = data.content;
|
|
259
|
+
if (data.encoding) setEncoding(data.encoding);
|
|
260
|
+
}).catch(() => {});
|
|
261
|
+
};
|
|
262
|
+
window.addEventListener("file:changed", handler);
|
|
263
|
+
return () => window.removeEventListener("file:changed", handler);
|
|
264
|
+
}, [filePath, projectName, isExternalFile, inlineContent, isUntitled]);
|
|
265
|
+
|
|
243
266
|
// Update tab title unsaved indicator
|
|
244
267
|
useEffect(() => {
|
|
245
268
|
if (!ownTab) return;
|
|
@@ -256,7 +256,7 @@ interface FileTreeProps {
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
259
|
-
const { tree, loading, error, fetchTree, reset, selectedFiles, clearSelection } = useFileStore(useShallow((s) => ({ tree: s.tree, loading: s.loading, error: s.error, fetchTree: s.fetchTree, reset: s.reset, selectedFiles: s.selectedFiles, clearSelection: s.clearSelection })));
|
|
259
|
+
const { tree, loading, error, fetchTree, reset, selectedFiles, clearSelection, setExpanded } = useFileStore(useShallow((s) => ({ tree: s.tree, loading: s.loading, error: s.error, fetchTree: s.fetchTree, reset: s.reset, selectedFiles: s.selectedFiles, clearSelection: s.clearSelection, setExpanded: s.setExpanded })));
|
|
260
260
|
const activeProject = useProjectStore((s) => s.activeProject);
|
|
261
261
|
const openTab = useTabStore((s) => s.openTab);
|
|
262
262
|
const [actionState, setActionState] = useState<{
|
|
@@ -277,12 +277,23 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
277
277
|
}
|
|
278
278
|
}, [activeProject?.name]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
279
279
|
|
|
280
|
-
// Auto-refresh file tree
|
|
281
|
-
// TODO: Replace with fs.watch + WebSocket push for real-time sync without needing window focus
|
|
280
|
+
// Auto-refresh file tree on window focus and real-time file changes via WebSocket
|
|
282
281
|
useEffect(() => {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
if (!activeProject) return;
|
|
283
|
+
const refresh = () => fetchTree(activeProject.name);
|
|
284
|
+
let debounceTimer: ReturnType<typeof setTimeout>;
|
|
285
|
+
const debouncedRefresh = () => { clearTimeout(debounceTimer); debounceTimer = setTimeout(refresh, 300); };
|
|
286
|
+
const handleFileChanged = (e: Event) => {
|
|
287
|
+
const detail = (e as CustomEvent).detail;
|
|
288
|
+
if (detail.projectName === activeProject.name) debouncedRefresh();
|
|
289
|
+
};
|
|
290
|
+
window.addEventListener("focus", refresh);
|
|
291
|
+
window.addEventListener("file:changed", handleFileChanged);
|
|
292
|
+
return () => {
|
|
293
|
+
clearTimeout(debounceTimer);
|
|
294
|
+
window.removeEventListener("focus", refresh);
|
|
295
|
+
window.removeEventListener("file:changed", handleFileChanged);
|
|
296
|
+
};
|
|
286
297
|
}, [activeProject, fetchTree]);
|
|
287
298
|
|
|
288
299
|
const uploadFiles = useCallback(async (targetDir: string, files: FileList) => {
|
|
@@ -304,10 +315,11 @@ export function FileTree({ onFileOpen }: FileTreeProps = {}) {
|
|
|
304
315
|
console.error("Upload failed:", json.error);
|
|
305
316
|
}
|
|
306
317
|
loadTree();
|
|
318
|
+
if (targetDir) setExpanded(targetDir, true);
|
|
307
319
|
} catch (e) {
|
|
308
320
|
console.error("Upload error:", e);
|
|
309
321
|
}
|
|
310
|
-
}, [activeProject, loadTree]);
|
|
322
|
+
}, [activeProject, loadTree, setExpanded]);
|
|
311
323
|
|
|
312
324
|
const [isRootDragOver, setIsRootDragOver] = useState(false);
|
|
313
325
|
const rootDragCounter = useRef(0);
|
|
@@ -366,6 +366,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
366
366
|
// Finalize the streaming message — preserve SDK UUID for fork/rewind
|
|
367
367
|
const finalContent = streamingContentRef.current;
|
|
368
368
|
const finalEvents = [...streamingEventsRef.current];
|
|
369
|
+
const finalAccount = streamingAccountRef.current;
|
|
369
370
|
const doneUuid = ev.lastMessageUuid as string | undefined;
|
|
370
371
|
setMessages((prev) => {
|
|
371
372
|
const last = prev[prev.length - 1];
|
|
@@ -378,6 +379,19 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
378
379
|
...(doneUuid && { sdkUuid: doneUuid }),
|
|
379
380
|
}];
|
|
380
381
|
}
|
|
382
|
+
// No assistant message flushed yet (rAF was still pending when cancelled).
|
|
383
|
+
// Create one from accumulated refs so the response isn't silently lost.
|
|
384
|
+
if (finalContent || finalEvents.length > 0) {
|
|
385
|
+
return [...prev, {
|
|
386
|
+
id: `final-${Date.now()}`,
|
|
387
|
+
role: "assistant" as const,
|
|
388
|
+
content: finalContent,
|
|
389
|
+
events: finalEvents,
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
...(doneUuid && { sdkUuid: doneUuid }),
|
|
392
|
+
...finalAccount,
|
|
393
|
+
}];
|
|
394
|
+
}
|
|
381
395
|
return prev;
|
|
382
396
|
});
|
|
383
397
|
streamingContentRef.current = "";
|
|
@@ -401,6 +415,12 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
401
415
|
// Ignore keepalive pings
|
|
402
416
|
if ((data as any).type === "ping") return;
|
|
403
417
|
|
|
418
|
+
// Dispatch file change events for real-time editor reload
|
|
419
|
+
if ((data as any).type === "file:changed") {
|
|
420
|
+
window.dispatchEvent(new CustomEvent("file:changed", { detail: data }));
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
404
424
|
// Dispatch global Jira events so components can listen via window events
|
|
405
425
|
if (typeof (data as any).type === "string" && (data as any).type.startsWith("jira:")) {
|
|
406
426
|
window.dispatchEvent(new CustomEvent((data as any).type, { detail: data }));
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/markdown-renderer-BLksGuhd.js","assets/rolldown-runtime-FhOqtrmT.js","assets/index--W7nYakj.js","assets/vendor-mermaid-CylkVm4U.js","assets/vendor-ui-B-T_damt.js","assets/vendor-markdown-0Mxgxy0L.js","assets/input-CHRMley8.js","assets/utils-ChWX7pZv.js","assets/createLucideIcon-BjHrJDVb.js","assets/x-DlFGzN8d.js","assets/scroll-area-DwWF9FpN.js","assets/ai-settings-section-D2vqiydT.js","assets/dist-C5IgeqrV.js","assets/plus-51UQ45rf.js","assets/refresh-cw-CSFrDtiu.js","assets/trash-2-BgDIBl6f.js","assets/api-client-C3tXCh0r.js","assets/api-settings-2eTz4SgY.js","assets/chevron-right-BzAdxJRG.js","assets/database-D4DIhgi-.js","assets/react-BkWDCPD7.js","assets/extension-store-CkyOvGbF.js","assets/keybindings-store-CpP5_miA.js","assets/tab-store-Jvy1eZGM.js","assets/project-store-CczGNZyf.js","assets/settings-store-CuYjM0FF.js","assets/index-iZHWllzQ.css","assets/csv-preview-D37K2LRd.js","assets/lib-BqkcKGFq.js","assets/arrow-up-Dtrfv490.js","assets/csv-parser-BAa56Nnn.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-B-T_damt.js";import{t as r}from"./createLucideIcon-BjHrJDVb.js";import"./scroll-area-DwWF9FpN.js";import{t as i}from"./chevron-right-BzAdxJRG.js";import{a,l as o,n as s,o as c,r as l,s as u,t as d}from"./input-CHRMley8.js";import{t as f}from"./code-CuravVys.js";import{t as ee}from"./database-D4DIhgi-.js";import{n as p,r as m}from"./x-DlFGzN8d.js";import{t as h}from"./table-DbSviOmw.js";import{t as g}from"./text-wrap-DzvCTq_i.js";import{i as _,r as v,t as y}from"./api-client-C3tXCh0r.js";import{G as b}from"./vendor-mermaid-CylkVm4U.js";import{n as te}from"./settings-store-CuYjM0FF.js";import{t as x}from"./utils-ChWX7pZv.js";import{n as ne,t as re}from"./tab-store-Jvy1eZGM.js";import{n as ie}from"./project-store-CczGNZyf.js";import{B as S,E as ae,F as C,I as w,L as T,M as E,P as D,R as O,a as oe,c as se,d as k,f as A,g as j,h as M,l as N,m as P,o as F,p as ce,u as le,y as I,z as ue}from"./index--W7nYakj.js";import{n as de,t as fe}from"./use-monaco-theme-kjiAwvOp.js";import{n as pe,t as me}from"./sql-completion-provider-D3acAhav.js";var he=r(`file-exclamation-point`,[[`path`,{d:`M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z`,key:`1oefj6`}],[`path`,{d:`M12 9v4`,key:`juzpu7`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),L=e(n(),1),R=t(),ge={ts:O,tsx:O,js:O,jsx:O,py:O,rs:O,go:O,html:O,css:O,scss:O,json:ue,md:T,txt:T,yaml:w,yml:w};function z(e,t){return t?D:ge[e.split(`.`).pop()?.toLowerCase()??``]??C}function _e(e,t){let n=[],r=e;for(let e=0;e<t.length;e++){let i=t[e],a=t.slice(0,e+1).join(`/`),o=r.find(e=>e.name===i);if(n.push({name:i,fullPath:a,node:o??null,siblings:r}),o?.children)r=o.children;else{for(let r=e+1;r<t.length;r++)n.push({name:t[r],fullPath:t.slice(0,r+1).join(`/`),node:null,siblings:[]});break}}return n}function B(e){return[...e].sort((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1)}function ve({filePath:e,projectName:t,tabId:n,className:r}){let a=I(e=>e.tree),{updateTab:o,openTab:s}=re(M(e=>({updateTab:e.updateTab,openTab:e.openTab}))),c=(0,L.useRef)(null),l=(0,L.useMemo)(()=>_e(a,e.split(`/`).filter(Boolean)),[a,e]);(0,L.useEffect)(()=>{c.current&&(c.current.scrollLeft=c.current.scrollWidth)},[l]);function u(e,r){let i=x(e);r.metaKey||r.ctrlKey?s({type:`editor`,title:i,metadata:{filePath:e,projectName:t},projectId:t,closable:!0}):o(n,{title:i,metadata:{filePath:e,projectName:t}})}return(0,R.jsx)(`div`,{ref:c,className:r,children:l.map((e,n)=>(0,R.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[n>0&&(0,R.jsx)(i,{className:`size-3 text-muted-foreground shrink-0 mx-0.5`}),e.siblings.length>0?(0,R.jsx)(V,{segment:e,isLast:n===l.length-1,projectName:t,onFileClick:u}):(0,R.jsx)(`span`,{className:`text-xs text-muted-foreground px-1 py-0.5`,children:e.name})]},e.fullPath))})}function V({segment:e,isLast:t,projectName:n,onFileClick:r}){let i=(0,L.useMemo)(()=>B(e.siblings),[e.siblings]);return(0,R.jsxs)(se,{children:[(0,R.jsx)(P,{asChild:!0,children:(0,R.jsx)(`button`,{type:`button`,className:`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${t?`text-foreground font-medium`:`text-muted-foreground`}`,children:e.name})}),(0,R.jsx)(N,{align:`start`,className:`max-h-[300px] p-1`,children:i.map(t=>(0,R.jsx)(H,{node:t,projectName:n,activePath:e.fullPath,onFileClick:r},t.path))})]})}function H({node:e,projectName:t,activePath:n,onFileClick:r}){let i=z(e.name,e.type===`directory`),a=e.path===n;return e.type===`directory`&&e.children&&e.children.length>0?(0,R.jsxs)(k,{children:[(0,R.jsxs)(ce,{className:`text-xs gap-1.5 ${a?`bg-muted`:``}`,children:[(0,R.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,R.jsx)(`span`,{className:`truncate`,children:e.name})]}),(0,R.jsx)(A,{className:`max-h-[300px] overflow-y-auto p-1`,children:B(e.children).map(e=>(0,R.jsx)(H,{node:e,projectName:t,activePath:n,onFileClick:r},e.path))})]}):(0,R.jsxs)(le,{className:`text-xs gap-1.5 cursor-pointer ${a?`bg-muted`:``}`,onSelect:e=>{},onClick:t=>{e.type!==`directory`&&r(e.path,t)},children:[(0,R.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,R.jsx)(`span`,{className:`truncate`,children:e.name})]})}function U({active:e,onClick:t,icon:n,label:r}){return(0,R.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:[(0,R.jsx)(n,{className:`size-3`}),(0,R.jsx)(`span`,{className:`hidden sm:inline`,children:r})]})}function ye({ext:e,mdMode:t,onMdModeChange:n,csvMode:r,onCsvModeChange:i,wordWrap:a,onToggleWordWrap:o,filePath:s,projectName:c,className:l}){return(0,R.jsxs)(`div`,{className:l,children:[(e===`md`||e===`mdx`)&&n&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(U,{active:t===`edit`,onClick:()=>n(`edit`),icon:f,label:`Edit`}),(0,R.jsx)(U,{active:t===`preview`,onClick:()=>n(`preview`),icon:p,label:`Preview`})]}),e===`csv`&&i&&(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(U,{active:r===`table`,onClick:()=>i(`table`),icon:h,label:`Table`}),(0,R.jsx)(U,{active:r===`raw`,onClick:()=>i(`raw`),icon:f,label:`Raw`})]}),(0,R.jsx)(U,{active:a,onClick:o,icon:g,label:`Wrap`}),s&&c&&(0,R.jsx)(U,{active:!1,onClick:()=>j(c,s),icon:m,label:`Download`})]})}function be({open:e,defaultName:t,content:n,onSave:r,onCancel:i}){let[f,ee]=(0,L.useState)(t),[p,m]=(0,L.useState)(!1),[h,g]=(0,L.useState)(``),_=ie(e=>e.activeProject),v=(0,L.useCallback)(()=>{let e=f.trim();if(!e){g(`Filename cannot be empty`);return}if(/[/\\]/.test(e)){g(`Filename cannot contain / or \\`);return}g(``),m(!0)},[f]),y=(0,L.useCallback)(e=>{let t=e.includes(`\\`)?`\\`:`/`;r(e.endsWith(t)?`${e}${f.trim()}`:`${e}${t}${f.trim()}`,n)},[f,n,r]);return p?(0,R.jsx)(F,{open:!0,mode:`folder`,root:_?.path,title:`Save "${f.trim()}" to...`,onSelect:y,onCancel:()=>m(!1)}):(0,R.jsx)(s,{open:e,onOpenChange:e=>{e||i()},children:(0,R.jsxs)(l,{className:`sm:max-w-md`,children:[(0,R.jsx)(c,{children:(0,R.jsx)(u,{children:`Save As`})}),(0,R.jsxs)(`div`,{className:`flex flex-col gap-2 py-2`,children:[(0,R.jsx)(`label`,{className:`text-sm text-muted-foreground`,children:`Filename`}),(0,R.jsx)(d,{value:f,onChange:e=>{ee(e.target.value),g(``)},onKeyDown:e=>{e.key===`Enter`&&v()},placeholder:`e.g. my-file.ts`,autoFocus:!0}),h&&(0,R.jsx)(`p`,{className:`text-xs text-destructive`,children:h})]}),(0,R.jsxs)(a,{children:[(0,R.jsx)(o,{variant:`outline`,onClick:i,children:`Cancel`}),(0,R.jsx)(o,{onClick:v,children:`Choose Folder...`})]})]})})}var xe=(0,L.lazy)(()=>b(()=>import(`./markdown-renderer-BLksGuhd.js`).then(e=>({default:e.MarkdownRenderer})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]))),Se=(0,L.lazy)(()=>b(()=>import(`./csv-preview-D37K2LRd.js`).then(e=>({default:e.CsvPreview})),__vite__mapDeps([27,1,4,5,28,8,29,30]))),Ce=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`svg`,`ico`]),we=new Set([`db`,`sqlite`,`sqlite3`]);function Te(e){return e.split(`.`).pop()?.toLowerCase()??``}function Ee(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`,sql:`sql`}[Te(e)]??`plaintext`}var W=(0,L.memo)(function({metadata:e,tabId:t}){let n=e?.filePath,r=e?.projectName,i=e?.inlineContent,a=e?.inlineLanguage,[o,s]=(0,L.useState)(i??null),[c,l]=(0,L.useState)(`utf-8`),[u,d]=(0,L.useState)(!0),[f,p]=(0,L.useState)(null),[m,h]=(0,L.useState)(!1),g=(0,L.useRef)(null),v=(0,L.useRef)(``),b=(0,L.useRef)(null),{tabs:ie,updateTab:S}=re(M(e=>({tabs:e.tabs,updateTab:e.updateTab}))),{wordWrap:C,toggleWordWrap:w}=te(M(e=>({wordWrap:e.wordWrap,toggleWordWrap:e.toggleWordWrap}))),T=fe(),D=e?.isUntitled===!0,O=e?.unsavedContent,[se,k]=(0,L.useState)(!1),A=ie.find(e=>e.id===t),j=n?Te(n):``,N=Ce.has(j),P=j===`pdf`,F=we.has(j),ce=j===`md`||j===`mdx`,le=j===`csv`,I=j===`sql`,[ue,ge]=(0,L.useState)(`preview`),[z,_e]=(0,L.useState)(`table`),{connections:B,cachedTables:V,refreshTables:H}=oe(),[U,xe]=(0,L.useState)(()=>{if(!I||!n)return null;let e=localStorage.getItem(`ppm:sql-conn:${n}`);return e?Number(e):null}),W=(0,L.useRef)(null),G=(0,L.useRef)(null),K=(0,L.useMemo)(()=>B.find(e=>e.id===U)??null,[B,U]),Ae=i!=null&&(a===`json`||a===`xml`),[q,J]=(0,L.useState)(!1),je=(0,L.useCallback)(()=>{if(i)if(q)s(i),J(!1);else{let e=i.trimStart();if(a===`json`)try{s(JSON.stringify(JSON.parse(e),null,2)),J(!0)}catch{}else if(a===`xml`){let t=0;s(e.replace(/(>)(<)(\/*)/g,`$1
|
|
3
|
-
$2$3`).split(`
|
|
4
|
-
`).map(e=>{let n=e.trim();n.startsWith(`</`)&&(t=Math.max(0,t-1));let r=` `.repeat(t)+n;return n.startsWith(`<`)&&!n.startsWith(`</`)&&!n.endsWith(`/>`)&&!n.includes(`</`)&&t++,r}).join(`
|
|
5
|
-
`)),J(!0)}}},[i,a,q]),Me=(0,L.useCallback)(e=>{xe(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),H(e).catch(()=>{})},[n,H]),Y=(0,L.useMemo)(()=>{if(!I||!U)return;let e=(V.get(U)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>y.get(`/api/db/connections/${U}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[I,U,V]);(0,L.useEffect)(()=>{if(!(!W.current||!Y))return G.current?.dispose(),me(),G.current=W.current.languages.registerCompletionItemProvider(`sql`,pe(W.current,Y)),()=>{G.current?.dispose()}},[Y]);let Ne=re(e=>e.openTab),X=(0,L.useCallback)(e=>{K&&Ne({type:`database`,title:`${K.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:K.id,connectionName:K.name,dbType:K.type,initialSql:e}})},[K,Ne]),Pe=(0,L.useCallback)(()=>{if(!b.current||!K)return;let e=b.current,t=e.getSelection();X(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[K,X]),Z=(0,L.useRef)([]),Fe=(0,L.useRef)(X);Fe.current=X,(0,L.useEffect)(()=>()=>{Z.current.forEach(e=>e.dispose()),Z.current=[]},[]),(0,L.useEffect)(()=>{F&&t&&S(t,{type:`sqlite`})},[F,t,S]);let Q=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,L.useEffect)(()=>{if(i!=null){d(!1);return}if(D){s(O??``),v.current=O??``,d(!1),O&&h(!0);return}if(!n||!Q&&!r)return;if(N||P){d(!1);return}d(!0),p(null);let e=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${_(r)}/files/read?path=${encodeURIComponent(n)}`;return y.get(e).then(e=>{s(e.content),e.encoding&&l(e.encoding),v.current=e.content,d(!1)}).catch(e=>{p(e instanceof Error?e.message:`Failed to load file`),d(!1)}),()=>{g.current&&clearTimeout(g.current)}},[n,r,N,P,Q,D]),(0,L.useEffect)(()=>{if(!A)return;let t=D?`Untitled-${e?.untitledNumber??1}`:n?x(n):`Untitled`,r=m?`${t} \u25CF`:t;A.title!==r&&S(A.id,{title:r})},[m]);let Ie=(0,L.useCallback)(async e=>{if(n&&!(!Q&&!r))try{Q?await y.put(`/api/fs/write`,{path:n,content:e}):await y.put(`${_(r)}/files/write`,{path:n,content:e}),h(!1)}catch{}},[n,r,Q]);function Le(n){let r=n??``;s(r),v.current=r,h(!0),g.current&&clearTimeout(g.current),D?g.current=setTimeout(()=>{t&&S(t,{metadata:{...e,unsavedContent:v.current}})},2e3):g.current=setTimeout(()=>Ie(v.current),1e3)}let Re=(0,L.useCallback)(async(e,n)=>{try{if(g.current&&clearTimeout(g.current),await y.put(`/api/fs/write`,{path:e,content:n}),t){let{closeTab:n,openTab:r}=ne.getState();n(t),r({type:`editor`,title:x(e),projectId:null,metadata:{filePath:e},closable:!0})}h(!1),k(!1)}catch{}},[t]),$=e?.lineNumber,ze=(0,L.useCallback)((e,t)=>{if(b.current=e,W.current=t,$&&$>0&&setTimeout(()=>{e.revealLineInCenter($),e.setPosition({lineNumber:$,column:1}),e.focus()},100),D&&e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>k(!0)),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>te.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),Y&&(G.current?.dispose(),G.current=t.languages.registerCompletionItemProvider(`sql`,pe(t,Y))),I){Z.current.forEach(e=>e.dispose()),Z.current=[];let n=e.getModel(),r=e.addCommand(0,(e,t)=>{t&&Fe.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
|
-
`),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
|
-
`)),a=-1,o=[])}return a>0&&o.join(``).trim()&&c(a,o.join(`
|
|
8
|
-
`)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[Y]);if(!i&&!D&&(!n||!Q&&!r))return(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(u)return(0,R.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,R.jsx)(E,{className:`size-5 animate-spin`}),(0,R.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(f)return(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:f});if(N)return(0,R.jsx)(Oe,{filePath:n,projectName:r});if(P)return(0,R.jsx)(ke,{filePath:n,projectName:r});if(c===`base64`)return(0,R.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,R.jsx)(he,{className:`size-10 text-text-subtle`}),(0,R.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,R.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let Be=I?(0,R.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,R.jsx)(ee,{className:`size-3 text-muted-foreground`}),(0,R.jsxs)(`select`,{value:U??``,onChange:e=>{let t=Number(e.target.value);t&&Me(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,R.jsx)(`option`,{value:``,children:`Connection…`}),B.map(e=>(0,R.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,R.jsx)(`button`,{type:`button`,onClick:Pe,disabled:!K,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run all in DB Viewer`,children:(0,R.jsx)(ae,{className:`size-3.5`})})]}):null;return(0,R.jsxs)(`div`,{className:`flex flex-col h-full w-full overflow-hidden`,children:[i!=null&&Ae&&(0,R.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,R.jsx)(`button`,{type:`button`,onClick:je,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:q?`Raw`:`Beautify`})}),n&&r&&t&&(0,R.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,R.jsx)(ve,{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`}),Be,(0,R.jsx)(ye,{ext:j,mdMode:ue,onMdModeChange:ge,csvMode:z,onCsvModeChange:_e,wordWrap:C,onToggleWordWrap:w,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),I&&(!r||!t)&&(0,R.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,R.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?x(n):`SQL`}),Be]}),le&&z===`table`?(0,R.jsx)(L.Suspense,{fallback:(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,R.jsx)(E,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,R.jsx)(Se,{content:o??``,onContentChange:Le,wordWrap:C})}):ce&&ue===`preview`?(0,R.jsx)(De,{content:o??``}):(0,R.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,R.jsx)(de,{height:`100%`,language:a??Ee(n??``),value:o??``,onChange:i==null?Le:void 0,onMount:ze,theme:T,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:C?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:i!=null},loading:(0,R.jsx)(E,{className:`size-5 animate-spin text-text-subtle`})})}),se&&(0,R.jsx)(be,{open:se,defaultName:`Untitled-${e?.untitledNumber??1}`,content:v.current,onSave:Re,onCancel:()=>k(!1)})]})});function De({content:e}){return(0,R.jsx)(L.Suspense,{fallback:(0,R.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,R.jsx)(xe,{content:e,className:`flex-1 overflow-auto p-4`})})}function Oe({filePath:e,projectName:t}){let[n,r]=(0,L.useState)(null),[i,a]=(0,L.useState)(!1);return(0,L.useEffect)(()=>{let n,i=`${_(t)}/files/raw?path=${encodeURIComponent(e)}`,o=v();return fetch(i,{headers:o?{Authorization:`Bearer ${o}`}:{}}).then(e=>{if(!e.ok)throw Error(`Failed`);return e.blob()}).then(e=>{let t=URL.createObjectURL(e);n=t,r(t)}).catch(()=>a(!0)),()=>{n&&URL.revokeObjectURL(n)}},[e,t]),i?(0,R.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,R.jsx)(he,{className:`size-10 text-text-subtle`}),(0,R.jsx)(`p`,{className:`text-sm`,children:`Failed to load image.`})]}):n?(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,R.jsx)(`img`,{src:n,alt:e,className:`max-w-full max-h-full object-contain`})}):(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,R.jsx)(E,{className:`size-5 animate-spin text-text-subtle`})})}function ke({filePath:e,projectName:t}){let[n,r]=(0,L.useState)(null),[i,a]=(0,L.useState)(!1);(0,L.useEffect)(()=>{let n,i=`${_(t)}/files/raw?path=${encodeURIComponent(e)}`,o=v();return fetch(i,{headers:o?{Authorization:`Bearer ${o}`}:{}}).then(e=>{if(!e.ok)throw Error(`Failed`);return e.blob()}).then(e=>{let t=URL.createObjectURL(new Blob([e],{type:`application/pdf`}));n=t,r(t)}).catch(()=>a(!0)),()=>{n&&URL.revokeObjectURL(n)}},[e,t]);let o=(0,L.useCallback)(()=>{n&&window.open(n,`_blank`)},[n]);return i?(0,R.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,R.jsx)(he,{className:`size-10 text-text-subtle`}),(0,R.jsx)(`p`,{className:`text-sm`,children:`Failed to load PDF.`})]}):n?(0,R.jsxs)(`div`,{className:`flex flex-col h-full`,children:[(0,R.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,R.jsx)(`span`,{className:`text-xs text-text-secondary truncate`,children:e}),(0,R.jsxs)(`button`,{onClick:o,className:`flex items-center gap-1 text-xs text-text-secondary hover:text-text-primary transition-colors`,children:[(0,R.jsx)(S,{className:`size-3`}),` Open in new tab`]})]}),(0,R.jsx)(`iframe`,{src:n,title:e,className:`flex-1 w-full border-none`})]}):(0,R.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,R.jsx)(E,{className:`size-5 animate-spin text-text-subtle`})})}export{W as CodeEditor};
|