summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components/server/server-row.tsx
blob: 63a40bac2411dfe00b31d52c5342f41b6f801c08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { Tooltip } from "@opencode-ai/ui/tooltip"
import {
  children,
  createEffect,
  createMemo,
  createSignal,
  type JSXElement,
  onCleanup,
  onMount,
  type ParentProps,
  Show,
} from "solid-js"
import { useLanguage } from "@/context/language"
import { type ServerConnection, serverName } from "@/context/server"
import type { ServerHealth } from "@/utils/server-health"

interface ServerRowProps extends ParentProps {
  conn: ServerConnection.Any
  status?: ServerHealth
  class?: string
  nameClass?: string
  versionClass?: string
  dimmed?: boolean
  badge?: JSXElement
  showCredentials?: boolean
}

export function ServerRow(props: ServerRowProps) {
  const language = useLanguage()
  const [truncated, setTruncated] = createSignal(false)
  let nameRef: HTMLSpanElement | undefined
  let versionRef: HTMLSpanElement | undefined
  const name = createMemo(() => serverName(props.conn))

  const check = () => {
    const nameTruncated = nameRef ? nameRef.scrollWidth > nameRef.clientWidth : false
    const versionTruncated = versionRef ? versionRef.scrollWidth > versionRef.clientWidth : false
    setTruncated(nameTruncated || versionTruncated)
  }

  createEffect(() => {
    name()
    props.conn.http.url
    props.status?.version
    queueMicrotask(check)
  })

  onMount(() => {
    check()
    if (typeof ResizeObserver !== "function") return
    const observer = new ResizeObserver(check)
    if (nameRef) observer.observe(nameRef)
    if (versionRef) observer.observe(versionRef)
    onCleanup(() => observer.disconnect())
  })

  const tooltipValue = () => (
    <span class="flex items-center gap-2">
      <span>{serverName(props.conn, true)}</span>
      <Show when={props.status?.version}>
        <span class="text-text-invert-weak">v{props.status?.version}</span>
      </Show>
    </span>
  )

  const badge = children(() => props.badge)

  return (
    <Tooltip
      class="flex-1 min-w-0"
      value={tooltipValue()}
      contentStyle={{ "max-width": "none", "white-space": "nowrap" }}
      placement="top-start"
      inactive={!truncated() && !props.conn.displayName}
    >
      <div class={props.class} classList={{ "opacity-50": props.dimmed }}>
        <div class="flex flex-col items-start min-w-0 w-full">
          <div class="flex flex-row items-center gap-2 min-w-0 w-full">
            <span ref={nameRef} class={`${props.nameClass ?? "truncate"} min-w-0`}>
              {name()}
            </span>
            <Show
              when={badge()}
              fallback={
                <Show when={props.status?.version}>
                  <span
                    ref={versionRef}
                    class={`${props.versionClass ?? "text-text-weak text-14-regular truncate"} min-w-0`}
                  >
                    v{props.status?.version}
                  </span>
                </Show>
              }
            >
              {(badge) => badge()}
            </Show>
          </div>
          <Show when={props.showCredentials && props.conn.type === "http" && props.conn}>
            {(conn) => (
              <div class="flex flex-row gap-3">
                <span>
                  {conn().http.username ? (
                    <span class="text-text-weak">{conn().http.username}</span>
                  ) : (
                    <span class="text-text-weaker">{language.t("server.row.noUsername")}</span>
                  )}
                </span>
                {conn().http.password && <span class="text-text-weak">••••••••</span>}
              </div>
            )}
          </Show>
        </div>
        {props.children}
      </div>
    </Tooltip>
  )
}

export function ServerHealthIndicator(props: { health?: ServerHealth }) {
  return (
    <div
      classList={{
        "size-1.5 rounded-full shrink-0": true,
        "bg-icon-success-base": props.health?.healthy === true,
        "bg-icon-critical-base": props.health?.healthy === false,
        "bg-border-weak-base": props.health === undefined,
      }}
    />
  )
}