@hienlh/ppm 0.13.49 → 0.13.50

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/dist/web/assets/{audio-preview-CQ-l5P8I.js → audio-preview-CILFIsuu.js} +2 -2
  5. package/dist/web/assets/{audio-preview-CQ-l5P8I.js.map → audio-preview-CILFIsuu.js.map} +1 -1
  6. package/dist/web/assets/{chat-tab-DbuBr2ax.js → chat-tab-DBYwH_Aa.js} +4 -4
  7. package/dist/web/assets/{chat-tab-DbuBr2ax.js.map → chat-tab-DBYwH_Aa.js.map} +1 -1
  8. package/dist/web/assets/{code-editor-DEa0t62y.js → code-editor-MXnkYRLp.js} +3 -3
  9. package/dist/web/assets/{code-editor-DEa0t62y.js.map → code-editor-MXnkYRLp.js.map} +1 -1
  10. package/dist/web/assets/{conflict-editor-D5H9urYy.js → conflict-editor-C6wH5wV6.js} +2 -2
  11. package/dist/web/assets/{conflict-editor-D5H9urYy.js.map → conflict-editor-C6wH5wV6.js.map} +1 -1
  12. package/dist/web/assets/{database-viewer-CW60ytCl.js → database-viewer-BjUruZLv.js} +2 -2
  13. package/dist/web/assets/{database-viewer-CW60ytCl.js.map → database-viewer-BjUruZLv.js.map} +1 -1
  14. package/dist/web/assets/{diff-viewer-BfatMgWw.js → diff-viewer-B_nU7bQi.js} +2 -2
  15. package/dist/web/assets/{diff-viewer-BfatMgWw.js.map → diff-viewer-B_nU7bQi.js.map} +1 -1
  16. package/dist/web/assets/{extension-webview-DKSDoW_g.js → extension-webview-B56ZfvoD.js} +2 -2
  17. package/dist/web/assets/{extension-webview-DKSDoW_g.js.map → extension-webview-B56ZfvoD.js.map} +1 -1
  18. package/dist/web/assets/{glide-data-grid-Bx48618B.js → glide-data-grid-D-qQqqp7.js} +2 -2
  19. package/dist/web/assets/{glide-data-grid-Bx48618B.js.map → glide-data-grid-D-qQqqp7.js.map} +1 -1
  20. package/dist/web/assets/{image-preview-ClY2xl1B.js → image-preview-Dc6AiqYX.js} +2 -2
  21. package/dist/web/assets/{image-preview-ClY2xl1B.js.map → image-preview-Dc6AiqYX.js.map} +1 -1
  22. package/dist/web/assets/{index-DkQ6jVSH.js → index-8_rE2Q1-.js} +5 -5
  23. package/dist/web/assets/{index-DkQ6jVSH.js.map → index-8_rE2Q1-.js.map} +1 -1
  24. package/dist/web/assets/keybindings-store-COJD5O6M.js +1 -0
  25. package/dist/web/assets/{markdown-renderer-BmMmo0F-.js → markdown-renderer-CNQ8I0Dk.js} +2 -2
  26. package/dist/web/assets/{markdown-renderer-BmMmo0F-.js.map → markdown-renderer-CNQ8I0Dk.js.map} +1 -1
  27. package/dist/web/assets/notification-store-BiZaLXop.js +1 -0
  28. package/dist/web/assets/{panel-store-Dy8-7E_g.js → panel-store-C8wwxBpn.js} +2 -2
  29. package/dist/web/assets/{panel-store-Dy8-7E_g.js.map → panel-store-C8wwxBpn.js.map} +1 -1
  30. package/dist/web/assets/{pdf-preview-YylEP_Su.js → pdf-preview-zs9QdgDp.js} +2 -2
  31. package/dist/web/assets/{pdf-preview-YylEP_Su.js.map → pdf-preview-zs9QdgDp.js.map} +1 -1
  32. package/dist/web/assets/{port-forwarding-tab-COdo70kU.js → port-forwarding-tab-sArYx1nt.js} +2 -2
  33. package/dist/web/assets/{port-forwarding-tab-COdo70kU.js.map → port-forwarding-tab-sArYx1nt.js.map} +1 -1
  34. package/dist/web/assets/{postgres-viewer-3y9VshZZ.js → postgres-viewer-khk7N7cd.js} +2 -2
  35. package/dist/web/assets/{postgres-viewer-3y9VshZZ.js.map → postgres-viewer-khk7N7cd.js.map} +1 -1
  36. package/dist/web/assets/{settings-tab-sYavdJk-.js → settings-tab-CGWhVzQm.js} +1 -1
  37. package/dist/web/assets/{sql-query-editor-DlBYx1Ye.js → sql-query-editor-B5Ndypxp.js} +2 -2
  38. package/dist/web/assets/{sql-query-editor-DlBYx1Ye.js.map → sql-query-editor-B5Ndypxp.js.map} +1 -1
  39. package/dist/web/assets/{sqlite-viewer-Bj8oPYho.js → sqlite-viewer-BkpONSGa.js} +2 -2
  40. package/dist/web/assets/{sqlite-viewer-Bj8oPYho.js.map → sqlite-viewer-BkpONSGa.js.map} +1 -1
  41. package/dist/web/assets/{tab-store-Dtg1_qL0.js → tab-store-CNas5Ny8.js} +2 -2
  42. package/dist/web/assets/{tab-store-Dtg1_qL0.js.map → tab-store-CNas5Ny8.js.map} +1 -1
  43. package/dist/web/assets/{terminal-tab-B7ECmf95.js → terminal-tab-BgMCsdeN.js} +2 -2
  44. package/dist/web/assets/{terminal-tab-B7ECmf95.js.map → terminal-tab-BgMCsdeN.js.map} +1 -1
  45. package/dist/web/assets/{video-preview-CT78iZBo.js → video-preview-w8ZAy8av.js} +2 -2
  46. package/dist/web/assets/{video-preview-CT78iZBo.js.map → video-preview-w8ZAy8av.js.map} +1 -1
  47. package/dist/web/index.html +3 -3
  48. package/dist/web/sw.js +1 -1
  49. package/package.json +1 -1
  50. package/src/web/stores/panel-store.ts +5 -0
  51. package/dist/web/assets/keybindings-store-DrAeg6Gw.js +0 -1
  52. package/dist/web/assets/notification-store-Bukl8bKo.js +0 -1
@@ -1 +1 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import"./scroll-area-BDi_FNzr.js";import"./ai-settings-section-AuV6Lzz2.js";import"./input-_LFQwhzd.js";import"./api-client-DIhJ5qVW.js";import"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import"./file-store-DOxcU_7s.js";import"./api-settings-C3T95dWg.js";import{s as e}from"./index-DkQ6jVSH.js";export{e as SettingsTab};
1
+ import"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import"./scroll-area-BDi_FNzr.js";import"./ai-settings-section-AuV6Lzz2.js";import"./input-_LFQwhzd.js";import"./api-client-DIhJ5qVW.js";import"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import"./file-store-DOxcU_7s.js";import"./api-settings-C3T95dWg.js";import{s as e}from"./index-8_rE2Q1-.js";export{e as SettingsTab};
@@ -1,4 +1,4 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{i as r,r as i}from"./glide-data-grid-Bx48618B.js";import{n as a,t as o}from"./use-monaco-theme-DEI-tJAh.js";var s=e(n(),1),c=t();function l(e,t){let n=e.split(`
1
+ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{i as r,r as i}from"./glide-data-grid-D-qQqqp7.js";import{n as a,t as o}from"./use-monaco-theme-DEI-tJAh.js";var s=e(n(),1),c=t();function l(e,t){let n=e.split(`
2
2
  `),r=0;for(let e=0;e<n.length;e++){let i=n[e].trim();e<t-1&&i.endsWith(`;`)&&(r=e+1)}let i=n.length-1;for(let e=t-1;e<n.length;e++)if(n[e].trim().endsWith(`;`)){i=e;break}for(;r<=i;){let e=n[r].trim();if(e&&!e.startsWith(`--`))break;r++}return n.slice(r,i+1).join(`
3
3
  `).trim()}function u({onExecute:e,loading:t,defaultValue:n=`SELECT * FROM `,schemaInfo:u,cacheKey:d}){let f=d?`ppm:sql-query:${d}`:null,[p,m]=(0,s.useState)(()=>{if(f)try{return sessionStorage.getItem(f)??n}catch{}return n}),h=(0,s.useRef)(!1),g=(0,s.useRef)(null),_=(0,s.useRef)(null),v=(0,s.useRef)(null),y=(0,s.useRef)(e);y.current=e;let b=o();(0,s.useEffect)(()=>{if(!(!_.current||!u))return v.current?.dispose(),i(),v.current=_.current.languages.registerCompletionItemProvider(`sql`,r(_.current,u)),()=>{v.current?.dispose()}},[u]);let x=(0,s.useCallback)((e,t)=>{g.current=e,_.current=t,e.addAction({id:`run-query-at-cursor`,label:`Run Statement at Cursor`,keybindings:[t.KeyMod.CtrlCmd|t.KeyCode.Enter],run:e=>{let t=e.getPosition();if(!t)return;let n=l(e.getValue(),t.lineNumber);n&&y.current(n)}}),u&&(v.current?.dispose(),v.current=t.languages.registerCompletionItemProvider(`sql`,r(t,u)))},[u]);return(0,s.useEffect)(()=>{h.current||m(n)},[n]),(0,c.jsx)(`div`,{className:`h-full overflow-hidden`,children:(0,c.jsx)(a,{height:`100%`,language:`sql`,theme:b,value:p,onChange:e=>{let t=e??``;if(m(t),h.current=!0,f)try{sessionStorage.setItem(f,t)}catch{}},onMount:x,options:{minimap:{enabled:!1},lineNumbers:`off`,scrollBeyondLastLine:!1,wordWrap:`on`,fontSize:12,tabSize:2,renderLineHighlight:`none`,overviewRulerLanes:0,hideCursorInOverviewRuler:!0,scrollbar:{vertical:`auto`,horizontal:`auto`,verticalScrollbarSize:6,horizontalScrollbarSize:6},padding:{top:4,bottom:4},lineDecorationsWidth:4,lineNumbersMinChars:0,glyphMargin:!1,folding:!1,fixedOverflowWidgets:!0}})})}export{u as t};
4
- //# sourceMappingURL=sql-query-editor-DlBYx1Ye.js.map
4
+ //# sourceMappingURL=sql-query-editor-B5Ndypxp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql-query-editor-DlBYx1Ye.js","names":[],"sources":["../../../src/web/components/database/sql-query-editor.tsx"],"sourcesContent":["import { useState, useCallback, useRef, useEffect } from \"react\";\nimport Editor, { type OnMount } from \"@monaco-editor/react\";\nimport type * as MonacoType from \"monaco-editor\";\nimport { useMonacoTheme } from \"@/lib/use-monaco-theme\";\nimport { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from \"./sql-completion-provider\";\n\ninterface SqlQueryEditorProps {\n onExecute: (sql: string) => void;\n loading: boolean;\n defaultValue?: string;\n schemaInfo?: SchemaInfo;\n /** Unique key for caching query text in sessionStorage (e.g. connectionId) */\n cacheKey?: string;\n}\n\n/** Find the SQL statement surrounding the cursor line (split by ;) */\nexport function getStatementAtCursor(text: string, cursorLine: number): string {\n const lines = text.split(\"\\n\");\n // Find statement boundaries (lines where a statement ends with ;)\n let stmtStart = 0;\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (i < cursorLine - 1 && trimmed.endsWith(\";\")) {\n stmtStart = i + 1;\n }\n }\n // Find statement end\n let stmtEnd = lines.length - 1;\n for (let i = cursorLine - 1; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (trimmed.endsWith(\";\")) {\n stmtEnd = i;\n break;\n }\n }\n // Skip leading empty/comment lines\n while (stmtStart <= stmtEnd) {\n const t = lines[stmtStart]!.trim();\n if (t && !t.startsWith(\"--\")) break;\n stmtStart++;\n }\n return lines.slice(stmtStart, stmtEnd + 1).join(\"\\n\").trim();\n}\n\n/** Shared Monaco-based SQL query editor (editor only, no results) */\nexport function SqlQueryEditor({ onExecute, loading, defaultValue = \"SELECT * FROM \", schemaInfo, cacheKey }: SqlQueryEditorProps) {\n const storageKey = cacheKey ? `ppm:sql-query:${cacheKey}` : null;\n const [query, setQuery] = useState(() => {\n if (storageKey) { try { return sessionStorage.getItem(storageKey) ?? defaultValue; } catch { /* */ } }\n return defaultValue;\n });\n const userEditedRef = useRef(false);\n const editorRef = useRef<MonacoType.editor.IStandaloneCodeEditor | null>(null);\n const monacoRef = useRef<typeof MonacoType | null>(null);\n const disposableRef = useRef<MonacoType.IDisposable | null>(null);\n const onExecuteRef = useRef(onExecute);\n onExecuteRef.current = onExecute;\n const monacoTheme = useMonacoTheme();\n\n useEffect(() => {\n if (!monacoRef.current || !schemaInfo) return;\n disposableRef.current?.dispose();\n clearCompletionCache();\n disposableRef.current = monacoRef.current.languages.registerCompletionItemProvider(\n \"sql\",\n createSqlCompletionProvider(monacoRef.current, schemaInfo),\n );\n return () => { disposableRef.current?.dispose(); };\n }, [schemaInfo]);\n\n const handleMount: OnMount = useCallback((editor, monaco) => {\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Cmd/Ctrl+Enter: run statement at cursor\n editor.addAction({\n id: \"run-query-at-cursor\",\n label: \"Run Statement at Cursor\",\n keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],\n run: (ed) => {\n const pos = ed.getPosition();\n if (!pos) return;\n const text = ed.getValue();\n const stmt = getStatementAtCursor(text, pos.lineNumber);\n if (stmt) onExecuteRef.current(stmt);\n },\n });\n\n if (schemaInfo) {\n disposableRef.current?.dispose();\n disposableRef.current = monaco.languages.registerCompletionItemProvider(\n \"sql\",\n createSqlCompletionProvider(monaco, schemaInfo),\n );\n }\n }, [schemaInfo]);\n\n // Sync from defaultValue only if user hasn't manually edited\n useEffect(() => {\n if (!userEditedRef.current) setQuery(defaultValue);\n }, [defaultValue]);\n\n return (\n <div className=\"h-full overflow-hidden\">\n <Editor\n height=\"100%\"\n language=\"sql\"\n theme={monacoTheme}\n value={query}\n onChange={(v) => { const val = v ?? \"\"; setQuery(val); userEditedRef.current = true; if (storageKey) try { sessionStorage.setItem(storageKey, val); } catch {} }}\n onMount={handleMount}\n options={{\n minimap: { enabled: false },\n lineNumbers: \"off\",\n scrollBeyondLastLine: false,\n wordWrap: \"on\",\n fontSize: 12,\n tabSize: 2,\n renderLineHighlight: \"none\",\n overviewRulerLanes: 0,\n hideCursorInOverviewRuler: true,\n scrollbar: { vertical: \"auto\", horizontal: \"auto\", verticalScrollbarSize: 6, horizontalScrollbarSize: 6 },\n padding: { top: 4, bottom: 4 },\n lineDecorationsWidth: 4,\n lineNumbersMinChars: 0,\n glyphMargin: false,\n folding: false,\n fixedOverflowWidgets: true,\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sql-query-editor-B5Ndypxp.js","names":[],"sources":["../../../src/web/components/database/sql-query-editor.tsx"],"sourcesContent":["import { useState, useCallback, useRef, useEffect } from \"react\";\nimport Editor, { type OnMount } from \"@monaco-editor/react\";\nimport type * as MonacoType from \"monaco-editor\";\nimport { useMonacoTheme } from \"@/lib/use-monaco-theme\";\nimport { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from \"./sql-completion-provider\";\n\ninterface SqlQueryEditorProps {\n onExecute: (sql: string) => void;\n loading: boolean;\n defaultValue?: string;\n schemaInfo?: SchemaInfo;\n /** Unique key for caching query text in sessionStorage (e.g. connectionId) */\n cacheKey?: string;\n}\n\n/** Find the SQL statement surrounding the cursor line (split by ;) */\nexport function getStatementAtCursor(text: string, cursorLine: number): string {\n const lines = text.split(\"\\n\");\n // Find statement boundaries (lines where a statement ends with ;)\n let stmtStart = 0;\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (i < cursorLine - 1 && trimmed.endsWith(\";\")) {\n stmtStart = i + 1;\n }\n }\n // Find statement end\n let stmtEnd = lines.length - 1;\n for (let i = cursorLine - 1; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (trimmed.endsWith(\";\")) {\n stmtEnd = i;\n break;\n }\n }\n // Skip leading empty/comment lines\n while (stmtStart <= stmtEnd) {\n const t = lines[stmtStart]!.trim();\n if (t && !t.startsWith(\"--\")) break;\n stmtStart++;\n }\n return lines.slice(stmtStart, stmtEnd + 1).join(\"\\n\").trim();\n}\n\n/** Shared Monaco-based SQL query editor (editor only, no results) */\nexport function SqlQueryEditor({ onExecute, loading, defaultValue = \"SELECT * FROM \", schemaInfo, cacheKey }: SqlQueryEditorProps) {\n const storageKey = cacheKey ? `ppm:sql-query:${cacheKey}` : null;\n const [query, setQuery] = useState(() => {\n if (storageKey) { try { return sessionStorage.getItem(storageKey) ?? defaultValue; } catch { /* */ } }\n return defaultValue;\n });\n const userEditedRef = useRef(false);\n const editorRef = useRef<MonacoType.editor.IStandaloneCodeEditor | null>(null);\n const monacoRef = useRef<typeof MonacoType | null>(null);\n const disposableRef = useRef<MonacoType.IDisposable | null>(null);\n const onExecuteRef = useRef(onExecute);\n onExecuteRef.current = onExecute;\n const monacoTheme = useMonacoTheme();\n\n useEffect(() => {\n if (!monacoRef.current || !schemaInfo) return;\n disposableRef.current?.dispose();\n clearCompletionCache();\n disposableRef.current = monacoRef.current.languages.registerCompletionItemProvider(\n \"sql\",\n createSqlCompletionProvider(monacoRef.current, schemaInfo),\n );\n return () => { disposableRef.current?.dispose(); };\n }, [schemaInfo]);\n\n const handleMount: OnMount = useCallback((editor, monaco) => {\n editorRef.current = editor;\n monacoRef.current = monaco;\n\n // Cmd/Ctrl+Enter: run statement at cursor\n editor.addAction({\n id: \"run-query-at-cursor\",\n label: \"Run Statement at Cursor\",\n keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],\n run: (ed) => {\n const pos = ed.getPosition();\n if (!pos) return;\n const text = ed.getValue();\n const stmt = getStatementAtCursor(text, pos.lineNumber);\n if (stmt) onExecuteRef.current(stmt);\n },\n });\n\n if (schemaInfo) {\n disposableRef.current?.dispose();\n disposableRef.current = monaco.languages.registerCompletionItemProvider(\n \"sql\",\n createSqlCompletionProvider(monaco, schemaInfo),\n );\n }\n }, [schemaInfo]);\n\n // Sync from defaultValue only if user hasn't manually edited\n useEffect(() => {\n if (!userEditedRef.current) setQuery(defaultValue);\n }, [defaultValue]);\n\n return (\n <div className=\"h-full overflow-hidden\">\n <Editor\n height=\"100%\"\n language=\"sql\"\n theme={monacoTheme}\n value={query}\n onChange={(v) => { const val = v ?? \"\"; setQuery(val); userEditedRef.current = true; if (storageKey) try { sessionStorage.setItem(storageKey, val); } catch {} }}\n onMount={handleMount}\n options={{\n minimap: { enabled: false },\n lineNumbers: \"off\",\n scrollBeyondLastLine: false,\n wordWrap: \"on\",\n fontSize: 12,\n tabSize: 2,\n renderLineHighlight: \"none\",\n overviewRulerLanes: 0,\n hideCursorInOverviewRuler: true,\n scrollbar: { vertical: \"auto\", horizontal: \"auto\", verticalScrollbarSize: 6, horizontalScrollbarSize: 6 },\n padding: { top: 4, bottom: 4 },\n lineDecorationsWidth: 4,\n lineNumbersMinChars: 0,\n glyphMargin: false,\n folding: false,\n fixedOverflowWidgets: true,\n }}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import{t as r}from"./database-DOWH9-Vv.js";import{t as i}from"./glide-data-grid-Bx48618B.js";import{t as a}from"./refresh-cw-BjrAbUJe.js";import{t as o}from"./table-BzjWcs87.js";import{i as s,t as c}from"./api-client-DIhJ5qVW.js";import"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import"./panel-store-Dy8-7E_g.js";import"./tab-store-Dtg1_qL0.js";import{at as l,q as u}from"./index-DkQ6jVSH.js";import"./data-grid-types-D2cHE8hx.js";import"./use-monaco-theme-DEI-tJAh.js";import{t as d}from"./sql-query-editor-DlBYx1Ye.js";var f=e(n(),1);function p(e,t,n){let[r,i]=(0,f.useState)([]),[a,o]=(0,f.useState)(null),[l,u]=(0,f.useState)(null),[d,p]=(0,f.useState)([]),[m,h]=(0,f.useState)(!1),[g,_]=(0,f.useState)(null),[v,y]=(0,f.useState)(1),[b,x]=(0,f.useState)(null),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(!1),E=n?`/api/db/connections/${n}`:null,D=E??`${s(e)}/sqlite`,O=E?``:`path=${encodeURIComponent(t)}`,k=(0,f.useCallback)(async()=>{h(!0),_(null);try{let e=E?`?cached=1`:O?`?${O}`:``,t=await c.get(`${D}/tables${e}`);i(t),!E&&t.length>0&&!a&&o(t[0].name)}catch(e){_(e.message)}finally{h(!1)}},[D,O,E]);(0,f.useEffect)(()=>{k()},[k]);let A=(0,f.useCallback)(async()=>{if(a){h(!0);try{let e=O?`${O}&`:``,[t,n]=await Promise.all([c.get(`${D}/data?${e}table=${encodeURIComponent(a)}&page=${v}&limit=100`),c.get(`${D}/schema?${e}table=${encodeURIComponent(a)}`)]);u(t),p(n)}catch(e){_(e.message)}finally{h(!1)}}},[D,O,a,v]);return(0,f.useEffect)(()=>{A()},[A]),{tables:r,selectedTable:a,selectTable:(0,f.useCallback)(e=>{o(e),y(1),x(null)},[]),tableData:l,schema:d,loading:m,error:g,page:v,setPage:y,queryResult:b,queryError:S,queryLoading:w,executeQuery:(0,f.useCallback)(async e=>{T(!0),C(null);try{let n=E?{sql:e}:{path:t,sql:e},r=await c.post(`${D}/query`,n);x(r),r.changeType===`modify`&&A()}catch(e){C(e.message)}finally{T(!1)}},[D,E,t,A]),updateCell:(0,f.useCallback)(async(e,n,r)=>{if(a)try{E?await c.put(`${D}/cell`,{table:a,pkColumn:`rowid`,pkValue:e,column:n,value:r}):await c.put(`${D}/cell`,{path:t,table:a,rowid:e,column:n,value:r}),A()}catch(e){_(e.message)}},[D,E,t,a,A]),deleteRow:(0,f.useCallback)(async e=>{if(a)try{E?await c.del(`${D}/row`,{table:a,pkColumn:`rowid`,pkValue:e}):await c.del(`${D}/row`,{path:t,table:a,rowid:e}),A(),k()}catch(e){_(e.message)}},[D,E,t,a,A,k]),refreshTables:k,refreshData:A}}var m=t();function h({tables:e,selectedTable:t,onSelect:n,onRefresh:r}){return(0,m.jsxs)(`div`,{className:`w-48 shrink-0 flex flex-col bg-background overflow-hidden`,children:[(0,m.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border`,children:[(0,m.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground uppercase tracking-wider`,children:`Tables`}),(0,m.jsx)(`button`,{type:`button`,onClick:r,className:`text-muted-foreground hover:text-foreground transition-colors`,title:`Refresh tables`,children:(0,m.jsx)(a,{className:`size-3`})})]}),(0,m.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[e.map(e=>(0,m.jsxs)(`button`,{type:`button`,onClick:()=>n(e.name),className:`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors ${t===e.name?`bg-muted text-foreground`:`text-muted-foreground hover:bg-muted/50 hover:text-foreground`}`,children:[(0,m.jsx)(o,{className:`size-3 shrink-0`}),(0,m.jsx)(`span`,{className:`truncate flex-1`,children:e.name}),(0,m.jsx)(`span`,{className:`text-[10px] opacity-60`,children:e.rowCount})]},e.name)),e.length===0&&(0,m.jsx)(`p`,{className:`px-3 py-4 text-xs text-muted-foreground text-center`,children:`No tables found`})]})]})}function g({onExecute:e,loading:t}){return(0,m.jsx)(d,{onExecute:e,loading:t})}function _({metadata:e}){let t=e?.filePath,n=e?.projectName,i=e?.connectionId,a=e?.tableName,[o,s]=(0,f.useState)(!1);return i?(0,m.jsx)(v,{projectName:``,dbPath:``,connectionId:i,connectionName:e?.connectionName,initialTable:a,queryPanelOpen:o,onToggleQueryPanel:()=>s(e=>!e),hideTableList:!0}):!t||!n?(0,m.jsxs)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:[(0,m.jsx)(r,{className:`size-5 mr-2`}),` No database file selected.`]}):(0,m.jsx)(v,{projectName:n,dbPath:t,queryPanelOpen:o,onToggleQueryPanel:()=>s(e=>!e)})}function v({projectName:e,dbPath:t,connectionId:n,connectionName:a,initialTable:o,queryPanelOpen:s,onToggleQueryPanel:c,hideTableList:d}){let _=p(e,t,n),v=(0,f.useRef)(!1);return(0,f.useEffect)(()=>{o&&!v.current&&_.tables.length>0&&(v.current=!0,_.selectTable(o))},[o,_.tables]),_.error&&_.tables.length===0?(0,m.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,m.jsx)(l,{className:`size-10 text-destructive`}),(0,m.jsx)(`p`,{className:`text-sm`,children:_.error})]}):_.loading&&_.tables.length===0?(0,m.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,m.jsx)(u,{className:`size-5 animate-spin`}),(0,m.jsx)(`span`,{className:`text-sm`,children:`Loading database...`})]}):(0,m.jsxs)(`div`,{className:`flex h-full w-full overflow-hidden`,children:[!d&&(0,m.jsx)(h,{tables:_.tables,selectedTable:_.selectedTable,onSelect:_.selectTable,onRefresh:_.refreshTables}),(0,m.jsxs)(`div`,{className:`flex-1 flex flex-col overflow-hidden ${d?``:`border-l border-border`}`,children:[(0,m.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,m.jsx)(r,{className:`size-3.5 text-muted-foreground`}),(0,m.jsx)(`span`,{className:`text-xs text-muted-foreground truncate`,children:a??t}),(0,m.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:_.selectedTable&&`/ ${_.selectedTable}`}),(0,m.jsx)(`div`,{className:`ml-auto`,children:(0,m.jsx)(`button`,{type:`button`,onClick:c,className:`px-2 py-1 rounded text-xs transition-colors ${s?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:`SQL`})})]}),(0,m.jsx)(`div`,{className:`flex-1 overflow-hidden ${s?`max-h-[60%]`:``}`,children:_.tableData?(0,m.jsx)(i,{columns:_.tableData.columns,rows:_.tableData.rows,total:_.tableData.total,limit:_.tableData.limit,schema:_.schema.map(e=>({name:e.name,type:e.type,nullable:!e.notnull,pk:!!e.pk,defaultValue:e.dflt_value,fk:e.fk??null})),loading:_.loading,page:_.page,onPageChange:_.setPage,onCellUpdate:(e,t,n,r)=>_.updateCell(t,n,r),onRowDelete:(e,t)=>_.deleteRow(t)}):(0,m.jsx)(`div`,{className:`flex items-center justify-center h-full text-xs text-muted-foreground`,children:_.loading?(0,m.jsx)(u,{className:`size-4 animate-spin`}):`Select a table`})}),s&&(0,m.jsx)(`div`,{className:`border-t border-border h-[40%] shrink-0`,children:(0,m.jsx)(g,{onExecute:_.executeQuery,loading:_.queryLoading})})]})]})}export{_ as SqliteViewer};
2
- //# sourceMappingURL=sqlite-viewer-Bj8oPYho.js.map
1
+ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import{t as r}from"./database-DOWH9-Vv.js";import{t as i}from"./glide-data-grid-D-qQqqp7.js";import{t as a}from"./refresh-cw-BjrAbUJe.js";import{t as o}from"./table-BzjWcs87.js";import{i as s,t as c}from"./api-client-DIhJ5qVW.js";import"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import"./panel-store-C8wwxBpn.js";import"./tab-store-CNas5Ny8.js";import{at as l,q as u}from"./index-8_rE2Q1-.js";import"./data-grid-types-D2cHE8hx.js";import"./use-monaco-theme-DEI-tJAh.js";import{t as d}from"./sql-query-editor-B5Ndypxp.js";var f=e(n(),1);function p(e,t,n){let[r,i]=(0,f.useState)([]),[a,o]=(0,f.useState)(null),[l,u]=(0,f.useState)(null),[d,p]=(0,f.useState)([]),[m,h]=(0,f.useState)(!1),[g,_]=(0,f.useState)(null),[v,y]=(0,f.useState)(1),[b,x]=(0,f.useState)(null),[S,C]=(0,f.useState)(null),[w,T]=(0,f.useState)(!1),E=n?`/api/db/connections/${n}`:null,D=E??`${s(e)}/sqlite`,O=E?``:`path=${encodeURIComponent(t)}`,k=(0,f.useCallback)(async()=>{h(!0),_(null);try{let e=E?`?cached=1`:O?`?${O}`:``,t=await c.get(`${D}/tables${e}`);i(t),!E&&t.length>0&&!a&&o(t[0].name)}catch(e){_(e.message)}finally{h(!1)}},[D,O,E]);(0,f.useEffect)(()=>{k()},[k]);let A=(0,f.useCallback)(async()=>{if(a){h(!0);try{let e=O?`${O}&`:``,[t,n]=await Promise.all([c.get(`${D}/data?${e}table=${encodeURIComponent(a)}&page=${v}&limit=100`),c.get(`${D}/schema?${e}table=${encodeURIComponent(a)}`)]);u(t),p(n)}catch(e){_(e.message)}finally{h(!1)}}},[D,O,a,v]);return(0,f.useEffect)(()=>{A()},[A]),{tables:r,selectedTable:a,selectTable:(0,f.useCallback)(e=>{o(e),y(1),x(null)},[]),tableData:l,schema:d,loading:m,error:g,page:v,setPage:y,queryResult:b,queryError:S,queryLoading:w,executeQuery:(0,f.useCallback)(async e=>{T(!0),C(null);try{let n=E?{sql:e}:{path:t,sql:e},r=await c.post(`${D}/query`,n);x(r),r.changeType===`modify`&&A()}catch(e){C(e.message)}finally{T(!1)}},[D,E,t,A]),updateCell:(0,f.useCallback)(async(e,n,r)=>{if(a)try{E?await c.put(`${D}/cell`,{table:a,pkColumn:`rowid`,pkValue:e,column:n,value:r}):await c.put(`${D}/cell`,{path:t,table:a,rowid:e,column:n,value:r}),A()}catch(e){_(e.message)}},[D,E,t,a,A]),deleteRow:(0,f.useCallback)(async e=>{if(a)try{E?await c.del(`${D}/row`,{table:a,pkColumn:`rowid`,pkValue:e}):await c.del(`${D}/row`,{path:t,table:a,rowid:e}),A(),k()}catch(e){_(e.message)}},[D,E,t,a,A,k]),refreshTables:k,refreshData:A}}var m=t();function h({tables:e,selectedTable:t,onSelect:n,onRefresh:r}){return(0,m.jsxs)(`div`,{className:`w-48 shrink-0 flex flex-col bg-background overflow-hidden`,children:[(0,m.jsxs)(`div`,{className:`flex items-center justify-between px-3 py-2 border-b border-border`,children:[(0,m.jsx)(`span`,{className:`text-xs font-medium text-muted-foreground uppercase tracking-wider`,children:`Tables`}),(0,m.jsx)(`button`,{type:`button`,onClick:r,className:`text-muted-foreground hover:text-foreground transition-colors`,title:`Refresh tables`,children:(0,m.jsx)(a,{className:`size-3`})})]}),(0,m.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[e.map(e=>(0,m.jsxs)(`button`,{type:`button`,onClick:()=>n(e.name),className:`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors ${t===e.name?`bg-muted text-foreground`:`text-muted-foreground hover:bg-muted/50 hover:text-foreground`}`,children:[(0,m.jsx)(o,{className:`size-3 shrink-0`}),(0,m.jsx)(`span`,{className:`truncate flex-1`,children:e.name}),(0,m.jsx)(`span`,{className:`text-[10px] opacity-60`,children:e.rowCount})]},e.name)),e.length===0&&(0,m.jsx)(`p`,{className:`px-3 py-4 text-xs text-muted-foreground text-center`,children:`No tables found`})]})]})}function g({onExecute:e,loading:t}){return(0,m.jsx)(d,{onExecute:e,loading:t})}function _({metadata:e}){let t=e?.filePath,n=e?.projectName,i=e?.connectionId,a=e?.tableName,[o,s]=(0,f.useState)(!1);return i?(0,m.jsx)(v,{projectName:``,dbPath:``,connectionId:i,connectionName:e?.connectionName,initialTable:a,queryPanelOpen:o,onToggleQueryPanel:()=>s(e=>!e),hideTableList:!0}):!t||!n?(0,m.jsxs)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:[(0,m.jsx)(r,{className:`size-5 mr-2`}),` No database file selected.`]}):(0,m.jsx)(v,{projectName:n,dbPath:t,queryPanelOpen:o,onToggleQueryPanel:()=>s(e=>!e)})}function v({projectName:e,dbPath:t,connectionId:n,connectionName:a,initialTable:o,queryPanelOpen:s,onToggleQueryPanel:c,hideTableList:d}){let _=p(e,t,n),v=(0,f.useRef)(!1);return(0,f.useEffect)(()=>{o&&!v.current&&_.tables.length>0&&(v.current=!0,_.selectTable(o))},[o,_.tables]),_.error&&_.tables.length===0?(0,m.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,m.jsx)(l,{className:`size-10 text-destructive`}),(0,m.jsx)(`p`,{className:`text-sm`,children:_.error})]}):_.loading&&_.tables.length===0?(0,m.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,m.jsx)(u,{className:`size-5 animate-spin`}),(0,m.jsx)(`span`,{className:`text-sm`,children:`Loading database...`})]}):(0,m.jsxs)(`div`,{className:`flex h-full w-full overflow-hidden`,children:[!d&&(0,m.jsx)(h,{tables:_.tables,selectedTable:_.selectedTable,onSelect:_.selectTable,onRefresh:_.refreshTables}),(0,m.jsxs)(`div`,{className:`flex-1 flex flex-col overflow-hidden ${d?``:`border-l border-border`}`,children:[(0,m.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1.5 border-b border-border bg-background shrink-0`,children:[(0,m.jsx)(r,{className:`size-3.5 text-muted-foreground`}),(0,m.jsx)(`span`,{className:`text-xs text-muted-foreground truncate`,children:a??t}),(0,m.jsx)(`span`,{className:`text-xs text-muted-foreground`,children:_.selectedTable&&`/ ${_.selectedTable}`}),(0,m.jsx)(`div`,{className:`ml-auto`,children:(0,m.jsx)(`button`,{type:`button`,onClick:c,className:`px-2 py-1 rounded text-xs transition-colors ${s?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:`SQL`})})]}),(0,m.jsx)(`div`,{className:`flex-1 overflow-hidden ${s?`max-h-[60%]`:``}`,children:_.tableData?(0,m.jsx)(i,{columns:_.tableData.columns,rows:_.tableData.rows,total:_.tableData.total,limit:_.tableData.limit,schema:_.schema.map(e=>({name:e.name,type:e.type,nullable:!e.notnull,pk:!!e.pk,defaultValue:e.dflt_value,fk:e.fk??null})),loading:_.loading,page:_.page,onPageChange:_.setPage,onCellUpdate:(e,t,n,r)=>_.updateCell(t,n,r),onRowDelete:(e,t)=>_.deleteRow(t)}):(0,m.jsx)(`div`,{className:`flex items-center justify-center h-full text-xs text-muted-foreground`,children:_.loading?(0,m.jsx)(u,{className:`size-4 animate-spin`}):`Select a table`})}),s&&(0,m.jsx)(`div`,{className:`border-t border-border h-[40%] shrink-0`,children:(0,m.jsx)(g,{onExecute:_.executeQuery,loading:_.queryLoading})})]})]})}export{_ as SqliteViewer};
2
+ //# sourceMappingURL=sqlite-viewer-BkpONSGa.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-viewer-Bj8oPYho.js","names":[],"sources":["../../../src/web/components/sqlite/use-sqlite.ts","../../../src/web/components/sqlite/sqlite-table-list.tsx","../../../src/web/components/sqlite/sqlite-query-editor.tsx","../../../src/web/components/sqlite/sqlite-viewer.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { api, projectUrl } from \"@/lib/api-client\";\n\nexport interface TableInfo { name: string; rowCount: number }\nexport interface ColumnInfo { cid: number; name: string; type: string; notnull: boolean; pk: boolean; dflt_value: string | null; fk?: { table: string; column: string } | null }\nexport interface QueryResult { columns: string[]; rows: Record<string, unknown>[]; rowsAffected: number; changeType: \"select\" | \"modify\"; executionTimeMs?: number }\ninterface TableData { columns: string[]; rows: Record<string, unknown>[]; total: number; page: number; limit: number }\n\nexport function useSqlite(projectName: string, dbPath: string, connectionId?: number) {\n const [tables, setTables] = useState<TableInfo[]>([]);\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [tableData, setTableData] = useState<TableData | null>(null);\n const [schema, setSchema] = useState<ColumnInfo[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [page, setPage] = useState(1);\n const [queryResult, setQueryResult] = useState<QueryResult | null>(null);\n const [queryError, setQueryError] = useState<string | null>(null);\n const [queryLoading, setQueryLoading] = useState(false);\n\n // When connectionId present, use unified API; otherwise use project-scoped API\n const unifiedBase = connectionId ? `/api/db/connections/${connectionId}` : null;\n const base = unifiedBase ?? `${projectUrl(projectName)}/sqlite`;\n const qs = unifiedBase ? \"\" : `path=${encodeURIComponent(dbPath)}`;\n\n // Fetch tables on mount — use cache when connectionId (sidebar handles live sync)\n const fetchTables = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const qsPart = unifiedBase ? \"?cached=1\" : qs ? `?${qs}` : \"\";\n const data = await api.get<TableInfo[]>(`${base}/tables${qsPart}`);\n setTables(data);\n if (!unifiedBase && data.length > 0 && !selectedTable) setSelectedTable(data[0]!.name);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n }, [base, qs, unifiedBase]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => { fetchTables(); }, [fetchTables]);\n\n // Fetch table data when selection or page changes\n const fetchTableData = useCallback(async () => {\n if (!selectedTable) return;\n setLoading(true);\n try {\n const qsPrefix = qs ? `${qs}&` : \"\";\n const [data, cols] = await Promise.all([\n api.get<TableData>(`${base}/data?${qsPrefix}table=${encodeURIComponent(selectedTable)}&page=${page}&limit=100`),\n api.get<ColumnInfo[]>(`${base}/schema?${qsPrefix}table=${encodeURIComponent(selectedTable)}`),\n ]);\n setTableData(data);\n setSchema(cols);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n }, [base, qs, selectedTable, page]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => { fetchTableData(); }, [fetchTableData]);\n\n const selectTable = useCallback((name: string) => {\n setSelectedTable(name);\n setPage(1);\n setQueryResult(null);\n }, []);\n\n const executeQuery = useCallback(async (sql: string) => {\n setQueryLoading(true);\n setQueryError(null);\n try {\n const body = unifiedBase ? { sql } : { path: dbPath, sql };\n const result = await api.post<QueryResult>(`${base}/query`, body);\n setQueryResult(result);\n if (result.changeType === \"modify\") fetchTableData();\n } catch (e) {\n setQueryError((e as Error).message);\n } finally {\n setQueryLoading(false);\n }\n }, [base, unifiedBase, dbPath, fetchTableData]);\n\n const updateCell = useCallback(async (rowid: number, column: string, value: unknown) => {\n if (!selectedTable) return;\n try {\n if (unifiedBase) {\n await api.put(`${base}/cell`, { table: selectedTable, pkColumn: \"rowid\", pkValue: rowid, column, value });\n } else {\n await api.put(`${base}/cell`, { path: dbPath, table: selectedTable, rowid, column, value });\n }\n fetchTableData();\n } catch (e) {\n setError((e as Error).message);\n }\n }, [base, unifiedBase, dbPath, selectedTable, fetchTableData]);\n\n const deleteRow = useCallback(async (rowid: number) => {\n if (!selectedTable) return;\n try {\n if (unifiedBase) {\n await api.del(`${base}/row`, { table: selectedTable, pkColumn: \"rowid\", pkValue: rowid });\n } else {\n await api.del(`${base}/row`, { path: dbPath, table: selectedTable, rowid });\n }\n fetchTableData();\n fetchTables(); // refresh row counts\n } catch (e) {\n setError((e as Error).message);\n }\n }, [base, unifiedBase, dbPath, selectedTable, fetchTableData, fetchTables]);\n\n return {\n tables, selectedTable, selectTable, tableData, schema,\n loading, error, page, setPage,\n queryResult, queryError, queryLoading, executeQuery,\n updateCell, deleteRow, refreshTables: fetchTables, refreshData: fetchTableData,\n };\n}\n","import { Table, RefreshCw } from \"lucide-react\";\nimport type { TableInfo } from \"./use-sqlite\";\n\ninterface Props {\n tables: TableInfo[];\n selectedTable: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\nexport function SqliteTableList({ tables, selectedTable, onSelect, onRefresh }: Props) {\n return (\n <div className=\"w-48 shrink-0 flex flex-col bg-background overflow-hidden\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border\">\n <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider\">Tables</span>\n <button\n type=\"button\"\n onClick={onRefresh}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Refresh tables\"\n >\n <RefreshCw className=\"size-3\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n {tables.map((t) => (\n <button\n key={t.name}\n type=\"button\"\n onClick={() => onSelect(t.name)}\n className={`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors ${\n selectedTable === t.name\n ? \"bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n }`}\n >\n <Table className=\"size-3 shrink-0\" />\n <span className=\"truncate flex-1\">{t.name}</span>\n <span className=\"text-[10px] opacity-60\">{t.rowCount}</span>\n </button>\n ))}\n {tables.length === 0 && (\n <p className=\"px-3 py-4 text-xs text-muted-foreground text-center\">No tables found</p>\n )}\n </div>\n </div>\n );\n}\n","import { SqlQueryEditor } from \"../database/sql-query-editor\";\n\ninterface Props {\n onExecute: (sql: string) => void;\n loading: boolean;\n}\n\nexport function SqliteQueryEditor({ onExecute, loading }: Props) {\n return <SqlQueryEditor onExecute={onExecute} loading={loading} />;\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { Database, Loader2, AlertCircle } from \"lucide-react\";\nimport { useSqlite } from \"./use-sqlite\";\nimport { SqliteTableList } from \"./sqlite-table-list\";\nimport { GlideDataGrid } from \"../database/glide-data-grid\";\nimport { SqliteQueryEditor } from \"./sqlite-query-editor\";\n\ninterface SqliteViewerProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nexport function SqliteViewer({ metadata }: SqliteViewerProps) {\n const filePath = metadata?.filePath as string | undefined;\n const projectName = metadata?.projectName as string | undefined;\n const connectionId = metadata?.connectionId as number | undefined;\n const initialTable = metadata?.tableName as string | undefined;\n const [queryPanelOpen, setQueryPanelOpen] = useState(false);\n\n // Connection-based mode: skip file selection requirement\n if (connectionId) {\n return (\n <SqliteViewerInner\n projectName=\"\"\n dbPath=\"\"\n connectionId={connectionId}\n connectionName={metadata?.connectionName as string | undefined}\n initialTable={initialTable}\n queryPanelOpen={queryPanelOpen}\n onToggleQueryPanel={() => setQueryPanelOpen((v) => !v)}\n hideTableList\n />\n );\n }\n\n if (!filePath || !projectName) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <Database className=\"size-5 mr-2\" /> No database file selected.\n </div>\n );\n }\n\n return (\n <SqliteViewerInner\n projectName={projectName}\n dbPath={filePath}\n queryPanelOpen={queryPanelOpen}\n onToggleQueryPanel={() => setQueryPanelOpen((v) => !v)}\n />\n );\n}\n\nfunction SqliteViewerInner({\n projectName, dbPath, connectionId, connectionName, initialTable, queryPanelOpen, onToggleQueryPanel, hideTableList,\n}: {\n projectName: string; dbPath: string; connectionId?: number; connectionName?: string; initialTable?: string;\n queryPanelOpen: boolean; onToggleQueryPanel: () => void; hideTableList?: boolean;\n}) {\n const sqlite = useSqlite(projectName, dbPath, connectionId);\n\n // Jump to initial table from sidebar click\n const didInit = useRef(false);\n useEffect(() => {\n if (initialTable && !didInit.current && sqlite.tables.length > 0) {\n didInit.current = true;\n sqlite.selectTable(initialTable);\n }\n }, [initialTable, sqlite.tables]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (sqlite.error && sqlite.tables.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-text-secondary\">\n <AlertCircle className=\"size-10 text-destructive\" />\n <p className=\"text-sm\">{sqlite.error}</p>\n </div>\n );\n }\n\n if (sqlite.loading && sqlite.tables.length === 0) {\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 database...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full w-full overflow-hidden\">\n {/* Left sidebar — table list (hidden when opened from database sidebar) */}\n {!hideTableList && (\n <SqliteTableList\n tables={sqlite.tables}\n selectedTable={sqlite.selectedTable}\n onSelect={sqlite.selectTable}\n onRefresh={sqlite.refreshTables}\n />\n )}\n\n {/* Main content area */}\n <div className={`flex-1 flex flex-col overflow-hidden ${!hideTableList ? \"border-l border-border\" : \"\"}`}>\n {/* Toolbar */}\n <div className=\"flex items-center gap-2 px-3 py-1.5 border-b border-border bg-background shrink-0\">\n <Database className=\"size-3.5 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground truncate\">{connectionName ?? dbPath}</span>\n <span className=\"text-xs text-muted-foreground\">\n {sqlite.selectedTable && `/ ${sqlite.selectedTable}`}\n </span>\n <div className=\"ml-auto\">\n <button\n type=\"button\"\n onClick={onToggleQueryPanel}\n className={`px-2 py-1 rounded text-xs transition-colors ${queryPanelOpen ? \"bg-muted text-foreground\" : \"text-muted-foreground hover:text-foreground\"}`}\n >\n SQL\n </button>\n </div>\n </div>\n\n {/* Data grid — adapter from sqlite rowid-based API to GlideDataGrid's pk-based API */}\n <div className={`flex-1 overflow-hidden ${queryPanelOpen ? \"max-h-[60%]\" : \"\"}`}>\n {sqlite.tableData ? (\n <GlideDataGrid\n columns={sqlite.tableData.columns}\n rows={sqlite.tableData.rows}\n total={sqlite.tableData.total}\n limit={sqlite.tableData.limit}\n schema={sqlite.schema.map((c) => ({ name: c.name, type: c.type, nullable: !c.notnull, pk: !!c.pk, defaultValue: c.dflt_value, fk: c.fk ?? null }))}\n loading={sqlite.loading}\n page={sqlite.page}\n onPageChange={sqlite.setPage}\n onCellUpdate={(_pkCol, pkVal, col, val) => sqlite.updateCell(pkVal as number, col, val)}\n onRowDelete={(_pkCol, pkVal) => sqlite.deleteRow(pkVal as number)}\n />\n ) : (\n <div className=\"flex items-center justify-center h-full text-xs text-muted-foreground\">\n {sqlite.loading ? <Loader2 className=\"size-4 animate-spin\" /> : \"Select a table\"}\n </div>\n )}\n </div>\n\n {/* Query editor (collapsible) */}\n {queryPanelOpen && (\n <div className=\"border-t border-border h-[40%] shrink-0\">\n <SqliteQueryEditor\n onExecute={sqlite.executeQuery}\n loading={sqlite.queryLoading}\n />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-viewer-BkpONSGa.js","names":[],"sources":["../../../src/web/components/sqlite/use-sqlite.ts","../../../src/web/components/sqlite/sqlite-table-list.tsx","../../../src/web/components/sqlite/sqlite-query-editor.tsx","../../../src/web/components/sqlite/sqlite-viewer.tsx"],"sourcesContent":["import { useState, useEffect, useCallback } from \"react\";\nimport { api, projectUrl } from \"@/lib/api-client\";\n\nexport interface TableInfo { name: string; rowCount: number }\nexport interface ColumnInfo { cid: number; name: string; type: string; notnull: boolean; pk: boolean; dflt_value: string | null; fk?: { table: string; column: string } | null }\nexport interface QueryResult { columns: string[]; rows: Record<string, unknown>[]; rowsAffected: number; changeType: \"select\" | \"modify\"; executionTimeMs?: number }\ninterface TableData { columns: string[]; rows: Record<string, unknown>[]; total: number; page: number; limit: number }\n\nexport function useSqlite(projectName: string, dbPath: string, connectionId?: number) {\n const [tables, setTables] = useState<TableInfo[]>([]);\n const [selectedTable, setSelectedTable] = useState<string | null>(null);\n const [tableData, setTableData] = useState<TableData | null>(null);\n const [schema, setSchema] = useState<ColumnInfo[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [page, setPage] = useState(1);\n const [queryResult, setQueryResult] = useState<QueryResult | null>(null);\n const [queryError, setQueryError] = useState<string | null>(null);\n const [queryLoading, setQueryLoading] = useState(false);\n\n // When connectionId present, use unified API; otherwise use project-scoped API\n const unifiedBase = connectionId ? `/api/db/connections/${connectionId}` : null;\n const base = unifiedBase ?? `${projectUrl(projectName)}/sqlite`;\n const qs = unifiedBase ? \"\" : `path=${encodeURIComponent(dbPath)}`;\n\n // Fetch tables on mount — use cache when connectionId (sidebar handles live sync)\n const fetchTables = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const qsPart = unifiedBase ? \"?cached=1\" : qs ? `?${qs}` : \"\";\n const data = await api.get<TableInfo[]>(`${base}/tables${qsPart}`);\n setTables(data);\n if (!unifiedBase && data.length > 0 && !selectedTable) setSelectedTable(data[0]!.name);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n }, [base, qs, unifiedBase]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => { fetchTables(); }, [fetchTables]);\n\n // Fetch table data when selection or page changes\n const fetchTableData = useCallback(async () => {\n if (!selectedTable) return;\n setLoading(true);\n try {\n const qsPrefix = qs ? `${qs}&` : \"\";\n const [data, cols] = await Promise.all([\n api.get<TableData>(`${base}/data?${qsPrefix}table=${encodeURIComponent(selectedTable)}&page=${page}&limit=100`),\n api.get<ColumnInfo[]>(`${base}/schema?${qsPrefix}table=${encodeURIComponent(selectedTable)}`),\n ]);\n setTableData(data);\n setSchema(cols);\n } catch (e) {\n setError((e as Error).message);\n } finally {\n setLoading(false);\n }\n }, [base, qs, selectedTable, page]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useEffect(() => { fetchTableData(); }, [fetchTableData]);\n\n const selectTable = useCallback((name: string) => {\n setSelectedTable(name);\n setPage(1);\n setQueryResult(null);\n }, []);\n\n const executeQuery = useCallback(async (sql: string) => {\n setQueryLoading(true);\n setQueryError(null);\n try {\n const body = unifiedBase ? { sql } : { path: dbPath, sql };\n const result = await api.post<QueryResult>(`${base}/query`, body);\n setQueryResult(result);\n if (result.changeType === \"modify\") fetchTableData();\n } catch (e) {\n setQueryError((e as Error).message);\n } finally {\n setQueryLoading(false);\n }\n }, [base, unifiedBase, dbPath, fetchTableData]);\n\n const updateCell = useCallback(async (rowid: number, column: string, value: unknown) => {\n if (!selectedTable) return;\n try {\n if (unifiedBase) {\n await api.put(`${base}/cell`, { table: selectedTable, pkColumn: \"rowid\", pkValue: rowid, column, value });\n } else {\n await api.put(`${base}/cell`, { path: dbPath, table: selectedTable, rowid, column, value });\n }\n fetchTableData();\n } catch (e) {\n setError((e as Error).message);\n }\n }, [base, unifiedBase, dbPath, selectedTable, fetchTableData]);\n\n const deleteRow = useCallback(async (rowid: number) => {\n if (!selectedTable) return;\n try {\n if (unifiedBase) {\n await api.del(`${base}/row`, { table: selectedTable, pkColumn: \"rowid\", pkValue: rowid });\n } else {\n await api.del(`${base}/row`, { path: dbPath, table: selectedTable, rowid });\n }\n fetchTableData();\n fetchTables(); // refresh row counts\n } catch (e) {\n setError((e as Error).message);\n }\n }, [base, unifiedBase, dbPath, selectedTable, fetchTableData, fetchTables]);\n\n return {\n tables, selectedTable, selectTable, tableData, schema,\n loading, error, page, setPage,\n queryResult, queryError, queryLoading, executeQuery,\n updateCell, deleteRow, refreshTables: fetchTables, refreshData: fetchTableData,\n };\n}\n","import { Table, RefreshCw } from \"lucide-react\";\nimport type { TableInfo } from \"./use-sqlite\";\n\ninterface Props {\n tables: TableInfo[];\n selectedTable: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\nexport function SqliteTableList({ tables, selectedTable, onSelect, onRefresh }: Props) {\n return (\n <div className=\"w-48 shrink-0 flex flex-col bg-background overflow-hidden\">\n <div className=\"flex items-center justify-between px-3 py-2 border-b border-border\">\n <span className=\"text-xs font-medium text-muted-foreground uppercase tracking-wider\">Tables</span>\n <button\n type=\"button\"\n onClick={onRefresh}\n className=\"text-muted-foreground hover:text-foreground transition-colors\"\n title=\"Refresh tables\"\n >\n <RefreshCw className=\"size-3\" />\n </button>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n {tables.map((t) => (\n <button\n key={t.name}\n type=\"button\"\n onClick={() => onSelect(t.name)}\n className={`w-full flex items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors ${\n selectedTable === t.name\n ? \"bg-muted text-foreground\"\n : \"text-muted-foreground hover:bg-muted/50 hover:text-foreground\"\n }`}\n >\n <Table className=\"size-3 shrink-0\" />\n <span className=\"truncate flex-1\">{t.name}</span>\n <span className=\"text-[10px] opacity-60\">{t.rowCount}</span>\n </button>\n ))}\n {tables.length === 0 && (\n <p className=\"px-3 py-4 text-xs text-muted-foreground text-center\">No tables found</p>\n )}\n </div>\n </div>\n );\n}\n","import { SqlQueryEditor } from \"../database/sql-query-editor\";\n\ninterface Props {\n onExecute: (sql: string) => void;\n loading: boolean;\n}\n\nexport function SqliteQueryEditor({ onExecute, loading }: Props) {\n return <SqlQueryEditor onExecute={onExecute} loading={loading} />;\n}\n","import { useState, useEffect, useRef, useCallback } from \"react\";\nimport { Database, Loader2, AlertCircle } from \"lucide-react\";\nimport { useSqlite } from \"./use-sqlite\";\nimport { SqliteTableList } from \"./sqlite-table-list\";\nimport { GlideDataGrid } from \"../database/glide-data-grid\";\nimport { SqliteQueryEditor } from \"./sqlite-query-editor\";\n\ninterface SqliteViewerProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nexport function SqliteViewer({ metadata }: SqliteViewerProps) {\n const filePath = metadata?.filePath as string | undefined;\n const projectName = metadata?.projectName as string | undefined;\n const connectionId = metadata?.connectionId as number | undefined;\n const initialTable = metadata?.tableName as string | undefined;\n const [queryPanelOpen, setQueryPanelOpen] = useState(false);\n\n // Connection-based mode: skip file selection requirement\n if (connectionId) {\n return (\n <SqliteViewerInner\n projectName=\"\"\n dbPath=\"\"\n connectionId={connectionId}\n connectionName={metadata?.connectionName as string | undefined}\n initialTable={initialTable}\n queryPanelOpen={queryPanelOpen}\n onToggleQueryPanel={() => setQueryPanelOpen((v) => !v)}\n hideTableList\n />\n );\n }\n\n if (!filePath || !projectName) {\n return (\n <div className=\"flex items-center justify-center h-full text-text-secondary text-sm\">\n <Database className=\"size-5 mr-2\" /> No database file selected.\n </div>\n );\n }\n\n return (\n <SqliteViewerInner\n projectName={projectName}\n dbPath={filePath}\n queryPanelOpen={queryPanelOpen}\n onToggleQueryPanel={() => setQueryPanelOpen((v) => !v)}\n />\n );\n}\n\nfunction SqliteViewerInner({\n projectName, dbPath, connectionId, connectionName, initialTable, queryPanelOpen, onToggleQueryPanel, hideTableList,\n}: {\n projectName: string; dbPath: string; connectionId?: number; connectionName?: string; initialTable?: string;\n queryPanelOpen: boolean; onToggleQueryPanel: () => void; hideTableList?: boolean;\n}) {\n const sqlite = useSqlite(projectName, dbPath, connectionId);\n\n // Jump to initial table from sidebar click\n const didInit = useRef(false);\n useEffect(() => {\n if (initialTable && !didInit.current && sqlite.tables.length > 0) {\n didInit.current = true;\n sqlite.selectTable(initialTable);\n }\n }, [initialTable, sqlite.tables]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (sqlite.error && sqlite.tables.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-text-secondary\">\n <AlertCircle className=\"size-10 text-destructive\" />\n <p className=\"text-sm\">{sqlite.error}</p>\n </div>\n );\n }\n\n if (sqlite.loading && sqlite.tables.length === 0) {\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 database...</span>\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full w-full overflow-hidden\">\n {/* Left sidebar — table list (hidden when opened from database sidebar) */}\n {!hideTableList && (\n <SqliteTableList\n tables={sqlite.tables}\n selectedTable={sqlite.selectedTable}\n onSelect={sqlite.selectTable}\n onRefresh={sqlite.refreshTables}\n />\n )}\n\n {/* Main content area */}\n <div className={`flex-1 flex flex-col overflow-hidden ${!hideTableList ? \"border-l border-border\" : \"\"}`}>\n {/* Toolbar */}\n <div className=\"flex items-center gap-2 px-3 py-1.5 border-b border-border bg-background shrink-0\">\n <Database className=\"size-3.5 text-muted-foreground\" />\n <span className=\"text-xs text-muted-foreground truncate\">{connectionName ?? dbPath}</span>\n <span className=\"text-xs text-muted-foreground\">\n {sqlite.selectedTable && `/ ${sqlite.selectedTable}`}\n </span>\n <div className=\"ml-auto\">\n <button\n type=\"button\"\n onClick={onToggleQueryPanel}\n className={`px-2 py-1 rounded text-xs transition-colors ${queryPanelOpen ? \"bg-muted text-foreground\" : \"text-muted-foreground hover:text-foreground\"}`}\n >\n SQL\n </button>\n </div>\n </div>\n\n {/* Data grid — adapter from sqlite rowid-based API to GlideDataGrid's pk-based API */}\n <div className={`flex-1 overflow-hidden ${queryPanelOpen ? \"max-h-[60%]\" : \"\"}`}>\n {sqlite.tableData ? (\n <GlideDataGrid\n columns={sqlite.tableData.columns}\n rows={sqlite.tableData.rows}\n total={sqlite.tableData.total}\n limit={sqlite.tableData.limit}\n schema={sqlite.schema.map((c) => ({ name: c.name, type: c.type, nullable: !c.notnull, pk: !!c.pk, defaultValue: c.dflt_value, fk: c.fk ?? null }))}\n loading={sqlite.loading}\n page={sqlite.page}\n onPageChange={sqlite.setPage}\n onCellUpdate={(_pkCol, pkVal, col, val) => sqlite.updateCell(pkVal as number, col, val)}\n onRowDelete={(_pkCol, pkVal) => sqlite.deleteRow(pkVal as number)}\n />\n ) : (\n <div className=\"flex items-center justify-center h-full text-xs text-muted-foreground\">\n {sqlite.loading ? <Loader2 className=\"size-4 animate-spin\" /> : \"Select a table\"}\n </div>\n )}\n </div>\n\n {/* Query editor (collapsible) */}\n {queryPanelOpen && (\n <div className=\"border-t border-border h-[40%] shrink-0\">\n <SqliteQueryEditor\n onExecute={sqlite.executeQuery}\n loading={sqlite.queryLoading}\n />\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"6rBAQA,SAAgB,EAAU,EAAqB,EAAgB,EAAuB,CACpF,GAAM,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAmC,EAAE,CAAC,CAC/C,CAAC,EAAe,IAAA,EAAA,EAAA,UAA4C,KAAK,CACjE,CAAC,EAAW,IAAA,EAAA,EAAA,UAA2C,KAAK,CAC5D,CAAC,EAAQ,IAAA,EAAA,EAAA,UAAoC,EAAE,CAAC,CAChD,CAAC,EAAS,IAAA,EAAA,EAAA,UAAuB,GAAM,CACvC,CAAC,EAAO,IAAA,EAAA,EAAA,UAAoC,KAAK,CACjD,CAAC,EAAM,IAAA,EAAA,EAAA,UAAoB,EAAE,CAC7B,CAAC,EAAa,IAAA,EAAA,EAAA,UAA+C,KAAK,CAClE,CAAC,EAAY,IAAA,EAAA,EAAA,UAAyC,KAAK,CAC3D,CAAC,EAAc,IAAA,EAAA,EAAA,UAA4B,GAAM,CAGjD,EAAc,EAAe,uBAAuB,IAAiB,KACrE,EAAO,GAAe,GAAG,EAAW,EAAY,CAAC,SACjD,EAAK,EAAc,GAAK,QAAQ,mBAAmB,EAAO,GAG1D,GAAA,EAAA,EAAA,aAA0B,SAAY,CAC1C,EAAW,GAAK,CAChB,EAAS,KAAK,CACd,GAAI,CACF,IAAM,EAAS,EAAc,YAAc,EAAK,IAAI,IAAO,GACrD,EAAO,MAAM,EAAI,IAAiB,GAAG,EAAK,SAAS,IAAS,CAClE,EAAU,EAAK,CACX,CAAC,GAAe,EAAK,OAAS,GAAK,CAAC,GAAe,EAAiB,EAAK,GAAI,KAAK,OAC/E,EAAG,CACV,EAAU,EAAY,QAAQ,QACtB,CACR,EAAW,GAAM,GAElB,CAAC,EAAM,EAAI,EAAY,CAAC,EAE3B,EAAA,EAAA,eAAgB,CAAE,GAAa,EAAK,CAAC,EAAY,CAAC,CAGlD,IAAM,GAAA,EAAA,EAAA,aAA6B,SAAY,CACxC,KACL,GAAW,GAAK,CAChB,GAAI,CACF,IAAM,EAAW,EAAK,GAAG,EAAG,GAAK,GAC3B,CAAC,EAAM,GAAQ,MAAM,QAAQ,IAAI,CACrC,EAAI,IAAe,GAAG,EAAK,QAAQ,EAAS,QAAQ,mBAAmB,EAAc,CAAC,QAAQ,EAAK,YAAY,CAC/G,EAAI,IAAkB,GAAG,EAAK,UAAU,EAAS,QAAQ,mBAAmB,EAAc,GAAG,CAC9F,CAAC,CACF,EAAa,EAAK,CAClB,EAAU,EAAK,OACR,EAAG,CACV,EAAU,EAAY,QAAQ,QACtB,CACR,EAAW,GAAM,IAElB,CAAC,EAAM,EAAI,EAAe,EAAK,CAAC,CAsDnC,OApDA,EAAA,EAAA,eAAgB,CAAE,GAAgB,EAAK,CAAC,EAAe,CAAC,CAoDjD,CACL,SAAQ,gBAAe,aAAA,EAAA,EAAA,aAnDQ,GAAiB,CAChD,EAAiB,EAAK,CACtB,EAAQ,EAAE,CACV,EAAe,KAAK,EACnB,EAAE,CAAC,CA+CgC,YAAW,SAC/C,UAAS,QAAO,OAAM,UACtB,cAAa,aAAY,eAAc,cAAA,EAAA,EAAA,aA/CR,KAAO,IAAgB,CACtD,EAAgB,GAAK,CACrB,EAAc,KAAK,CACnB,GAAI,CACF,IAAM,EAAO,EAAc,CAAE,MAAK,CAAG,CAAE,KAAM,EAAQ,MAAK,CACpD,EAAS,MAAM,EAAI,KAAkB,GAAG,EAAK,QAAS,EAAK,CACjE,EAAe,EAAO,CAClB,EAAO,aAAe,UAAU,GAAgB,OAC7C,EAAG,CACV,EAAe,EAAY,QAAQ,QAC3B,CACR,EAAgB,GAAM,GAEvB,CAAC,EAAM,EAAa,EAAQ,EAAe,CAAC,CAmC7C,YAAA,EAAA,EAAA,aAjC6B,MAAO,EAAe,EAAgB,IAAmB,CACjF,KACL,GAAI,CACE,EACF,MAAM,EAAI,IAAI,GAAG,EAAK,OAAQ,CAAE,MAAO,EAAe,SAAU,QAAS,QAAS,EAAO,SAAQ,QAAO,CAAC,CAEzG,MAAM,EAAI,IAAI,GAAG,EAAK,OAAQ,CAAE,KAAM,EAAQ,MAAO,EAAe,QAAO,SAAQ,QAAO,CAAC,CAE7F,GAAgB,OACT,EAAG,CACV,EAAU,EAAY,QAAQ,GAE/B,CAAC,EAAM,EAAa,EAAQ,EAAe,EAAe,CAAC,CAqBhD,WAAA,EAAA,EAAA,aAnBgB,KAAO,IAAkB,CAChD,KACL,GAAI,CACE,EACF,MAAM,EAAI,IAAI,GAAG,EAAK,MAAO,CAAE,MAAO,EAAe,SAAU,QAAS,QAAS,EAAO,CAAC,CAEzF,MAAM,EAAI,IAAI,GAAG,EAAK,MAAO,CAAE,KAAM,EAAQ,MAAO,EAAe,QAAO,CAAC,CAE7E,GAAgB,CAChB,GAAa,OACN,EAAG,CACV,EAAU,EAAY,QAAQ,GAE/B,CAAC,EAAM,EAAa,EAAQ,EAAe,EAAgB,EAAY,CAAC,CAMlD,cAAe,EAAa,YAAa,EACjE,WC7GH,SAAgB,EAAgB,CAAE,SAAQ,gBAAe,WAAU,aAAoB,CACrF,OAAA,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,qEAAf,EAAA,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,8EAAf,EAAA,EAAA,EAAA,KACG,OAAD,CAAM,UAAU,8EAAqE,SAAa,CAAA,EAAA,EAAA,EAAA,KACjG,SAAD,CACE,KAAK,SACL,QAAS,EACT,UAAU,gEACV,MAAM,oCAEL,EAAD,CAAW,UAAU,SAAW,CAAA,CACzB,CAAA,CACL,cACL,MAAD,CAAK,UAAU,kCAAf,CACG,EAAO,IAAK,IAAA,EAAA,EAAA,MACV,SAAD,CAEE,KAAK,SACL,YAAe,EAAS,EAAE,KAAK,CAC/B,UAAW,kFACT,IAAkB,EAAE,KAChB,2BACA,2EAPR,WAUG,EAAD,CAAO,UAAU,kBAAoB,CAAA,WACpC,OAAD,CAAM,UAAU,2BAAmB,EAAE,KAAY,CAAA,WAChD,OAAD,CAAM,UAAU,kCAA0B,EAAE,SAAgB,CAAA,CACrD,EAZF,EAAE,KAYA,CACT,CACD,EAAO,SAAW,IAAA,EAAA,EAAA,KAChB,IAAD,CAAG,UAAU,+DAAsD,kBAAmB,CAAA,CAEpF,GACF,GCtCV,SAAgB,EAAkB,CAAE,YAAW,WAAkB,CAC/D,OAAA,EAAA,EAAA,KAAQ,EAAD,CAA2B,YAAoB,UAAW,CAAA,CCInE,SAAgB,EAAa,CAAE,YAA+B,CAC5D,IAAM,EAAW,GAAU,SACrB,EAAc,GAAU,YACxB,EAAe,GAAU,aACzB,EAAe,GAAU,UACzB,CAAC,EAAgB,IAAA,EAAA,EAAA,UAA8B,GAAM,CA0B3D,OAvBI,GACF,EAAA,EAAA,KACG,EAAD,CACE,YAAY,GACZ,OAAO,GACO,eACd,eAAgB,GAAU,eACZ,eACE,iBAChB,uBAA0B,EAAmB,GAAM,CAAC,EAAE,CACtD,cAAA,GACA,CAAA,CAIF,CAAC,GAAY,CAAC,GAChB,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,+EAAf,EAAA,EAAA,EAAA,KACG,EAAD,CAAU,UAAU,cAAgB,CAAA,CAAA,8BAChC,IAIV,EAAA,EAAA,KACG,EAAD,CACe,cACb,OAAQ,EACQ,iBAChB,uBAA0B,EAAmB,GAAM,CAAC,EAAE,CACtD,CAAA,CAIN,SAAS,EAAkB,CACzB,cAAa,SAAQ,eAAc,iBAAgB,eAAc,iBAAgB,qBAAoB,iBAIpG,CACD,IAAM,EAAS,EAAU,EAAa,EAAQ,EAAa,CAGrD,GAAA,EAAA,EAAA,QAAiB,GAAM,CA0B7B,OAzBA,EAAA,EAAA,eAAgB,CACV,GAAgB,CAAC,EAAQ,SAAW,EAAO,OAAO,OAAS,IAC7D,EAAQ,QAAU,GAClB,EAAO,YAAY,EAAa,GAEjC,CAAC,EAAc,EAAO,OAAO,CAAC,CAE7B,EAAO,OAAS,EAAO,OAAO,SAAW,GAC3C,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,sFAAf,EAAA,EAAA,EAAA,KACG,EAAD,CAAa,UAAU,2BAA6B,CAAA,EAAA,EAAA,EAAA,KACnD,IAAD,CAAG,UAAU,mBAAW,EAAO,MAAU,CAAA,CACrC,GAIN,EAAO,SAAW,EAAO,OAAO,SAAW,GAC7C,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,6EAAf,EAAA,EAAA,EAAA,KACG,EAAD,CAAS,UAAU,sBAAwB,CAAA,EAAA,EAAA,EAAA,KAC1C,OAAD,CAAM,UAAU,mBAAU,sBAA0B,CAAA,CAChD,IAIV,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,8CAAf,CAEG,CAAC,IAAA,EAAA,EAAA,KACC,EAAD,CACE,OAAQ,EAAO,OACf,cAAe,EAAO,cACtB,SAAU,EAAO,YACjB,UAAW,EAAO,cAClB,CAAA,EAAA,EAAA,EAAA,MAIH,MAAD,CAAK,UAAW,wCAAyC,EAA2C,GAA3B,oCAAzE,YAEG,MAAD,CAAK,UAAU,6FAAf,WACG,EAAD,CAAU,UAAU,iCAAmC,CAAA,WACtD,OAAD,CAAM,UAAU,kDAA0C,GAAkB,EAAc,CAAA,WACzF,OAAD,CAAM,UAAU,yCACb,EAAO,eAAiB,KAAK,EAAO,gBAChC,CAAA,WACN,MAAD,CAAK,UAAU,6BACZ,SAAD,CACE,KAAK,SACL,QAAS,EACT,UAAW,+CAA+C,EAAiB,2BAA6B,yDACzG,MAEQ,CAAA,CACL,CAAA,CACF,aAGL,MAAD,CAAK,UAAW,0BAA0B,EAAiB,cAAgB,cACxE,EAAO,WAAA,EAAA,EAAA,KACL,EAAD,CACE,QAAS,EAAO,UAAU,QAC1B,KAAM,EAAO,UAAU,KACvB,MAAO,EAAO,UAAU,MACxB,MAAO,EAAO,UAAU,MACxB,OAAQ,EAAO,OAAO,IAAK,IAAO,CAAE,KAAM,EAAE,KAAM,KAAM,EAAE,KAAM,SAAU,CAAC,EAAE,QAAS,GAAI,CAAC,CAAC,EAAE,GAAI,aAAc,EAAE,WAAY,GAAI,EAAE,IAAM,KAAM,EAAE,CAClJ,QAAS,EAAO,QAChB,KAAM,EAAO,KACb,aAAc,EAAO,QACrB,cAAe,EAAQ,EAAO,EAAK,IAAQ,EAAO,WAAW,EAAiB,EAAK,EAAI,CACvF,aAAc,EAAQ,IAAU,EAAO,UAAU,EAAgB,CACjE,CAAA,EAAA,EAAA,EAAA,KAED,MAAD,CAAK,UAAU,iFACZ,EAAO,SAAA,EAAA,EAAA,KAAW,EAAD,CAAS,UAAU,sBAAwB,CAAA,CAAG,iBAC5D,CAAA,CAEJ,CAAA,CAGL,IAAA,EAAA,EAAA,KACE,MAAD,CAAK,UAAU,6DACZ,EAAD,CACE,UAAW,EAAO,aAClB,QAAS,EAAO,aAChB,CAAA,CACE,CAAA,CAEJ,GACF"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"./react-DMIOAtcX.js";import{a as t,t as n}from"./panel-store-Dy8-7E_g.js";var r=e()(()=>({tabs:[],activeTabId:null,tabHistory:[],currentProject:null,switchProject:e=>{n.getState().switchProject(e),i()},openTab:e=>{let t=n.getState().openTab(e);return i(),t},closeTab:e=>{n.getState().closeTab(e),i()},setActiveTab:e=>{n.getState().setActiveTab(e),i()},updateTab:(e,t)=>{n.getState().updateTab(e,t),i()},openNewFile:()=>{let e=n.getState(),r=t(e.panels),a=e.openTab({type:`editor`,title:`Untitled-${r}`,projectId:null,metadata:{isUntitled:!0,untitledNumber:r},closable:!0});return i(),a}}));function i(){let e=n.getState(),t=e.panels[e.focusedPanelId];r.setState({tabs:t?.tabs??[],activeTabId:t?.activeTabId??null,tabHistory:t?.tabHistory??[],currentProject:e.currentProject})}n.subscribe(()=>i());export{r as t};
2
- //# sourceMappingURL=tab-store-Dtg1_qL0.js.map
1
+ import{t as e}from"./react-DMIOAtcX.js";import{a as t,t as n}from"./panel-store-C8wwxBpn.js";var r=e()(()=>({tabs:[],activeTabId:null,tabHistory:[],currentProject:null,switchProject:e=>{n.getState().switchProject(e),i()},openTab:e=>{let t=n.getState().openTab(e);return i(),t},closeTab:e=>{n.getState().closeTab(e),i()},setActiveTab:e=>{n.getState().setActiveTab(e),i()},updateTab:(e,t)=>{n.getState().updateTab(e,t),i()},openNewFile:()=>{let e=n.getState(),r=t(e.panels),a=e.openTab({type:`editor`,title:`Untitled-${r}`,projectId:null,metadata:{isUntitled:!0,untitledNumber:r},closable:!0});return i(),a}}));function i(){let e=n.getState(),t=e.panels[e.focusedPanelId];r.setState({tabs:t?.tabs??[],activeTabId:t?.activeTabId??null,tabHistory:t?.tabHistory??[],currentProject:e.currentProject})}n.subscribe(()=>i());export{r as t};
2
+ //# sourceMappingURL=tab-store-CNas5Ny8.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tab-store-Dtg1_qL0.js","names":[],"sources":["../../../src/web/stores/tab-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { usePanelStore } from \"./panel-store\";\nimport { getNextUntitledNumber } from \"./panel-utils\";\n\nexport type TabType =\n | \"terminal\"\n | \"chat\"\n | \"editor\"\n | \"database\"\n | \"sqlite\"\n | \"postgres\"\n | \"git-diff\"\n | \"settings\"\n | \"ports\"\n | \"extension\"\n | \"extension-webview\"\n | \"conflict-editor\";\n\nexport interface Tab {\n id: string;\n type: TabType;\n title: string;\n projectId: string | null;\n metadata?: Record<string, unknown>;\n closable: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Facade store — delegates to panel-store, exposes focused panel's tabs\n// ---------------------------------------------------------------------------\ninterface TabStore {\n /** Tabs of the focused panel */\n tabs: Tab[];\n /** Active tab in focused panel */\n activeTabId: string | null;\n tabHistory: string[];\n currentProject: string | null;\n switchProject: (projectName: string) => void;\n openTab: (tab: Omit<Tab, \"id\">) => string;\n closeTab: (id: string) => void;\n setActiveTab: (id: string) => void;\n updateTab: (id: string, updates: Partial<Omit<Tab, \"id\">>) => void;\n openNewFile: () => string;\n}\n\nexport const useTabStore = create<TabStore>()(() => ({\n tabs: [],\n activeTabId: null,\n tabHistory: [],\n currentProject: null,\n\n switchProject: (projectName: string) => {\n usePanelStore.getState().switchProject(projectName);\n syncFromPanelStore();\n },\n\n openTab: (tabDef) => {\n const id = usePanelStore.getState().openTab(tabDef);\n syncFromPanelStore();\n return id;\n },\n\n closeTab: (id) => {\n usePanelStore.getState().closeTab(id);\n syncFromPanelStore();\n },\n\n setActiveTab: (id) => {\n usePanelStore.getState().setActiveTab(id);\n syncFromPanelStore();\n },\n\n updateTab: (id, updates) => {\n usePanelStore.getState().updateTab(id, updates);\n syncFromPanelStore();\n },\n\n openNewFile: () => {\n const ps = usePanelStore.getState();\n const num = getNextUntitledNumber(ps.panels);\n const id = ps.openTab({\n type: \"editor\",\n title: `Untitled-${num}`,\n projectId: null,\n metadata: { isUntitled: true, untitledNumber: num },\n closable: true,\n });\n syncFromPanelStore();\n return id;\n },\n}));\n\n// ---------------------------------------------------------------------------\n// Sync focused panel state → tab-store for backward compat\n// ---------------------------------------------------------------------------\nfunction syncFromPanelStore() {\n const ps = usePanelStore.getState();\n const focused = ps.panels[ps.focusedPanelId];\n useTabStore.setState({\n tabs: focused?.tabs ?? [],\n activeTabId: focused?.activeTabId ?? null,\n tabHistory: focused?.tabHistory ?? [],\n currentProject: ps.currentProject,\n });\n}\n\n// Subscribe to panel-store changes to keep tab-store in sync\nusePanelStore.subscribe(() => syncFromPanelStore());\n"],"mappings":"6FA6CA,IAAa,EAAc,GAAkB,MAAQ,CACnD,KAAM,EAAE,CACR,YAAa,KACb,WAAY,EAAE,CACd,eAAgB,KAEhB,cAAgB,GAAwB,CACtC,EAAc,UAAU,CAAC,cAAc,EAAY,CACnD,GAAoB,EAGtB,QAAU,GAAW,CACnB,IAAM,EAAK,EAAc,UAAU,CAAC,QAAQ,EAAO,CAEnD,OADA,GAAoB,CACb,GAGT,SAAW,GAAO,CAChB,EAAc,UAAU,CAAC,SAAS,EAAG,CACrC,GAAoB,EAGtB,aAAe,GAAO,CACpB,EAAc,UAAU,CAAC,aAAa,EAAG,CACzC,GAAoB,EAGtB,WAAY,EAAI,IAAY,CAC1B,EAAc,UAAU,CAAC,UAAU,EAAI,EAAQ,CAC/C,GAAoB,EAGtB,gBAAmB,CACjB,IAAM,EAAK,EAAc,UAAU,CAC7B,EAAM,EAAsB,EAAG,OAAO,CACtC,EAAK,EAAG,QAAQ,CACpB,KAAM,SACN,MAAO,YAAY,IACnB,UAAW,KACX,SAAU,CAAE,WAAY,GAAM,eAAgB,EAAK,CACnD,SAAU,GACX,CAAC,CAEF,OADA,GAAoB,CACb,GAEV,EAAE,CAKH,SAAS,GAAqB,CAC5B,IAAM,EAAK,EAAc,UAAU,CAC7B,EAAU,EAAG,OAAO,EAAG,gBAC7B,EAAY,SAAS,CACnB,KAAM,GAAS,MAAQ,EAAE,CACzB,YAAa,GAAS,aAAe,KACrC,WAAY,GAAS,YAAc,EAAE,CACrC,eAAgB,EAAG,eACpB,CAAC,CAIJ,EAAc,cAAgB,GAAoB,CAAC"}
1
+ {"version":3,"file":"tab-store-CNas5Ny8.js","names":[],"sources":["../../../src/web/stores/tab-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { usePanelStore } from \"./panel-store\";\nimport { getNextUntitledNumber } from \"./panel-utils\";\n\nexport type TabType =\n | \"terminal\"\n | \"chat\"\n | \"editor\"\n | \"database\"\n | \"sqlite\"\n | \"postgres\"\n | \"git-diff\"\n | \"settings\"\n | \"ports\"\n | \"extension\"\n | \"extension-webview\"\n | \"conflict-editor\";\n\nexport interface Tab {\n id: string;\n type: TabType;\n title: string;\n projectId: string | null;\n metadata?: Record<string, unknown>;\n closable: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Facade store — delegates to panel-store, exposes focused panel's tabs\n// ---------------------------------------------------------------------------\ninterface TabStore {\n /** Tabs of the focused panel */\n tabs: Tab[];\n /** Active tab in focused panel */\n activeTabId: string | null;\n tabHistory: string[];\n currentProject: string | null;\n switchProject: (projectName: string) => void;\n openTab: (tab: Omit<Tab, \"id\">) => string;\n closeTab: (id: string) => void;\n setActiveTab: (id: string) => void;\n updateTab: (id: string, updates: Partial<Omit<Tab, \"id\">>) => void;\n openNewFile: () => string;\n}\n\nexport const useTabStore = create<TabStore>()(() => ({\n tabs: [],\n activeTabId: null,\n tabHistory: [],\n currentProject: null,\n\n switchProject: (projectName: string) => {\n usePanelStore.getState().switchProject(projectName);\n syncFromPanelStore();\n },\n\n openTab: (tabDef) => {\n const id = usePanelStore.getState().openTab(tabDef);\n syncFromPanelStore();\n return id;\n },\n\n closeTab: (id) => {\n usePanelStore.getState().closeTab(id);\n syncFromPanelStore();\n },\n\n setActiveTab: (id) => {\n usePanelStore.getState().setActiveTab(id);\n syncFromPanelStore();\n },\n\n updateTab: (id, updates) => {\n usePanelStore.getState().updateTab(id, updates);\n syncFromPanelStore();\n },\n\n openNewFile: () => {\n const ps = usePanelStore.getState();\n const num = getNextUntitledNumber(ps.panels);\n const id = ps.openTab({\n type: \"editor\",\n title: `Untitled-${num}`,\n projectId: null,\n metadata: { isUntitled: true, untitledNumber: num },\n closable: true,\n });\n syncFromPanelStore();\n return id;\n },\n}));\n\n// ---------------------------------------------------------------------------\n// Sync focused panel state → tab-store for backward compat\n// ---------------------------------------------------------------------------\nfunction syncFromPanelStore() {\n const ps = usePanelStore.getState();\n const focused = ps.panels[ps.focusedPanelId];\n useTabStore.setState({\n tabs: focused?.tabs ?? [],\n activeTabId: focused?.activeTabId ?? null,\n tabHistory: focused?.tabHistory ?? [],\n currentProject: ps.currentProject,\n });\n}\n\n// Subscribe to panel-store changes to keep tab-store in sync\nusePanelStore.subscribe(() => syncFromPanelStore());\n"],"mappings":"6FA6CA,IAAa,EAAc,GAAkB,MAAQ,CACnD,KAAM,EAAE,CACR,YAAa,KACb,WAAY,EAAE,CACd,eAAgB,KAEhB,cAAgB,GAAwB,CACtC,EAAc,UAAU,CAAC,cAAc,EAAY,CACnD,GAAoB,EAGtB,QAAU,GAAW,CACnB,IAAM,EAAK,EAAc,UAAU,CAAC,QAAQ,EAAO,CAEnD,OADA,GAAoB,CACb,GAGT,SAAW,GAAO,CAChB,EAAc,UAAU,CAAC,SAAS,EAAG,CACrC,GAAoB,EAGtB,aAAe,GAAO,CACpB,EAAc,UAAU,CAAC,aAAa,EAAG,CACzC,GAAoB,EAGtB,WAAY,EAAI,IAAY,CAC1B,EAAc,UAAU,CAAC,UAAU,EAAI,EAAQ,CAC/C,GAAoB,EAGtB,gBAAmB,CACjB,IAAM,EAAK,EAAc,UAAU,CAC7B,EAAM,EAAsB,EAAG,OAAO,CACtC,EAAK,EAAG,QAAQ,CACpB,KAAM,SACN,MAAO,YAAY,IACnB,UAAW,KACX,SAAU,CAAE,WAAY,GAAM,eAAgB,EAAK,CACnD,SAAU,GACX,CAAC,CAEF,OADA,GAAoB,CACb,GAEV,EAAE,CAKH,SAAS,GAAqB,CAC5B,IAAM,EAAK,EAAc,UAAU,CAC7B,EAAU,EAAG,OAAO,EAAG,gBAC7B,EAAY,SAAS,CACnB,KAAM,GAAS,MAAQ,EAAE,CACzB,YAAa,GAAS,aAAe,KACrC,WAAY,GAAS,YAAc,EAAE,CACrC,eAAgB,EAAG,eACpB,CAAC,CAIJ,EAAc,cAAgB,GAAoB,CAAC"}
@@ -1,4 +1,4 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import"./api-client-DIhJ5qVW.js";import{n as r}from"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import{n as i}from"./utils-CQux7CsO.js";import{t as a}from"./panel-store-Dy8-7E_g.js";import{F as o,K as s,U as c,nt as l,rt as u}from"./index-DkQ6jVSH.js";import{n as d,r as f,t as p}from"./vendor-xterm-D1P36hcr.js";var m=e(n(),1),h={background:`#0f1419`,foreground:`#e5e7eb`,cursor:`#e5e7eb`,selectionBackground:`#3b82f640`,black:`#1a1f2e`,red:`#ef4444`,green:`#10b981`,yellow:`#f59e0b`,blue:`#3b82f6`,magenta:`#a855f7`,cyan:`#06b6d4`,white:`#e5e7eb`,brightBlack:`#6b7280`,brightRed:`#f87171`,brightGreen:`#34d399`,brightYellow:`#fbbf24`,brightBlue:`#60a5fa`,brightMagenta:`#c084fc`,brightCyan:`#22d3ee`,brightWhite:`#f9fafb`},g={background:`#ffffff`,foreground:`#1a1f2e`,cursor:`#1a1f2e`,selectionBackground:`#2563eb30`,black:`#1a1f2e`,red:`#dc2626`,green:`#059669`,yellow:`#d97706`,blue:`#2563eb`,magenta:`#9333ea`,cyan:`#0891b2`,white:`#f8fafc`,brightBlack:`#64748b`,brightRed:`#ef4444`,brightGreen:`#10b981`,brightYellow:`#f59e0b`,brightBlue:`#3b82f6`,brightMagenta:`#a855f7`,brightCyan:`#06b6d4`,brightWhite:`#ffffff`};function _(e){return e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?h:g:e===`light`?g:h}var v=`RESIZE:`;function y(e){let{sessionId:t,containerRef:n}=e,i=(0,m.useRef)(null),a=(0,m.useRef)(null),o=(0,m.useRef)(null),s=(0,m.useRef)(null),c=(0,m.useRef)(0),[l,u]=(0,m.useState)(!1),[h,g]=(0,m.useState)(!1),[y,b]=(0,m.useState)(!1),x=e.tabId?`ppm:terminal-session:${e.tabId}`:null,S=(0,m.useRef)((()=>{if(x)try{return localStorage.getItem(x)??t}catch{}return t})()),C=(0,m.useRef)(0),w=(0,m.useCallback)(e=>{let t=o.current;t?.readyState===WebSocket.OPEN&&t.send(e)},[]),T=(0,m.useCallback)(()=>i.current?.getSelection()??``,[]),E=(0,m.useCallback)(()=>{let e=i.current;if(!e)return``;let t=e.buffer.active,n=C.current,r=t.baseY+t.cursorY,a=[];for(let e=n;e<=r;e++){let n=t.getLine(e);n&&a.push(n.translateToString(!0))}for(;a.length>0&&a[a.length-1].trim()===``;)a.pop();return a.join(`
1
+ import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-UXCWAcmi.js";import"./api-client-DIhJ5qVW.js";import{n as r}from"./settings-store-8FpQDjEA.js";import"./vendor-mermaid-D2KKkqNs.js";import{n as i}from"./utils-CQux7CsO.js";import{t as a}from"./panel-store-C8wwxBpn.js";import{F as o,K as s,U as c,nt as l,rt as u}from"./index-8_rE2Q1-.js";import{n as d,r as f,t as p}from"./vendor-xterm-D1P36hcr.js";var m=e(n(),1),h={background:`#0f1419`,foreground:`#e5e7eb`,cursor:`#e5e7eb`,selectionBackground:`#3b82f640`,black:`#1a1f2e`,red:`#ef4444`,green:`#10b981`,yellow:`#f59e0b`,blue:`#3b82f6`,magenta:`#a855f7`,cyan:`#06b6d4`,white:`#e5e7eb`,brightBlack:`#6b7280`,brightRed:`#f87171`,brightGreen:`#34d399`,brightYellow:`#fbbf24`,brightBlue:`#60a5fa`,brightMagenta:`#c084fc`,brightCyan:`#22d3ee`,brightWhite:`#f9fafb`},g={background:`#ffffff`,foreground:`#1a1f2e`,cursor:`#1a1f2e`,selectionBackground:`#2563eb30`,black:`#1a1f2e`,red:`#dc2626`,green:`#059669`,yellow:`#d97706`,blue:`#2563eb`,magenta:`#9333ea`,cyan:`#0891b2`,white:`#f8fafc`,brightBlack:`#64748b`,brightRed:`#ef4444`,brightGreen:`#10b981`,brightYellow:`#f59e0b`,brightBlue:`#3b82f6`,brightMagenta:`#a855f7`,brightCyan:`#06b6d4`,brightWhite:`#ffffff`};function _(e){return e===`system`?window.matchMedia(`(prefers-color-scheme: dark)`).matches?h:g:e===`light`?g:h}var v=`RESIZE:`;function y(e){let{sessionId:t,containerRef:n}=e,i=(0,m.useRef)(null),a=(0,m.useRef)(null),o=(0,m.useRef)(null),s=(0,m.useRef)(null),c=(0,m.useRef)(0),[l,u]=(0,m.useState)(!1),[h,g]=(0,m.useState)(!1),[y,b]=(0,m.useState)(!1),x=e.tabId?`ppm:terminal-session:${e.tabId}`:null,S=(0,m.useRef)((()=>{if(x)try{return localStorage.getItem(x)??t}catch{}return t})()),C=(0,m.useRef)(0),w=(0,m.useCallback)(e=>{let t=o.current;t?.readyState===WebSocket.OPEN&&t.send(e)},[]),T=(0,m.useCallback)(()=>i.current?.getSelection()??``,[]),E=(0,m.useCallback)(()=>{let e=i.current;if(!e)return``;let t=e.buffer.active,n=C.current,r=t.baseY+t.cursorY,a=[];for(let e=n;e<=r;e++){let n=t.getLine(e);n&&a.push(n.translateToString(!0))}for(;a.length>0&&a[a.length-1].trim()===``;)a.pop();return a.join(`
2
2
  `)},[]),D=(0,m.useCallback)(()=>{let e=i.current,t=o.current;e&&t?.readyState===WebSocket.OPEN&&t.send(`${v}${e.cols},${e.rows}`)},[]),O=(0,m.useCallback)(()=>{if(s.current&&clearTimeout(s.current),o.current?.close(),o.current=null,S.current=`new`,x)try{localStorage.removeItem(x)}catch{}c.current=0,b(!1),u(!1),g(!1),setTimeout(()=>k(),0)},[]),k=(0,m.useCallback)(()=>{s.current&&=(clearTimeout(s.current),null),o.current&&=(o.current.onclose=null,o.current.close(),null);let t=i.current;if(!t)return;let n=window.location.protocol===`https:`?`wss:`:`ws:`,r=e.projectName??``,a=S.current,l=`/ws/project/${encodeURIComponent(r)}/terminal/${a}`,d=`${n}//${window.location.host}${l}`,f=new WebSocket(d);o.current=f,f.onopen=()=>{u(!0),g(!1),c.current=0,D()},f.onmessage=e=>{if(typeof e.data==`string`){if(e.data.startsWith(`{`))try{let n=JSON.parse(e.data);if(n.type===`session`||n.type===`error`||n.type===`exited`||n.type===`ping`){if(n.type===`session`&&n.id&&(S.current=n.id,x))try{localStorage.setItem(x,n.id)}catch{}n.type===`error`&&t.write(`\r\n\x1b[31mError: ${n.message}\x1b[0m\r\n`),n.type===`exited`&&b(!0);return}}catch{}t.write(e.data)}},f.onclose=()=>{u(!1),A()},f.onerror=()=>{f.close()}},[D]);function A(){let e=Math.min(1e3*2**c.current,3e4);c.current++,g(!0),s.current=setTimeout(()=>{k()},e)}return(0,m.useEffect)(()=>{let e=n.current;if(!e)return;let t=new f({cursorBlink:!0,fontSize:13,fontFamily:`var(--font-mono)`,theme:_(r.getState().theme)}),c=new d,l=new p;t.loadAddon(c),t.loadAddon(l),t.open(e),c.fit(),i.current=t,a.current=c,t.onData(e=>{if(e.includes(`\r`)||e.includes(`
3
3
  `)){let e=t.buffer.active;C.current=e.baseY+e.cursorY+1}let n=o.current;n?.readyState===WebSocket.OPEN&&n.send(e)}),k();let u=new ResizeObserver(()=>{try{c.fit(),D()}catch{}});u.observe(e);let m=r.getState().theme,h=r.subscribe(e=>{e.theme!==m&&(m=e.theme,t.options.theme=_(e.theme))});return()=>{h(),u.disconnect(),s.current&&clearTimeout(s.current),o.current?.close(),o.current=null,t.dispose(),i.current=null,a.current=null}},[]),{connected:l,reconnecting:h,exited:y,sendData:w,getSelection:T,getLastCommandOutput:E,restart:O}}var b=t(),x=[{label:`Tab`,value:` `},{label:`Esc`,value:`\x1B`},{label:`Ctrl`,value:null,isModifier:!0},{label:`↑`,value:`\x1B[A`},{label:`↓`,value:`\x1B[B`},{label:`←`,value:`\x1B[D`},{label:`→`,value:`\x1B[C`}],S=(0,m.memo)(function({metadata:e,tabId:t}){let n=e?.sessionId??`new`,r=e?.projectName,d=(0,m.useRef)(null),{connected:f,reconnecting:p,exited:h,sendData:g,getSelection:_,getLastCommandOutput:v,restart:S}=y({sessionId:n,projectName:r,containerRef:d,tabId:t}),[C,w]=(0,m.useState)(!1),[T,E]=(0,m.useState)(null);(0,m.useEffect)(()=>{let e=window.visualViewport;if(!e)return;function t(){e&&E(e.height)}return e.addEventListener(`resize`,t),()=>e.removeEventListener(`resize`,t)},[]);let D=(0,m.useCallback)(()=>{(d.current?.querySelector(`.xterm-helper-textarea`))?.focus()},[]),O=(0,m.useCallback)(e=>{if(D(),C&&e.length===1){let t=e.toLowerCase().charCodeAt(0)-96;t>=1&&t<=26&&g(String.fromCharCode(t)),w(!1);return}g(e)},[C,g,D]),k=(0,m.useCallback)(async()=>{let e=_();e&&await navigator.clipboard.writeText(e)},[_]),A=(0,m.useCallback)(async()=>{try{let e=await navigator.clipboard.readText();e&&(g(e),D())}catch{}},[g,D]),j=(0,m.useCallback)(()=>{let e=_(),t=e||v();if(!t.trim())return;let n="```bash\n"+t+"\n```",i=e?`Terminal selection`:`Terminal output`,s=!1,c=()=>{s=!0};window.addEventListener(`ppm:send-to-chat:ack`,c,{once:!0}),window.dispatchEvent(new CustomEvent(`ppm:send-to-chat`,{detail:{text:n,label:i,projectName:r}})),window.removeEventListener(`ppm:send-to-chat:ack`,c),s?o.success(`Sent to chat`,{duration:1500}):a.getState().openTab({type:`chat`,title:`Chat`,projectId:null,metadata:{projectName:r,pendingMessage:n},closable:!0})},[_,v,r]),M=typeof window<`u`&&`ontouchstart`in window;return(0,b.jsxs)(`div`,{className:`flex flex-col h-full`,style:T?{maxHeight:`${T}px`}:void 0,children:[(0,b.jsxs)(`div`,{className:`flex items-center gap-2 px-3 py-1 bg-surface border-b border-border text-xs`,children:[(0,b.jsx)(`span`,{className:i(`size-2 rounded-full`,h?`bg-error`:f?`bg-success`:p?`bg-warning`:`bg-error`)}),(0,b.jsx)(`span`,{className:`text-text-secondary`,children:h?`Process exited`:f?`Connected`:p?`Reconnecting...`:`Disconnected`}),h&&(0,b.jsxs)(`button`,{onClick:S,className:`flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors`,children:[(0,b.jsx)(c,{size:10}),`Restart`]}),(0,b.jsxs)(`button`,{onClick:j,title:`Send to Chat`,className:`flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors ml-auto`,children:[(0,b.jsx)(s,{size:10}),`Chat`]}),(0,b.jsx)(`span`,{className:`text-text-subtle font-mono`,children:n})]}),(0,b.jsx)(`div`,{ref:d,className:`flex-1 min-h-0 bg-background p-1`}),M&&(0,b.jsxs)(`div`,{className:`flex items-center gap-1 px-2 py-1.5 bg-surface border-t border-border overflow-x-auto`,children:[(0,b.jsx)(`button`,{onClick:k,className:`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`,children:(0,b.jsx)(l,{size:14})}),(0,b.jsx)(`button`,{onClick:A,className:`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`,children:(0,b.jsx)(u,{size:14})}),(0,b.jsx)(`button`,{onClick:j,className:`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`,"aria-label":`Send to Chat`,children:(0,b.jsx)(s,{size:14})}),(0,b.jsx)(`div`,{className:`w-px h-5 bg-border mx-0.5`}),x.map(e=>(0,b.jsx)(`button`,{onClick:()=>{e.label===`Ctrl`?w(!C):e.value&&O(e.value)},className:i(`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`,e.label===`Ctrl`&&C&&`bg-primary text-primary-foreground`),children:e.label},e.label))]})]})});export{S as TerminalTab};
4
- //# sourceMappingURL=terminal-tab-B7ECmf95.js.map
4
+ //# sourceMappingURL=terminal-tab-BgMCsdeN.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-tab-B7ECmf95.js","names":[],"sources":["../../../src/web/hooks/use-terminal.ts","../../../src/web/components/terminal/terminal-tab.tsx"],"sourcesContent":["import { useEffect, useRef, useCallback, useState } from \"react\";\nimport { Terminal, type ITheme } from \"@xterm/xterm\";\nimport { FitAddon } from \"@xterm/addon-fit\";\nimport { WebLinksAddon } from \"@xterm/addon-web-links\";\nimport { useSettingsStore, type Theme } from \"@/stores/settings-store\";\n\nconst DARK_THEME: ITheme = {\n background: \"#0f1419\",\n foreground: \"#e5e7eb\",\n cursor: \"#e5e7eb\",\n selectionBackground: \"#3b82f640\",\n black: \"#1a1f2e\",\n red: \"#ef4444\",\n green: \"#10b981\",\n yellow: \"#f59e0b\",\n blue: \"#3b82f6\",\n magenta: \"#a855f7\",\n cyan: \"#06b6d4\",\n white: \"#e5e7eb\",\n brightBlack: \"#6b7280\",\n brightRed: \"#f87171\",\n brightGreen: \"#34d399\",\n brightYellow: \"#fbbf24\",\n brightBlue: \"#60a5fa\",\n brightMagenta: \"#c084fc\",\n brightCyan: \"#22d3ee\",\n brightWhite: \"#f9fafb\",\n};\n\nconst LIGHT_THEME: ITheme = {\n background: \"#ffffff\",\n foreground: \"#1a1f2e\",\n cursor: \"#1a1f2e\",\n selectionBackground: \"#2563eb30\",\n black: \"#1a1f2e\",\n red: \"#dc2626\",\n green: \"#059669\",\n yellow: \"#d97706\",\n blue: \"#2563eb\",\n magenta: \"#9333ea\",\n cyan: \"#0891b2\",\n white: \"#f8fafc\",\n brightBlack: \"#64748b\",\n brightRed: \"#ef4444\",\n brightGreen: \"#10b981\",\n brightYellow: \"#f59e0b\",\n brightBlue: \"#3b82f6\",\n brightMagenta: \"#a855f7\",\n brightCyan: \"#06b6d4\",\n brightWhite: \"#ffffff\",\n};\n\nfunction resolveTheme(theme: Theme): ITheme {\n if (theme === \"system\") {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? DARK_THEME : LIGHT_THEME;\n }\n return theme === \"light\" ? LIGHT_THEME : DARK_THEME;\n}\n\ninterface UseTerminalOptions {\n sessionId: string;\n projectName?: string;\n containerRef: React.RefObject<HTMLDivElement | null>;\n /** Stable tab ID for persisting session across reload */\n tabId?: string;\n}\n\ninterface UseTerminalReturn {\n connected: boolean;\n reconnecting: boolean;\n exited: boolean;\n sendData: (data: string) => void;\n getSelection: () => string;\n /** Read buffer from last command start to current cursor (for \"Send to Chat\"). */\n getLastCommandOutput: () => string;\n restart: () => void;\n}\n\nconst RESIZE_PREFIX = \"\\x01RESIZE:\";\n\nexport function useTerminal(\n options: UseTerminalOptions,\n): UseTerminalReturn {\n const { sessionId, containerRef } = options;\n const termRef = useRef<Terminal | null>(null);\n const fitRef = useRef<FitAddon | null>(null);\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reconnectAttempts = useRef(0);\n const [connected, setConnected] = useState(false);\n const [reconnecting, setReconnecting] = useState(false);\n const [exited, setExited] = useState(false);\n // Restore persisted session ID from localStorage (survives page reload)\n const storageKey = options.tabId ? `ppm:terminal-session:${options.tabId}` : null;\n const initialSessionId = (() => {\n if (storageKey) {\n try { return localStorage.getItem(storageKey) ?? sessionId; } catch { /* */ }\n }\n return sessionId;\n })();\n const actualSessionId = useRef(initialSessionId);\n /** Absolute row where last command output starts (set when user presses Enter) */\n const commandStartRow = useRef(0);\n\n const sendData = useCallback((data: string) => {\n const ws = wsRef.current;\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n }, []);\n\n const getSelection = useCallback(() => {\n return termRef.current?.getSelection() ?? \"\";\n }, []);\n\n const getLastCommandOutput = useCallback(() => {\n const term = termRef.current;\n if (!term) return \"\";\n const buf = term.buffer.active;\n const startRow = commandStartRow.current;\n const endRow = buf.baseY + buf.cursorY;\n const lines: string[] = [];\n for (let i = startRow; i <= endRow; i++) {\n const line = buf.getLine(i);\n if (line) lines.push(line.translateToString(true));\n }\n // Trim trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1]!.trim() === \"\") lines.pop();\n return lines.join(\"\\n\");\n }, []);\n\n const sendResize = useCallback(() => {\n const term = termRef.current;\n const ws = wsRef.current;\n if (term && ws?.readyState === WebSocket.OPEN) {\n ws.send(`${RESIZE_PREFIX}${term.cols},${term.rows}`);\n }\n }, []);\n\n const restart = useCallback(() => {\n // Close existing WS, reset to \"new\" session, reconnect\n if (reconnectTimer.current) clearTimeout(reconnectTimer.current);\n wsRef.current?.close();\n wsRef.current = null;\n actualSessionId.current = \"new\";\n if (storageKey) { try { localStorage.removeItem(storageKey); } catch { /* */ } }\n reconnectAttempts.current = 0;\n setExited(false);\n setConnected(false);\n setReconnecting(false);\n // connectWs will be called after this via setTimeout to allow state to settle\n setTimeout(() => connectWs(), 0);\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const connectWs = useCallback(() => {\n // Prevent duplicate connections (e.g. React StrictMode re-mount racing\n // with a scheduled reconnect from the previous mount's WS close event).\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current);\n reconnectTimer.current = null;\n }\n if (wsRef.current) {\n wsRef.current.onclose = null; // prevent triggering scheduleReconnect\n wsRef.current.close();\n wsRef.current = null;\n }\n\n const term = termRef.current;\n if (!term) return;\n\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const projectName = options.projectName ?? \"\";\n // Use actual session ID from server on reconnect (not \"new\")\n const sid = actualSessionId.current;\n const path = `/ws/project/${encodeURIComponent(projectName)}/terminal/${sid}`;\n // In dev mode, connect directly to backend (port 8081) to bypass\n // Vite's dev proxy which has unreliable WebSocket upgrade handling.\n const url = import.meta.env.DEV\n ? `ws://${window.location.hostname}:8081${path}`\n : `${protocol}//${window.location.host}${path}`;\n\n const ws = new WebSocket(url);\n wsRef.current = ws;\n\n ws.onopen = () => {\n setConnected(true);\n setReconnecting(false);\n reconnectAttempts.current = 0;\n sendResize();\n };\n\n ws.onmessage = (event) => {\n if (typeof event.data === \"string\") {\n // Filter JSON control messages from terminal output\n if (event.data.startsWith(\"{\")) {\n try {\n const msg = JSON.parse(event.data);\n if (msg.type === \"session\" || msg.type === \"error\" || msg.type === \"exited\" || msg.type === \"ping\") {\n if (msg.type === \"session\" && msg.id) {\n actualSessionId.current = msg.id;\n // Persist to localStorage so reload reconnects to same PTY\n if (storageKey) {\n try { localStorage.setItem(storageKey, msg.id); } catch { /* */ }\n }\n }\n if (msg.type === \"error\") {\n term.write(`\\r\\n\\x1b[31mError: ${msg.message}\\x1b[0m\\r\\n`);\n }\n if (msg.type === \"exited\") {\n setExited(true);\n }\n return; // Don't write raw JSON to terminal\n }\n } catch {\n // Not JSON, write as terminal output\n }\n }\n term.write(event.data);\n }\n };\n\n ws.onclose = () => {\n setConnected(false);\n scheduleReconnect();\n };\n\n ws.onerror = () => {\n ws.close();\n };\n }, [sendResize]); // eslint-disable-line react-hooks/exhaustive-deps\n\n function scheduleReconnect() {\n const delay = Math.min(\n 1000 * Math.pow(2, reconnectAttempts.current),\n 30000,\n );\n reconnectAttempts.current++;\n setReconnecting(true);\n reconnectTimer.current = setTimeout(() => {\n connectWs();\n }, delay);\n }\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const term = new Terminal({\n cursorBlink: true,\n fontSize: 13,\n fontFamily: \"var(--font-mono)\",\n theme: resolveTheme(useSettingsStore.getState().theme),\n });\n\n const fitAddon = new FitAddon();\n const webLinksAddon = new WebLinksAddon();\n\n term.loadAddon(fitAddon);\n term.loadAddon(webLinksAddon);\n term.open(container);\n fitAddon.fit();\n\n termRef.current = term;\n fitRef.current = fitAddon;\n\n // Wire input to WS + track command boundaries\n term.onData((data) => {\n // When user presses Enter, mark next row as command output start\n if (data.includes(\"\\r\") || data.includes(\"\\n\")) {\n const buf = term.buffer.active;\n commandStartRow.current = buf.baseY + buf.cursorY + 1;\n }\n const ws = wsRef.current;\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n });\n\n // Connect WS\n connectWs();\n\n // ResizeObserver for auto-fit\n const resizeObserver = new ResizeObserver(() => {\n try {\n fitAddon.fit();\n sendResize();\n } catch {\n // Ignore fit errors during teardown\n }\n });\n resizeObserver.observe(container);\n\n // React to theme changes\n let prevTheme = useSettingsStore.getState().theme;\n const unsubTheme = useSettingsStore.subscribe((state) => {\n if (state.theme !== prevTheme) {\n prevTheme = state.theme;\n term.options.theme = resolveTheme(state.theme);\n }\n });\n\n return () => {\n unsubTheme();\n resizeObserver.disconnect();\n if (reconnectTimer.current) clearTimeout(reconnectTimer.current);\n wsRef.current?.close();\n wsRef.current = null;\n term.dispose();\n termRef.current = null;\n fitRef.current = null;\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n return { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, restart };\n}\n","import { useRef, useEffect, useState, useCallback, memo } from \"react\";\nimport { useTerminal } from \"@/hooks/use-terminal\";\nimport { cn } from \"@/lib/utils\";\nimport { Copy, ClipboardPaste, RotateCcw, MessageSquare } from \"lucide-react\";\nimport \"@xterm/xterm/css/xterm.css\";\nimport { toast } from \"sonner\";\n\nimport { usePanelStore } from \"@/stores/panel-store\";\n\ninterface TerminalTabProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nconst MOBILE_KEYS = [\n { label: \"Tab\", value: \"\\t\" },\n { label: \"Esc\", value: \"\\x1b\" },\n { label: \"Ctrl\", value: null, isModifier: true },\n { label: \"\\u2191\", value: \"\\x1b[A\" },\n { label: \"\\u2193\", value: \"\\x1b[B\" },\n { label: \"\\u2190\", value: \"\\x1b[D\" },\n { label: \"\\u2192\", value: \"\\x1b[C\" },\n] as const;\n\nexport const TerminalTab = memo(function TerminalTab({ metadata, tabId }: TerminalTabProps) {\n const sessionId = (metadata?.sessionId as string) ?? \"new\";\n const projectName = metadata?.projectName as string | undefined;\n const containerRef = useRef<HTMLDivElement>(null);\n\n const { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, restart } = useTerminal({ sessionId, projectName, containerRef, tabId });\n const [ctrlMode, setCtrlMode] = useState(false);\n const [viewportHeight, setViewportHeight] = useState<number | null>(null);\n\n // Adjust height when mobile keyboard opens\n useEffect(() => {\n const vv = window.visualViewport;\n if (!vv) return;\n\n function handleResize() {\n if (!vv) return;\n setViewportHeight(vv.height);\n }\n\n vv.addEventListener(\"resize\", handleResize);\n return () => vv.removeEventListener(\"resize\", handleResize);\n }, []);\n\n const focusTerminal = useCallback(() => {\n const termElement = containerRef.current?.querySelector(\n \".xterm-helper-textarea\",\n ) as HTMLTextAreaElement | null;\n termElement?.focus();\n }, []);\n\n const sendKey = useCallback(\n (value: string) => {\n focusTerminal();\n\n if (ctrlMode && value.length === 1) {\n // Ctrl+key: send char code 1-26 for a-z\n const code = value.toLowerCase().charCodeAt(0) - 96;\n if (code >= 1 && code <= 26) {\n sendData(String.fromCharCode(code));\n }\n setCtrlMode(false);\n return;\n }\n\n sendData(value);\n },\n [ctrlMode, sendData, focusTerminal],\n );\n\n const handleCopy = useCallback(async () => {\n const selection = getSelection();\n if (selection) {\n await navigator.clipboard.writeText(selection);\n }\n }, [getSelection]);\n\n const handlePaste = useCallback(async () => {\n try {\n const text = await navigator.clipboard.readText();\n if (text) {\n sendData(text);\n focusTerminal();\n }\n } catch {\n // Clipboard permission denied\n }\n }, [sendData, focusTerminal]);\n\n const handleSendToChat = useCallback(() => {\n const selection = getSelection();\n const text = selection || getLastCommandOutput();\n if (!text.trim()) return;\n const codeBlock = \"```bash\\n\" + text + \"\\n```\";\n const label = selection ? \"Terminal selection\" : \"Terminal output\";\n\n // Try to inject into an already-open chat tab as attachment chip\n let handled = false;\n const handler = () => { handled = true; };\n window.addEventListener(\"ppm:send-to-chat:ack\", handler, { once: true });\n window.dispatchEvent(new CustomEvent(\"ppm:send-to-chat\", { detail: { text: codeBlock, label, projectName } }));\n window.removeEventListener(\"ppm:send-to-chat:ack\", handler);\n\n if (handled) {\n toast.success(\"Sent to chat\", { duration: 1500 });\n } else {\n // No chat tab mounted — open a new one with the content as initialValue\n usePanelStore.getState().openTab({\n type: \"chat\",\n title: \"Chat\",\n projectId: null,\n metadata: { projectName, pendingMessage: codeBlock },\n closable: true,\n });\n }\n }, [getSelection, getLastCommandOutput, projectName]);\n\n const isMobile = typeof window !== \"undefined\" && \"ontouchstart\" in window;\n\n return (\n <div\n className=\"flex flex-col h-full\"\n style={viewportHeight ? { maxHeight: `${viewportHeight}px` } : undefined}\n >\n {/* Status bar */}\n <div className=\"flex items-center gap-2 px-3 py-1 bg-surface border-b border-border text-xs\">\n <span\n className={cn(\n \"size-2 rounded-full\",\n exited ? \"bg-error\" : connected ? \"bg-success\" : reconnecting ? \"bg-warning\" : \"bg-error\",\n )}\n />\n <span className=\"text-text-secondary\">\n {exited\n ? \"Process exited\"\n : connected\n ? \"Connected\"\n : reconnecting\n ? \"Reconnecting...\"\n : \"Disconnected\"}\n </span>\n {exited && (\n <button\n onClick={restart}\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors\"\n >\n <RotateCcw size={10} />\n Restart\n </button>\n )}\n <button\n onClick={handleSendToChat}\n title=\"Send to Chat\"\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors ml-auto\"\n >\n <MessageSquare size={10} />\n Chat\n </button>\n <span className=\"text-text-subtle font-mono\">{sessionId}</span>\n </div>\n\n {/* Terminal container */}\n <div ref={containerRef} className=\"flex-1 min-h-0 bg-background p-1\" />\n\n {/* Mobile toolbar */}\n {isMobile && (\n <div className=\"flex items-center gap-1 px-2 py-1.5 bg-surface border-t border-border overflow-x-auto\">\n <button\n onClick={handleCopy}\n className=\"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\"\n >\n <Copy size={14} />\n </button>\n <button\n onClick={handlePaste}\n className=\"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\"\n >\n <ClipboardPaste size={14} />\n </button>\n <button\n onClick={handleSendToChat}\n className=\"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\"\n aria-label=\"Send to Chat\"\n >\n <MessageSquare size={14} />\n </button>\n <div className=\"w-px h-5 bg-border mx-0.5\" />\n {MOBILE_KEYS.map((key) => (\n <button\n key={key.label}\n onClick={() => {\n if (key.label === \"Ctrl\") {\n setCtrlMode(!ctrlMode);\n } else if (key.value) {\n sendKey(key.value);\n }\n }}\n className={cn(\n \"px-3 py-1.5 rounded text-xs font-mono min-w-[36px] min-h-[32px]\",\n \"bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground\",\n \"transition-colors select-none\",\n key.label === \"Ctrl\" && ctrlMode && \"bg-primary text-primary-foreground\",\n )}\n >\n {key.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"terminal-tab-BgMCsdeN.js","names":[],"sources":["../../../src/web/hooks/use-terminal.ts","../../../src/web/components/terminal/terminal-tab.tsx"],"sourcesContent":["import { useEffect, useRef, useCallback, useState } from \"react\";\nimport { Terminal, type ITheme } from \"@xterm/xterm\";\nimport { FitAddon } from \"@xterm/addon-fit\";\nimport { WebLinksAddon } from \"@xterm/addon-web-links\";\nimport { useSettingsStore, type Theme } from \"@/stores/settings-store\";\n\nconst DARK_THEME: ITheme = {\n background: \"#0f1419\",\n foreground: \"#e5e7eb\",\n cursor: \"#e5e7eb\",\n selectionBackground: \"#3b82f640\",\n black: \"#1a1f2e\",\n red: \"#ef4444\",\n green: \"#10b981\",\n yellow: \"#f59e0b\",\n blue: \"#3b82f6\",\n magenta: \"#a855f7\",\n cyan: \"#06b6d4\",\n white: \"#e5e7eb\",\n brightBlack: \"#6b7280\",\n brightRed: \"#f87171\",\n brightGreen: \"#34d399\",\n brightYellow: \"#fbbf24\",\n brightBlue: \"#60a5fa\",\n brightMagenta: \"#c084fc\",\n brightCyan: \"#22d3ee\",\n brightWhite: \"#f9fafb\",\n};\n\nconst LIGHT_THEME: ITheme = {\n background: \"#ffffff\",\n foreground: \"#1a1f2e\",\n cursor: \"#1a1f2e\",\n selectionBackground: \"#2563eb30\",\n black: \"#1a1f2e\",\n red: \"#dc2626\",\n green: \"#059669\",\n yellow: \"#d97706\",\n blue: \"#2563eb\",\n magenta: \"#9333ea\",\n cyan: \"#0891b2\",\n white: \"#f8fafc\",\n brightBlack: \"#64748b\",\n brightRed: \"#ef4444\",\n brightGreen: \"#10b981\",\n brightYellow: \"#f59e0b\",\n brightBlue: \"#3b82f6\",\n brightMagenta: \"#a855f7\",\n brightCyan: \"#06b6d4\",\n brightWhite: \"#ffffff\",\n};\n\nfunction resolveTheme(theme: Theme): ITheme {\n if (theme === \"system\") {\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? DARK_THEME : LIGHT_THEME;\n }\n return theme === \"light\" ? LIGHT_THEME : DARK_THEME;\n}\n\ninterface UseTerminalOptions {\n sessionId: string;\n projectName?: string;\n containerRef: React.RefObject<HTMLDivElement | null>;\n /** Stable tab ID for persisting session across reload */\n tabId?: string;\n}\n\ninterface UseTerminalReturn {\n connected: boolean;\n reconnecting: boolean;\n exited: boolean;\n sendData: (data: string) => void;\n getSelection: () => string;\n /** Read buffer from last command start to current cursor (for \"Send to Chat\"). */\n getLastCommandOutput: () => string;\n restart: () => void;\n}\n\nconst RESIZE_PREFIX = \"\\x01RESIZE:\";\n\nexport function useTerminal(\n options: UseTerminalOptions,\n): UseTerminalReturn {\n const { sessionId, containerRef } = options;\n const termRef = useRef<Terminal | null>(null);\n const fitRef = useRef<FitAddon | null>(null);\n const wsRef = useRef<WebSocket | null>(null);\n const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const reconnectAttempts = useRef(0);\n const [connected, setConnected] = useState(false);\n const [reconnecting, setReconnecting] = useState(false);\n const [exited, setExited] = useState(false);\n // Restore persisted session ID from localStorage (survives page reload)\n const storageKey = options.tabId ? `ppm:terminal-session:${options.tabId}` : null;\n const initialSessionId = (() => {\n if (storageKey) {\n try { return localStorage.getItem(storageKey) ?? sessionId; } catch { /* */ }\n }\n return sessionId;\n })();\n const actualSessionId = useRef(initialSessionId);\n /** Absolute row where last command output starts (set when user presses Enter) */\n const commandStartRow = useRef(0);\n\n const sendData = useCallback((data: string) => {\n const ws = wsRef.current;\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n }, []);\n\n const getSelection = useCallback(() => {\n return termRef.current?.getSelection() ?? \"\";\n }, []);\n\n const getLastCommandOutput = useCallback(() => {\n const term = termRef.current;\n if (!term) return \"\";\n const buf = term.buffer.active;\n const startRow = commandStartRow.current;\n const endRow = buf.baseY + buf.cursorY;\n const lines: string[] = [];\n for (let i = startRow; i <= endRow; i++) {\n const line = buf.getLine(i);\n if (line) lines.push(line.translateToString(true));\n }\n // Trim trailing empty lines\n while (lines.length > 0 && lines[lines.length - 1]!.trim() === \"\") lines.pop();\n return lines.join(\"\\n\");\n }, []);\n\n const sendResize = useCallback(() => {\n const term = termRef.current;\n const ws = wsRef.current;\n if (term && ws?.readyState === WebSocket.OPEN) {\n ws.send(`${RESIZE_PREFIX}${term.cols},${term.rows}`);\n }\n }, []);\n\n const restart = useCallback(() => {\n // Close existing WS, reset to \"new\" session, reconnect\n if (reconnectTimer.current) clearTimeout(reconnectTimer.current);\n wsRef.current?.close();\n wsRef.current = null;\n actualSessionId.current = \"new\";\n if (storageKey) { try { localStorage.removeItem(storageKey); } catch { /* */ } }\n reconnectAttempts.current = 0;\n setExited(false);\n setConnected(false);\n setReconnecting(false);\n // connectWs will be called after this via setTimeout to allow state to settle\n setTimeout(() => connectWs(), 0);\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const connectWs = useCallback(() => {\n // Prevent duplicate connections (e.g. React StrictMode re-mount racing\n // with a scheduled reconnect from the previous mount's WS close event).\n if (reconnectTimer.current) {\n clearTimeout(reconnectTimer.current);\n reconnectTimer.current = null;\n }\n if (wsRef.current) {\n wsRef.current.onclose = null; // prevent triggering scheduleReconnect\n wsRef.current.close();\n wsRef.current = null;\n }\n\n const term = termRef.current;\n if (!term) return;\n\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const projectName = options.projectName ?? \"\";\n // Use actual session ID from server on reconnect (not \"new\")\n const sid = actualSessionId.current;\n const path = `/ws/project/${encodeURIComponent(projectName)}/terminal/${sid}`;\n // In dev mode, connect directly to backend (port 8081) to bypass\n // Vite's dev proxy which has unreliable WebSocket upgrade handling.\n const url = import.meta.env.DEV\n ? `ws://${window.location.hostname}:8081${path}`\n : `${protocol}//${window.location.host}${path}`;\n\n const ws = new WebSocket(url);\n wsRef.current = ws;\n\n ws.onopen = () => {\n setConnected(true);\n setReconnecting(false);\n reconnectAttempts.current = 0;\n sendResize();\n };\n\n ws.onmessage = (event) => {\n if (typeof event.data === \"string\") {\n // Filter JSON control messages from terminal output\n if (event.data.startsWith(\"{\")) {\n try {\n const msg = JSON.parse(event.data);\n if (msg.type === \"session\" || msg.type === \"error\" || msg.type === \"exited\" || msg.type === \"ping\") {\n if (msg.type === \"session\" && msg.id) {\n actualSessionId.current = msg.id;\n // Persist to localStorage so reload reconnects to same PTY\n if (storageKey) {\n try { localStorage.setItem(storageKey, msg.id); } catch { /* */ }\n }\n }\n if (msg.type === \"error\") {\n term.write(`\\r\\n\\x1b[31mError: ${msg.message}\\x1b[0m\\r\\n`);\n }\n if (msg.type === \"exited\") {\n setExited(true);\n }\n return; // Don't write raw JSON to terminal\n }\n } catch {\n // Not JSON, write as terminal output\n }\n }\n term.write(event.data);\n }\n };\n\n ws.onclose = () => {\n setConnected(false);\n scheduleReconnect();\n };\n\n ws.onerror = () => {\n ws.close();\n };\n }, [sendResize]); // eslint-disable-line react-hooks/exhaustive-deps\n\n function scheduleReconnect() {\n const delay = Math.min(\n 1000 * Math.pow(2, reconnectAttempts.current),\n 30000,\n );\n reconnectAttempts.current++;\n setReconnecting(true);\n reconnectTimer.current = setTimeout(() => {\n connectWs();\n }, delay);\n }\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n const term = new Terminal({\n cursorBlink: true,\n fontSize: 13,\n fontFamily: \"var(--font-mono)\",\n theme: resolveTheme(useSettingsStore.getState().theme),\n });\n\n const fitAddon = new FitAddon();\n const webLinksAddon = new WebLinksAddon();\n\n term.loadAddon(fitAddon);\n term.loadAddon(webLinksAddon);\n term.open(container);\n fitAddon.fit();\n\n termRef.current = term;\n fitRef.current = fitAddon;\n\n // Wire input to WS + track command boundaries\n term.onData((data) => {\n // When user presses Enter, mark next row as command output start\n if (data.includes(\"\\r\") || data.includes(\"\\n\")) {\n const buf = term.buffer.active;\n commandStartRow.current = buf.baseY + buf.cursorY + 1;\n }\n const ws = wsRef.current;\n if (ws?.readyState === WebSocket.OPEN) {\n ws.send(data);\n }\n });\n\n // Connect WS\n connectWs();\n\n // ResizeObserver for auto-fit\n const resizeObserver = new ResizeObserver(() => {\n try {\n fitAddon.fit();\n sendResize();\n } catch {\n // Ignore fit errors during teardown\n }\n });\n resizeObserver.observe(container);\n\n // React to theme changes\n let prevTheme = useSettingsStore.getState().theme;\n const unsubTheme = useSettingsStore.subscribe((state) => {\n if (state.theme !== prevTheme) {\n prevTheme = state.theme;\n term.options.theme = resolveTheme(state.theme);\n }\n });\n\n return () => {\n unsubTheme();\n resizeObserver.disconnect();\n if (reconnectTimer.current) clearTimeout(reconnectTimer.current);\n wsRef.current?.close();\n wsRef.current = null;\n term.dispose();\n termRef.current = null;\n fitRef.current = null;\n };\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n return { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, restart };\n}\n","import { useRef, useEffect, useState, useCallback, memo } from \"react\";\nimport { useTerminal } from \"@/hooks/use-terminal\";\nimport { cn } from \"@/lib/utils\";\nimport { Copy, ClipboardPaste, RotateCcw, MessageSquare } from \"lucide-react\";\nimport \"@xterm/xterm/css/xterm.css\";\nimport { toast } from \"sonner\";\n\nimport { usePanelStore } from \"@/stores/panel-store\";\n\ninterface TerminalTabProps {\n metadata?: Record<string, unknown>;\n tabId?: string;\n}\n\nconst MOBILE_KEYS = [\n { label: \"Tab\", value: \"\\t\" },\n { label: \"Esc\", value: \"\\x1b\" },\n { label: \"Ctrl\", value: null, isModifier: true },\n { label: \"\\u2191\", value: \"\\x1b[A\" },\n { label: \"\\u2193\", value: \"\\x1b[B\" },\n { label: \"\\u2190\", value: \"\\x1b[D\" },\n { label: \"\\u2192\", value: \"\\x1b[C\" },\n] as const;\n\nexport const TerminalTab = memo(function TerminalTab({ metadata, tabId }: TerminalTabProps) {\n const sessionId = (metadata?.sessionId as string) ?? \"new\";\n const projectName = metadata?.projectName as string | undefined;\n const containerRef = useRef<HTMLDivElement>(null);\n\n const { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, restart } = useTerminal({ sessionId, projectName, containerRef, tabId });\n const [ctrlMode, setCtrlMode] = useState(false);\n const [viewportHeight, setViewportHeight] = useState<number | null>(null);\n\n // Adjust height when mobile keyboard opens\n useEffect(() => {\n const vv = window.visualViewport;\n if (!vv) return;\n\n function handleResize() {\n if (!vv) return;\n setViewportHeight(vv.height);\n }\n\n vv.addEventListener(\"resize\", handleResize);\n return () => vv.removeEventListener(\"resize\", handleResize);\n }, []);\n\n const focusTerminal = useCallback(() => {\n const termElement = containerRef.current?.querySelector(\n \".xterm-helper-textarea\",\n ) as HTMLTextAreaElement | null;\n termElement?.focus();\n }, []);\n\n const sendKey = useCallback(\n (value: string) => {\n focusTerminal();\n\n if (ctrlMode && value.length === 1) {\n // Ctrl+key: send char code 1-26 for a-z\n const code = value.toLowerCase().charCodeAt(0) - 96;\n if (code >= 1 && code <= 26) {\n sendData(String.fromCharCode(code));\n }\n setCtrlMode(false);\n return;\n }\n\n sendData(value);\n },\n [ctrlMode, sendData, focusTerminal],\n );\n\n const handleCopy = useCallback(async () => {\n const selection = getSelection();\n if (selection) {\n await navigator.clipboard.writeText(selection);\n }\n }, [getSelection]);\n\n const handlePaste = useCallback(async () => {\n try {\n const text = await navigator.clipboard.readText();\n if (text) {\n sendData(text);\n focusTerminal();\n }\n } catch {\n // Clipboard permission denied\n }\n }, [sendData, focusTerminal]);\n\n const handleSendToChat = useCallback(() => {\n const selection = getSelection();\n const text = selection || getLastCommandOutput();\n if (!text.trim()) return;\n const codeBlock = \"```bash\\n\" + text + \"\\n```\";\n const label = selection ? \"Terminal selection\" : \"Terminal output\";\n\n // Try to inject into an already-open chat tab as attachment chip\n let handled = false;\n const handler = () => { handled = true; };\n window.addEventListener(\"ppm:send-to-chat:ack\", handler, { once: true });\n window.dispatchEvent(new CustomEvent(\"ppm:send-to-chat\", { detail: { text: codeBlock, label, projectName } }));\n window.removeEventListener(\"ppm:send-to-chat:ack\", handler);\n\n if (handled) {\n toast.success(\"Sent to chat\", { duration: 1500 });\n } else {\n // No chat tab mounted — open a new one with the content as initialValue\n usePanelStore.getState().openTab({\n type: \"chat\",\n title: \"Chat\",\n projectId: null,\n metadata: { projectName, pendingMessage: codeBlock },\n closable: true,\n });\n }\n }, [getSelection, getLastCommandOutput, projectName]);\n\n const isMobile = typeof window !== \"undefined\" && \"ontouchstart\" in window;\n\n return (\n <div\n className=\"flex flex-col h-full\"\n style={viewportHeight ? { maxHeight: `${viewportHeight}px` } : undefined}\n >\n {/* Status bar */}\n <div className=\"flex items-center gap-2 px-3 py-1 bg-surface border-b border-border text-xs\">\n <span\n className={cn(\n \"size-2 rounded-full\",\n exited ? \"bg-error\" : connected ? \"bg-success\" : reconnecting ? \"bg-warning\" : \"bg-error\",\n )}\n />\n <span className=\"text-text-secondary\">\n {exited\n ? \"Process exited\"\n : connected\n ? \"Connected\"\n : reconnecting\n ? \"Reconnecting...\"\n : \"Disconnected\"}\n </span>\n {exited && (\n <button\n onClick={restart}\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors\"\n >\n <RotateCcw size={10} />\n Restart\n </button>\n )}\n <button\n onClick={handleSendToChat}\n title=\"Send to Chat\"\n className=\"flex items-center gap-1 px-1.5 py-0.5 rounded text-xs bg-surface-elevated text-text-primary hover:bg-primary hover:text-primary-foreground active:bg-primary active:text-primary-foreground transition-colors ml-auto\"\n >\n <MessageSquare size={10} />\n Chat\n </button>\n <span className=\"text-text-subtle font-mono\">{sessionId}</span>\n </div>\n\n {/* Terminal container */}\n <div ref={containerRef} className=\"flex-1 min-h-0 bg-background p-1\" />\n\n {/* Mobile toolbar */}\n {isMobile && (\n <div className=\"flex items-center gap-1 px-2 py-1.5 bg-surface border-t border-border overflow-x-auto\">\n <button\n onClick={handleCopy}\n className=\"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\"\n >\n <Copy size={14} />\n </button>\n <button\n onClick={handlePaste}\n className=\"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\"\n >\n <ClipboardPaste size={14} />\n </button>\n <button\n onClick={handleSendToChat}\n className=\"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\"\n aria-label=\"Send to Chat\"\n >\n <MessageSquare size={14} />\n </button>\n <div className=\"w-px h-5 bg-border mx-0.5\" />\n {MOBILE_KEYS.map((key) => (\n <button\n key={key.label}\n onClick={() => {\n if (key.label === \"Ctrl\") {\n setCtrlMode(!ctrlMode);\n } else if (key.value) {\n sendKey(key.value);\n }\n }}\n className={cn(\n \"px-3 py-1.5 rounded text-xs font-mono min-w-[36px] min-h-[32px]\",\n \"bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground\",\n \"transition-colors select-none\",\n key.label === \"Ctrl\" && ctrlMode && \"bg-primary text-primary-foreground\",\n )}\n >\n {key.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-BwzaQ50n.js";import"./api-client-DIhJ5qVW.js";import{q as n}from"./index-DkQ6jVSH.js";import{t as r}from"./use-blob-url-DB4nNruT.js";var i=e();function a({filePath:e,projectName:a}){let{blobUrl:o,error:s}=r(e,a);return s?(0,i.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,i.jsx)(t,{className:`size-10 text-text-subtle`}),(0,i.jsx)(`p`,{className:`text-sm`,children:`Failed to load video.`})]}):o?(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,i.jsx)(`video`,{src:o,controls:!0,className:`max-w-full max-h-full`})}):(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,i.jsx)(n,{className:`size-5 animate-spin text-text-subtle`})})}export{a as VideoPreview};
2
- //# sourceMappingURL=video-preview-CT78iZBo.js.map
1
+ import{b as e}from"./vendor-markdown-0Mxgxy0L.js";import{t}from"./file-exclamation-point-BwzaQ50n.js";import"./api-client-DIhJ5qVW.js";import{q as n}from"./index-8_rE2Q1-.js";import{t as r}from"./use-blob-url-DB4nNruT.js";var i=e();function a({filePath:e,projectName:a}){let{blobUrl:o,error:s}=r(e,a);return s?(0,i.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,i.jsx)(t,{className:`size-10 text-text-subtle`}),(0,i.jsx)(`p`,{className:`text-sm`,children:`Failed to load video.`})]}):o?(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full p-4 bg-surface overflow-auto`,children:(0,i.jsx)(`video`,{src:o,controls:!0,className:`max-w-full max-h-full`})}):(0,i.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,i.jsx)(n,{className:`size-5 animate-spin text-text-subtle`})})}export{a as VideoPreview};
2
+ //# sourceMappingURL=video-preview-w8ZAy8av.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-preview-CT78iZBo.js","names":[],"sources":["../../../src/web/components/editor/video-preview.tsx"],"sourcesContent":["import { Loader2, FileWarning } from \"lucide-react\";\nimport { useBlobUrl } from \"./use-blob-url\";\n\nexport function VideoPreview({ filePath, projectName }: { filePath: string; projectName: string }) {\n const { blobUrl, error } = useBlobUrl(filePath, projectName);\n\n if (error) {\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\">Failed to load video.</p>\n </div>\n );\n }\n if (!blobUrl) {\n return <div className=\"flex items-center justify-center h-full\"><Loader2 className=\"size-5 animate-spin text-text-subtle\" /></div>;\n }\n return (\n <div className=\"flex items-center justify-center h-full p-4 bg-surface overflow-auto\">\n <video src={blobUrl} controls className=\"max-w-full max-h-full\" />\n </div>\n );\n}\n"],"mappings":"wOAGA,SAAgB,EAAa,CAAE,WAAU,eAA0D,CACjG,GAAM,CAAE,UAAS,SAAU,EAAW,EAAU,EAAY,CAa5D,OAXI,GACF,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,sFAAf,EAAA,EAAA,EAAA,KACG,EAAD,CAAa,UAAU,2BAA6B,CAAA,EAAA,EAAA,EAAA,KACnD,IAAD,CAAG,UAAU,mBAAU,wBAAyB,CAAA,CAC5C,GAGL,GAGL,EAAA,EAAA,KACG,MAAD,CAAK,UAAU,0FACZ,QAAD,CAAO,IAAK,EAAS,SAAA,GAAS,UAAU,wBAA0B,CAAA,CAC9D,CAAA,EALN,EAAA,EAAA,KAAQ,MAAD,CAAK,UAAU,6DAA2C,EAAD,CAAS,UAAU,uCAAyC,CAAA,CAAM,CAAA"}
1
+ {"version":3,"file":"video-preview-w8ZAy8av.js","names":[],"sources":["../../../src/web/components/editor/video-preview.tsx"],"sourcesContent":["import { Loader2, FileWarning } from \"lucide-react\";\nimport { useBlobUrl } from \"./use-blob-url\";\n\nexport function VideoPreview({ filePath, projectName }: { filePath: string; projectName: string }) {\n const { blobUrl, error } = useBlobUrl(filePath, projectName);\n\n if (error) {\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\">Failed to load video.</p>\n </div>\n );\n }\n if (!blobUrl) {\n return <div className=\"flex items-center justify-center h-full\"><Loader2 className=\"size-5 animate-spin text-text-subtle\" /></div>;\n }\n return (\n <div className=\"flex items-center justify-center h-full p-4 bg-surface overflow-auto\">\n <video src={blobUrl} controls className=\"max-w-full max-h-full\" />\n </div>\n );\n}\n"],"mappings":"wOAGA,SAAgB,EAAa,CAAE,WAAU,eAA0D,CACjG,GAAM,CAAE,UAAS,SAAU,EAAW,EAAU,EAAY,CAa5D,OAXI,GACF,EAAA,EAAA,MACG,MAAD,CAAK,UAAU,sFAAf,EAAA,EAAA,EAAA,KACG,EAAD,CAAa,UAAU,2BAA6B,CAAA,EAAA,EAAA,EAAA,KACnD,IAAD,CAAG,UAAU,mBAAU,wBAAyB,CAAA,CAC5C,GAGL,GAGL,EAAA,EAAA,KACG,MAAD,CAAK,UAAU,0FACZ,QAAD,CAAO,IAAK,EAAS,SAAA,GAAS,UAAU,wBAA0B,CAAA,CAC9D,CAAA,EALN,EAAA,EAAA,KAAQ,MAAD,CAAK,UAAU,6DAA2C,EAAD,CAAS,UAAU,uCAAyC,CAAA,CAAM,CAAA"}
@@ -39,7 +39,7 @@
39
39
  <link rel="preconnect" href="https://fonts.googleapis.com" />
40
40
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
41
41
  <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
42
- <script type="module" crossorigin src="/assets/index-DkQ6jVSH.js"></script>
42
+ <script type="module" crossorigin src="/assets/index-8_rE2Q1-.js"></script>
43
43
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
44
44
  <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-D2KKkqNs.js">
45
45
  <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
@@ -60,8 +60,8 @@
60
60
  <link rel="modulepreload" crossorigin href="/assets/chevron-right-DnHIvvcy.js">
61
61
  <link rel="modulepreload" crossorigin href="/assets/search-tM8K5zWU.js">
62
62
  <link rel="modulepreload" crossorigin href="/assets/file-store-DOxcU_7s.js">
63
- <link rel="modulepreload" crossorigin href="/assets/panel-store-Dy8-7E_g.js">
64
- <link rel="modulepreload" crossorigin href="/assets/tab-store-Dtg1_qL0.js">
63
+ <link rel="modulepreload" crossorigin href="/assets/panel-store-C8wwxBpn.js">
64
+ <link rel="modulepreload" crossorigin href="/assets/tab-store-CNas5Ny8.js">
65
65
  <link rel="stylesheet" crossorigin href="/assets/index-CKKoR3gY.css">
66
66
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
67
67
  <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
package/dist/web/sw.js CHANGED
@@ -1,2 +1,2 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"0ea9470156cf915e156f72538d3fef26","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/refresh-cw-BjrAbUJe.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/terminal-tab-B7ECmf95.js"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/database-viewer-CW60ytCl.js"},{"revision":null,"url":"assets/input-_LFQwhzd.js"},{"revision":null,"url":"assets/audio-preview-CQ-l5P8I.js"},{"revision":null,"url":"assets/data-grid-overlay-editor-C1UUm7Ob.js"},{"revision":null,"url":"assets/esm-DCbn6xno.js"},{"revision":null,"url":"assets/table-BzjWcs87.js"},{"revision":null,"url":"assets/text-wrap-DJz9Bgpa.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/treemap-KZPCXAKY-BvDgIWW9.js"},{"revision":null,"url":"assets/image-preview-ClY2xl1B.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-BJj3FH_w.js"},{"revision":null,"url":"assets/sparkles-CulWHe4c.js"},{"revision":null,"url":"assets/markdown-renderer-BmMmo0F-.js"},{"revision":null,"url":"assets/use-blob-url-DB4nNruT.js"},{"revision":null,"url":"assets/number-overlay-editor-CyEqxXcg.js"},{"revision":null,"url":"assets/search-tM8K5zWU.js"},{"revision":null,"url":"assets/globe-B4Ilypbs.js"},{"revision":null,"url":"assets/index-DkQ6jVSH.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/database-DOWH9-Vv.js"},{"revision":null,"url":"assets/tab-store-Dtg1_qL0.js"},{"revision":null,"url":"assets/dist-DVk8T0R5.js"},{"revision":null,"url":"assets/chat-tab-DbuBr2ax.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/scroll-area-BDi_FNzr.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-fAJMexNi.js"},{"revision":null,"url":"assets/glide-data-grid-nthEL3fk.css"},{"revision":null,"url":"assets/postgres-viewer-3y9VshZZ.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/settings-store-8FpQDjEA.js"},{"revision":null,"url":"assets/file-store-DOxcU_7s.js"},{"revision":null,"url":"assets/vendor-mermaid-D2KKkqNs.js"},{"revision":null,"url":"assets/dist-BoIkGNC8.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/vendor-xterm-D1P36hcr.js"},{"revision":null,"url":"assets/code-editor-DEa0t62y.js"},{"revision":null,"url":"assets/sqlite-viewer-Bj8oPYho.js"},{"revision":null,"url":"assets/lib-C2D8j3K3.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/ai-settings-section-AuV6Lzz2.js"},{"revision":null,"url":"assets/port-forwarding-tab-COdo70kU.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/react-DMIOAtcX.js"},{"revision":null,"url":"assets/utils-CQux7CsO.js"},{"revision":null,"url":"assets/csv-parser-Dly5nqE1.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/diff-viewer-BfatMgWw.js"},{"revision":null,"url":"assets/use-monaco-theme-DEI-tJAh.js"},{"revision":null,"url":"assets/vendor-ui-UXCWAcmi.js"},{"revision":null,"url":"assets/code-DGBecc50.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/settings-tab-sYavdJk-.js"},{"revision":null,"url":"assets/github-dark-dimmed.min-BrpRStFV.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/arrow-up-Rcw6_KKu.js"},{"revision":null,"url":"assets/file-exclamation-point-BwzaQ50n.js"},{"revision":null,"url":"assets/api-client-DIhJ5qVW.js"},{"revision":null,"url":"assets/pdf-preview-YylEP_Su.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DkybKTt9.js"},{"revision":null,"url":"assets/info-3K5VOQVL-D8uyBfC_.js"},{"revision":null,"url":"assets/csv-preview-B3Dyhgho.js"},{"revision":null,"url":"assets/sql-query-editor-DlBYx1Ye.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/extension-webview-DKSDoW_g.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/data-grid-types-D2cHE8hx.js"},{"revision":null,"url":"assets/panel-store-Dy8-7E_g.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-ClRAoBAn.js"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/video-preview-CT78iZBo.js"},{"revision":null,"url":"assets/keybindings-store-DrAeg6Gw.js"},{"revision":null,"url":"assets/conflict-editor-D5H9urYy.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BKfHRBz7.js"},{"revision":null,"url":"assets/notification-store-Bukl8bKo.js"},{"revision":null,"url":"assets/x-BPReZWnP.js"},{"revision":null,"url":"assets/glide-data-grid-Bx48618B.js"},{"revision":null,"url":"assets/chevron-right-DnHIvvcy.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/api-settings-C3T95dWg.js"},{"revision":null,"url":"assets/katex-C10ndCVt.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/index-CKKoR3gY.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.svg`,badge:`/icon-192.svg`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
1
+ try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"be912fbe9b241984d45d42adc15f1561","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":null,"url":"assets/refresh-cw-BjrAbUJe.js"},{"revision":null,"url":"assets/panel-store-C8wwxBpn.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":null,"url":"assets/input-_LFQwhzd.js"},{"revision":null,"url":"assets/data-grid-overlay-editor-C1UUm7Ob.js"},{"revision":null,"url":"assets/esm-DCbn6xno.js"},{"revision":null,"url":"assets/glide-data-grid-D-qQqqp7.js"},{"revision":null,"url":"assets/table-BzjWcs87.js"},{"revision":null,"url":"assets/text-wrap-DJz9Bgpa.js"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/treemap-KZPCXAKY-BvDgIWW9.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-BJj3FH_w.js"},{"revision":null,"url":"assets/sparkles-CulWHe4c.js"},{"revision":null,"url":"assets/use-blob-url-DB4nNruT.js"},{"revision":null,"url":"assets/number-overlay-editor-CyEqxXcg.js"},{"revision":null,"url":"assets/search-tM8K5zWU.js"},{"revision":null,"url":"assets/globe-B4Ilypbs.js"},{"revision":null,"url":"assets/github.min-D2BCvnWf.css"},{"revision":null,"url":"assets/database-DOWH9-Vv.js"},{"revision":null,"url":"assets/code-editor-MXnkYRLp.js"},{"revision":null,"url":"assets/image-preview-Dc6AiqYX.js"},{"revision":null,"url":"assets/dist-DVk8T0R5.js"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/scroll-area-BDi_FNzr.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-fAJMexNi.js"},{"revision":null,"url":"assets/glide-data-grid-nthEL3fk.css"},{"revision":null,"url":"assets/audio-preview-CILFIsuu.js"},{"revision":null,"url":"assets/sqlite-viewer-BkpONSGa.js"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/sql-query-editor-B5Ndypxp.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/settings-store-8FpQDjEA.js"},{"revision":null,"url":"assets/file-store-DOxcU_7s.js"},{"revision":null,"url":"assets/vendor-mermaid-D2KKkqNs.js"},{"revision":null,"url":"assets/dist-BoIkGNC8.js"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/vendor-xterm-D1P36hcr.js"},{"revision":null,"url":"assets/chat-tab-DBYwH_Aa.js"},{"revision":null,"url":"assets/lib-C2D8j3K3.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/ai-settings-section-AuV6Lzz2.js"},{"revision":null,"url":"assets/pdf-preview-zs9QdgDp.js"},{"revision":null,"url":"assets/terminal-tab-BgMCsdeN.js"},{"revision":null,"url":"assets/conflict-editor-C6wH5wV6.js"},{"revision":null,"url":"assets/index-8_rE2Q1-.js"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/react-DMIOAtcX.js"},{"revision":null,"url":"assets/utils-CQux7CsO.js"},{"revision":null,"url":"assets/csv-parser-Dly5nqE1.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/use-monaco-theme-DEI-tJAh.js"},{"revision":null,"url":"assets/vendor-ui-UXCWAcmi.js"},{"revision":null,"url":"assets/code-DGBecc50.js"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/github-dark-dimmed.min-BrpRStFV.css"},{"revision":null,"url":"assets/port-forwarding-tab-sArYx1nt.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/arrow-up-Rcw6_KKu.js"},{"revision":null,"url":"assets/file-exclamation-point-BwzaQ50n.js"},{"revision":null,"url":"assets/api-client-DIhJ5qVW.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-DkybKTt9.js"},{"revision":null,"url":"assets/info-3K5VOQVL-D8uyBfC_.js"},{"revision":null,"url":"assets/csv-preview-B3Dyhgho.js"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/markdown-renderer-CNQ8I0Dk.js"},{"revision":null,"url":"assets/tab-store-CNas5Ny8.js"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/data-grid-types-D2cHE8hx.js"},{"revision":null,"url":"assets/extension-webview-B56ZfvoD.js"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-ClRAoBAn.js"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/video-preview-w8ZAy8av.js"},{"revision":null,"url":"assets/settings-tab-CGWhVzQm.js"},{"revision":null,"url":"assets/database-viewer-BjUruZLv.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BKfHRBz7.js"},{"revision":null,"url":"assets/notification-store-BiZaLXop.js"},{"revision":null,"url":"assets/x-BPReZWnP.js"},{"revision":null,"url":"assets/chevron-right-DnHIvvcy.js"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/api-settings-C3T95dWg.js"},{"revision":null,"url":"assets/katex-C10ndCVt.js"},{"revision":null,"url":"assets/diff-viewer-B_nU7bQi.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/postgres-viewer-khk7N7cd.js"},{"revision":null,"url":"assets/index-CKKoR3gY.css"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/keybindings-store-COJD5O6M.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"26dccd02a2ef7522892015154f5e3680","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.svg`,badge:`/icon-192.svg`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
2
2
  //# sourceMappingURL=sw.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.13.49",
3
+ "version": "0.13.50",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",