|
|
@@ -9,6 +9,7 @@ import { ServerConnection, useServer } from "@/context/server"
|
|
|
import { useServerSDK } from "@/context/server-sdk"
|
|
|
import { useSync } from "@/context/sync"
|
|
|
import { useGlobal } from "@/context/global"
|
|
|
+import { hasNonBlockingServiceIssue, serverStatusDotClass } from "./status-popover-indicator"
|
|
|
|
|
|
const Body = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverBody })))
|
|
|
const ServerBody = lazy(() => import("./status-popover-body").then((x) => ({ default: x.StatusPopoverServerBody })))
|
|
|
@@ -19,16 +20,14 @@ export function StatusPopover() {
|
|
|
const global = useGlobal()
|
|
|
const sync = useSync()
|
|
|
const [shown, setShown] = createSignal(false)
|
|
|
- const ready = createMemo(() => global.servers.health[server.key]?.healthy === false || sync().data.mcp_ready)
|
|
|
- const mcpIssue = createMemo(() => {
|
|
|
- const mcp = Object.values(sync().data.mcp ?? {})
|
|
|
- const failed = mcp.some((item) => item.status === "failed" || item.status === "needs_client_registration")
|
|
|
- const warn = mcp.some((item) => item.status === "needs_auth")
|
|
|
- if (failed) return "critical" as const
|
|
|
- if (warn) return "warning" as const
|
|
|
- })
|
|
|
- const serverHealthy = () => global.servers.health[server.key]?.healthy === true
|
|
|
- const healthy = createMemo(() => global.servers.health[server.key]?.healthy === true && !mcpIssue())
|
|
|
+ const serverHealth = () => global.servers.health[server.key]?.healthy
|
|
|
+ const ready = createMemo(() => serverHealth() === false || (sync().data.mcp_ready && sync().data.lsp_ready))
|
|
|
+ const issue = createMemo(() =>
|
|
|
+ hasNonBlockingServiceIssue({
|
|
|
+ mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
|
|
+ lsp: (sync().data.lsp ?? []).map((item) => item.status),
|
|
|
+ }),
|
|
|
+ )
|
|
|
|
|
|
return (
|
|
|
<Popover
|
|
|
@@ -47,13 +46,11 @@ export function StatusPopover() {
|
|
|
<Icon name={shown() ? "status-active" : "status"} size="small" />
|
|
|
</div>
|
|
|
<div
|
|
|
- classList={{
|
|
|
- "absolute -top-px -right-px size-1.5 rounded-full": true,
|
|
|
- "bg-icon-success-base": ready() && healthy(),
|
|
|
- "bg-icon-warning-base": ready() && serverHealthy() && mcpIssue() === "warning",
|
|
|
- "bg-icon-critical-base": serverHealthy() || (ready() && serverHealthy() && mcpIssue() === "critical"),
|
|
|
- "bg-border-weak-base": serverHealthy() || !ready(),
|
|
|
- }}
|
|
|
+ class={`absolute -top-px -right-px size-1.5 rounded-full ${serverStatusDotClass({
|
|
|
+ ready: ready(),
|
|
|
+ serverHealth: serverHealth(),
|
|
|
+ issue: issue(),
|
|
|
+ })}`}
|
|
|
/>
|
|
|
</div>
|
|
|
}
|
|
|
@@ -87,21 +84,18 @@ function DirectoryStatusPopover() {
|
|
|
const sync = useSync()
|
|
|
const [shown, setShown] = createSignal(false)
|
|
|
const serverHealth = () => global.servers.health[ServerConnection.key(server().server)]?.healthy
|
|
|
- const ready = createMemo(() => serverHealth() === false || sync().data.mcp_ready)
|
|
|
- const mcpIssue = createMemo(() => {
|
|
|
- const mcp = Object.values(sync().data.mcp ?? {})
|
|
|
- const failed = mcp.some((item) => item.status === "failed" || item.status === "needs_client_registration")
|
|
|
- const warn = mcp.some((item) => item.status === "needs_auth")
|
|
|
- if (failed) return "critical" as const
|
|
|
- if (warn) return "warning" as const
|
|
|
- })
|
|
|
- const healthy = createMemo(() => serverHealth() === true && !mcpIssue())
|
|
|
+ const ready = createMemo(() => serverHealth() === false || (sync().data.mcp_ready && sync().data.lsp_ready))
|
|
|
+ const issue = createMemo(() =>
|
|
|
+ hasNonBlockingServiceIssue({
|
|
|
+ mcp: Object.values(sync().data.mcp ?? {}).map((item) => item.status),
|
|
|
+ lsp: (sync().data.lsp ?? []).map((item) => item.status),
|
|
|
+ }),
|
|
|
+ )
|
|
|
const state = createMemo<StatusPopoverState>(() => ({
|
|
|
shown: shown(),
|
|
|
ready: ready(),
|
|
|
- healthy: healthy(),
|
|
|
serverHealth: serverHealth(),
|
|
|
- issue: mcpIssue(),
|
|
|
+ issue: issue(),
|
|
|
label: language.t("status.popover.trigger"),
|
|
|
onOpenChange: setShown,
|
|
|
body: () => (
|
|
|
@@ -123,8 +117,8 @@ function ServerStatusPopover() {
|
|
|
const state = createMemo<StatusPopoverState>(() => ({
|
|
|
shown: shown(),
|
|
|
ready: serverHealth() !== undefined,
|
|
|
- healthy: serverHealth() === true,
|
|
|
serverHealth: serverHealth(),
|
|
|
+ issue: false,
|
|
|
label: language.t("status.popover.trigger"),
|
|
|
onOpenChange: setShown,
|
|
|
body: () => (
|
|
|
@@ -140,9 +134,8 @@ function ServerStatusPopover() {
|
|
|
type StatusPopoverState = {
|
|
|
shown: boolean
|
|
|
ready: boolean
|
|
|
- healthy: boolean
|
|
|
serverHealth: boolean | undefined
|
|
|
- issue?: "critical" | "warning"
|
|
|
+ issue: boolean
|
|
|
label: string
|
|
|
onOpenChange: (value: boolean) => void
|
|
|
body: () => JSX.Element
|
|
|
@@ -161,16 +154,6 @@ function StatusPopoverBody(props: { shown: boolean; children: JSX.Element }) {
|
|
|
}
|
|
|
|
|
|
function StatusPopoverView(props: { state: StatusPopoverState }) {
|
|
|
- const statusDotClass = () => ({
|
|
|
- "absolute rounded-full": true,
|
|
|
- "bg-icon-success-base": props.state.ready && props.state.healthy,
|
|
|
- "bg-icon-warning-base": props.state.ready && props.state.serverHealth === true && props.state.issue === "warning",
|
|
|
- "bg-icon-critical-base":
|
|
|
- props.state.serverHealth === false ||
|
|
|
- (props.state.ready && props.state.serverHealth === true && props.state.issue === "critical"),
|
|
|
- "bg-border-weak-base": props.state.serverHealth === undefined || !props.state.ready,
|
|
|
- })
|
|
|
-
|
|
|
const popoverProps = {
|
|
|
class:
|
|
|
"[&_[data-slot=popover-body]]:p-0 w-[360px] max-w-[calc(100vw-40px)] bg-transparent border-0 shadow-none rounded-xl",
|
|
|
@@ -195,8 +178,7 @@ function StatusPopoverView(props: { state: StatusPopoverState }) {
|
|
|
<div class="relative size-4">
|
|
|
<IconV2 name={props.state.shown ? "status-active" : "status"} />
|
|
|
<div
|
|
|
- classList={statusDotClass()}
|
|
|
- class="-top-1 -right-1 size-2 border border-[var(--v2-background-bg-deep)]"
|
|
|
+ class={`absolute -top-1 -right-1 size-2 rounded-full border border-[var(--v2-background-bg-deep)] ${serverStatusDotClass(props.state)}`}
|
|
|
/>
|
|
|
</div>
|
|
|
}
|