@telorun/debug-ui 0.5.0 → 0.6.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/debug-ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Browser-safe debug event UI for Telo: wire types, filtering, SSE client, and React components for watching a running app's event stream.",
5
5
  "keywords": [
6
6
  "telo",
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode, useMemo, useState } from "react";
2
- import type { AppEndpoint } from "../endpoints.js";
2
+ import type { AppEndpoint, EndpointReachability } from "../endpoints.js";
3
3
  import { distinctSuffixes, type EventFilter, matchesFilter } from "../filter.js";
4
4
  import { type DebugTheme, loadStoredTheme, storeTheme, useResolvedTheme } from "../theme.js";
5
5
  import { type DebugEvent, type DebugFrame, isLogFrame } from "../wire.js";
@@ -34,6 +34,9 @@ export interface DebugPanelProps {
34
34
  * header. The standalone {@link DebugWatcher} sources these from the producer's
35
35
  * handshake; the editor passes its resolved run endpoints. */
36
36
  endpoints?: readonly AppEndpoint[];
37
+ /** Per-port reachability for the endpoint badges (spinner / ok / error). The
38
+ * editor sources it from the runner's `reachability` events. */
39
+ endpointReachability?: ReadonlyMap<number, EndpointReachability>;
37
40
  /** Color theme. Defaults to `"system"` (follows the OS); an embedding host
38
41
  * (e.g. the editor) passes its own resolved `"light"` / `"dark"`. */
39
42
  theme?: DebugTheme;
@@ -58,6 +61,7 @@ export function DebugPanel({
58
61
  logsSlot,
59
62
  defaultTab = "graph",
60
63
  endpoints,
64
+ endpointReachability,
61
65
  theme,
62
66
  }: DebugPanelProps) {
63
67
  const [tab, setTab] = useState<Tab>(defaultTab);
@@ -109,7 +113,9 @@ export function DebugPanel({
109
113
  Logs{logsSlot ? null : <span className="tdbg-tabcount">{logs.length}</span>}
110
114
  </button>
111
115
  <span className="tdbg-tabs-spacer" />
112
- {endpoints && endpoints.length > 0 && <EndpointLinks endpoints={endpoints} />}
116
+ {endpoints && endpoints.length > 0 && (
117
+ <EndpointLinks endpoints={endpoints} reachability={endpointReachability} />
118
+ )}
113
119
  <button className="tdbg-btn" onClick={onTogglePause}>
114
120
  {paused ? "Resume" : "Pause"}
115
121
  </button>
@@ -1,14 +1,23 @@
1
- import { ExternalLink } from "lucide-react";
2
- import { type AppEndpoint, endpointHref, endpointLabel } from "../endpoints.js";
1
+ import { CheckCircle2, ExternalLink, Loader2, XCircle } from "lucide-react";
2
+ import {
3
+ type AppEndpoint,
4
+ type EndpointReachability,
5
+ endpointHref,
6
+ endpointLabel,
7
+ } from "../endpoints.js";
3
8
 
4
9
  export interface EndpointLinksProps {
5
10
  endpoints: readonly AppEndpoint[];
11
+ /** Per-port reachability, rendered as a status icon on each tcp link. A tcp
12
+ * endpoint with no entry yet shows the `checking` spinner. */
13
+ reachability?: ReadonlyMap<number, EndpointReachability>;
6
14
  }
7
15
 
8
16
  /** The running application's exposed addresses, rendered in the panel header.
9
- * tcp endpoints are clickable links to the app; udp endpoints show as a plain
10
- * label (not reachable over http). Renders nothing when there are no endpoints. */
11
- export function EndpointLinks({ endpoints }: EndpointLinksProps) {
17
+ * tcp endpoints are clickable links to the app, each carrying a reachability
18
+ * status icon (spinner while checking, ok/error once known); udp endpoints show
19
+ * as a plain label. Renders nothing when there are no endpoints. */
20
+ export function EndpointLinks({ endpoints, reachability }: EndpointLinksProps) {
12
21
  if (endpoints.length === 0) return null;
13
22
  return (
14
23
  <span className="tdbg-endpoints">
@@ -32,11 +41,42 @@ export function EndpointLinks({ endpoints }: EndpointLinksProps) {
32
41
  rel="noreferrer"
33
42
  title={`Open ${href}`}
34
43
  >
35
- <ExternalLink size={11} aria-hidden />
44
+ {reachability && (
45
+ <ReachabilityIcon state={reachability.get(endpoint.port) ?? "checking"} />
46
+ )}
36
47
  {label}
48
+ <ExternalLink size={11} aria-hidden />
37
49
  </a>
38
50
  );
39
51
  })}
40
52
  </span>
41
53
  );
42
54
  }
55
+
56
+ function ReachabilityIcon({ state }: { state: EndpointReachability }) {
57
+ if (state === "reachable") {
58
+ return (
59
+ <CheckCircle2
60
+ size={11}
61
+ className="tdbg-endpoint-status tdbg-endpoint-status--reachable"
62
+ aria-label="reachable"
63
+ />
64
+ );
65
+ }
66
+ if (state === "unreachable") {
67
+ return (
68
+ <XCircle
69
+ size={11}
70
+ className="tdbg-endpoint-status tdbg-endpoint-status--unreachable"
71
+ aria-label="not reachable"
72
+ />
73
+ );
74
+ }
75
+ return (
76
+ <Loader2
77
+ size={11}
78
+ className="tdbg-endpoint-status tdbg-endpoint-status--checking tdbg-spin"
79
+ aria-label="checking reachability"
80
+ />
81
+ );
82
+ }
@@ -88,6 +88,26 @@
88
88
  .tdbg-endpoint-link:hover {
89
89
  border-color: var(--tdbg-accent);
90
90
  }
91
+ .tdbg-endpoint-status {
92
+ flex: none;
93
+ }
94
+ .tdbg-endpoint-status--checking {
95
+ color: var(--tdbg-muted);
96
+ }
97
+ .tdbg-endpoint-status--reachable {
98
+ color: var(--tdbg-good);
99
+ }
100
+ .tdbg-endpoint-status--unreachable {
101
+ color: var(--tdbg-bad);
102
+ }
103
+ .tdbg-spin {
104
+ animation: tdbg-spin 0.8s linear infinite;
105
+ }
106
+ @keyframes tdbg-spin {
107
+ to {
108
+ transform: rotate(360deg);
109
+ }
110
+ }
91
111
 
92
112
  .tdbg-bar {
93
113
  position: sticky;
package/src/endpoints.ts CHANGED
@@ -13,6 +13,12 @@ export interface AppEndpoint {
13
13
  url?: string;
14
14
  }
15
15
 
16
+ /** Per-port reachability of a running app's endpoint, surfaced as a status icon
17
+ * beside the link: a spinner while `checking`, ok when `reachable`, error when
18
+ * `unreachable`. Mirrors the runner contract's `ReachabilityState`, kept local so
19
+ * debug-ui stays browser-safe with no dependency on the runner package. */
20
+ export type EndpointReachability = "checking" | "reachable" | "unreachable";
21
+
16
22
  /** The link a tcp endpoint resolves to, or `null` for a non-linkable (udp)
17
23
  * endpoint. Prefers an explicit `url`; otherwise derives `http://host:port`. */
18
24
  export function endpointHref(endpoint: AppEndpoint): string | null {