Skip to content

Commit 07dfa46

Browse files
feat: add TypeScript LSP status monitoring and restart functionality in workspace footer
1 parent f262582 commit 07dfa46

1 file changed

Lines changed: 104 additions & 1 deletion

File tree

  • src/pages/workspace/components/footer

src/pages/workspace/components/footer/index.tsx

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import { useProjectStore } from "@/stores/project";
33
import { useTerminalStore } from "@/stores/terminal";
44
import { detectLineEnding, detectIndentation, detectEncoding, getLanguageFromExtension } from "@/stores/buffers/utils";
55
import { Button } from "@/components/ui/button";
6-
import { Terminal, Check } from "lucide-react";
6+
import { Terminal, Check, RefreshCw, AlertCircle, Loader2 } from "lucide-react";
77
import React, { useState, useEffect, useRef } from "react";
88
import { GitBranchSwitcher } from "./git-branch-switcher";
99
import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover";
10+
import { typescriptLSPClient } from '@/services/typescript-lsp';
11+
// LSP status types
12+
type LSPStatus = 'running' | 'loading' | 'error' | 'stopped';
13+
1014
import { Switch } from "@/components/ui/switch";
1115

1216

@@ -18,6 +22,54 @@ export function WorkspaceFooter() {
1822
const { buffers, activeBufferId } = useBufferStore();
1923
const { isVisible: isTerminalVisible, setVisible: setTerminalVisible, tabs, createTab } = useTerminalStore();
2024

25+
// LSP status state
26+
const [lspStatus, setLspStatus] = useState<LSPStatus>('loading');
27+
const [lspError, setLspError] = useState<string | null>(null);
28+
const [lspPopoverOpen, setLspPopoverOpen] = useState(false);
29+
const [lspRestarting, setLspRestarting] = useState(false);
30+
31+
// Poll LSP status
32+
useEffect(() => {
33+
let mounted = true;
34+
async function pollStatus() {
35+
setLspStatus('loading');
36+
setLspError(null);
37+
try {
38+
const status = await typescriptLSPClient.getStatus();
39+
if (!mounted) return;
40+
if (status.isRunning) {
41+
setLspStatus('running');
42+
} else {
43+
setLspStatus('stopped');
44+
}
45+
} catch (e: any) {
46+
setLspStatus('error');
47+
setLspError(e?.message || 'Unknown error');
48+
}
49+
}
50+
pollStatus();
51+
const interval = setInterval(pollStatus, 4000);
52+
return () => { mounted = false; clearInterval(interval); };
53+
}, []);
54+
55+
// Restart LSP
56+
const handleRestartLSP = async () => {
57+
setLspRestarting(true);
58+
setLspError(null);
59+
setLspStatus('loading');
60+
try {
61+
if (currentProject) {
62+
await typescriptLSPClient.initialize(currentProject);
63+
}
64+
setLspStatus('running');
65+
} catch (e: any) {
66+
setLspStatus('error');
67+
setLspError(e?.message || 'Failed to restart LSP');
68+
} finally {
69+
setLspRestarting(false);
70+
}
71+
};
72+
2173

2274
// Index status state
2375
const [indexStatus, setIndexStatus] = useState<{
@@ -271,6 +323,57 @@ export function WorkspaceFooter() {
271323
<div className="flex items-center justify-between w-full text-xs text-muted-foreground">
272324
<div className="flex items-center gap-4">
273325
<GitBranchSwitcher />
326+
{/* LSP Status Button */}
327+
<Popover open={lspPopoverOpen} onOpenChange={setLspPopoverOpen}>
328+
<PopoverTrigger asChild>
329+
<Button
330+
variant="ghost"
331+
size="sm"
332+
className="h-5 px-2 flex items-center gap-1 hover:bg-gray-700"
333+
onClick={() => setLspPopoverOpen(true)}
334+
aria-label="LSP Status"
335+
>
336+
{lspStatus === 'loading' && <Loader2 className="h-3 w-3 animate-spin text-yellow-400" />}
337+
{lspStatus === 'running' && <Check className="h-3 w-3 text-green-400" />}
338+
{lspStatus === 'error' && <AlertCircle className="h-3 w-3 text-red-400" />}
339+
{lspStatus === 'stopped' && <AlertCircle className="h-3 w-3 text-gray-400" />}
340+
<span className="hidden sm:inline text-xs">
341+
{lspStatus === 'loading' && 'Typescript'}
342+
{lspStatus === 'running' && 'Typescript'}
343+
{lspStatus === 'error' && 'Typescript: Error'}
344+
{lspStatus === 'stopped' && 'Typescript: Stopped'}
345+
</span>
346+
</Button>
347+
</PopoverTrigger>
348+
<PopoverContent className="w-64 p-4" align="start">
349+
<div className="flex flex-col gap-2">
350+
<div className="font-bold text-white text-xs mb-1">TypeScript LSP Status</div>
351+
<div className="flex items-center gap-2">
352+
{lspStatus === 'loading' && <Loader2 className="h-4 w-4 animate-spin text-yellow-400" />}
353+
{lspStatus === 'running' && <Check className="h-4 w-4 text-green-400" />}
354+
{lspStatus === 'error' && <AlertCircle className="h-4 w-4 text-red-400" />}
355+
{lspStatus === 'stopped' && <AlertCircle className="h-4 w-4 text-gray-400" />}
356+
<span className="text-xs">
357+
{lspStatus === 'loading' && 'Loading...'}
358+
{lspStatus === 'running' && 'Running'}
359+
{lspStatus === 'error' && 'Error'}
360+
{lspStatus === 'stopped' && 'Stopped'}
361+
</span>
362+
</div>
363+
{lspError && <div className="text-xs text-red-400">{lspError}</div>}
364+
<Button
365+
size="sm"
366+
variant="secondary"
367+
className="mt-2 flex items-center gap-1"
368+
onClick={handleRestartLSP}
369+
disabled={lspRestarting || lspStatus === 'loading'}
370+
>
371+
<RefreshCw className={lspRestarting ? 'animate-spin h-3 w-3' : 'h-3 w-3'} />
372+
{lspRestarting ? 'Restarting...' : 'Restart LSP'}
373+
</Button>
374+
</div>
375+
</PopoverContent>
376+
</Popover>
274377
<Button
275378
variant="ghost"
276379
size="sm"

0 commit comments

Comments
 (0)