dialog-status.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { TextAttributes } from "@opentui/core"
  2. import { useTheme } from "../context/theme"
  3. import { useSync } from "@tui/context/sync"
  4. import { For, Match, Switch, Show, createMemo } from "solid-js"
  5. export type DialogStatusProps = {}
  6. export function DialogStatus() {
  7. const sync = useSync()
  8. const { theme } = useTheme()
  9. const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled))
  10. return (
  11. <box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
  12. <box flexDirection="row" justifyContent="space-between">
  13. <text fg={theme.text} attributes={TextAttributes.BOLD}>
  14. Status
  15. </text>
  16. <text fg={theme.textMuted}>esc</text>
  17. </box>
  18. <Show when={Object.keys(sync.data.mcp).length > 0} fallback={<text fg={theme.text}>No MCP Servers</text>}>
  19. <box>
  20. <text fg={theme.text}>{Object.keys(sync.data.mcp).length} MCP Servers</text>
  21. <For each={Object.entries(sync.data.mcp)}>
  22. {([key, item]) => (
  23. <box flexDirection="row" gap={1}>
  24. <text
  25. flexShrink={0}
  26. style={{
  27. fg: (
  28. {
  29. connected: theme.success,
  30. failed: theme.error,
  31. disabled: theme.textMuted,
  32. needs_auth: theme.warning,
  33. needs_client_registration: theme.error,
  34. } as Record<string, typeof theme.success>
  35. )[item.status],
  36. }}
  37. >
  38. </text>
  39. <text fg={theme.text} wrapMode="word">
  40. <b>{key}</b>{" "}
  41. <span style={{ fg: theme.textMuted }}>
  42. <Switch fallback={item.status}>
  43. <Match when={item.status === "connected"}>Connected</Match>
  44. <Match when={item.status === "failed" && item}>{(val) => val().error}</Match>
  45. <Match when={item.status === "disabled"}>Disabled in configuration</Match>
  46. <Match when={(item.status as string) === "needs_auth"}>
  47. Needs authentication (run: opencode mcp auth {key})
  48. </Match>
  49. <Match when={(item.status as string) === "needs_client_registration" && item}>
  50. {(val) => (val() as { error: string }).error}
  51. </Match>
  52. </Switch>
  53. </span>
  54. </text>
  55. </box>
  56. )}
  57. </For>
  58. </box>
  59. </Show>
  60. {sync.data.lsp.length > 0 && (
  61. <box>
  62. <text fg={theme.text}>{sync.data.lsp.length} LSP Servers</text>
  63. <For each={sync.data.lsp}>
  64. {(item) => (
  65. <box flexDirection="row" gap={1}>
  66. <text
  67. flexShrink={0}
  68. style={{
  69. fg: {
  70. connected: theme.success,
  71. error: theme.error,
  72. }[item.status],
  73. }}
  74. >
  75. </text>
  76. <text fg={theme.text} wrapMode="word">
  77. <b>{item.id}</b> <span style={{ fg: theme.textMuted }}>{item.root}</span>
  78. </text>
  79. </box>
  80. )}
  81. </For>
  82. </box>
  83. )}
  84. <Show when={enabledFormatters().length > 0} fallback={<text fg={theme.text}>No Formatters</text>}>
  85. <box>
  86. <text fg={theme.text}>{enabledFormatters().length} Formatters</text>
  87. <For each={enabledFormatters()}>
  88. {(item) => (
  89. <box flexDirection="row" gap={1}>
  90. <text
  91. flexShrink={0}
  92. style={{
  93. fg: theme.success,
  94. }}
  95. >
  96. </text>
  97. <text wrapMode="word" fg={theme.text}>
  98. <b>{item.name}</b>
  99. </text>
  100. </box>
  101. )}
  102. </For>
  103. </box>
  104. </Show>
  105. </box>
  106. )
  107. }