diff --git a/app/components/Attribute.tsx b/app/components/Attribute.tsx
index fad9457..9e2c7dd 100644
--- a/app/components/Attribute.tsx
+++ b/app/components/Attribute.tsx
@@ -1,23 +1,37 @@
-import { Check, Copy } from 'lucide-react';
+import { Check, Copy, Info } from 'lucide-react';
import cn from '~/utils/cn';
import toast from '~/utils/toast';
+import Tooltip from './Tooltip';
export interface AttributeProps {
name: string;
value: string;
+ tooltip?: string;
isCopyable?: boolean;
}
-export default function Attribute({ name, value, isCopyable }: AttributeProps) {
+export default function Attribute({
+ name,
+ value,
+ tooltip,
+ isCopyable,
+}: AttributeProps) {
return (
-
{name}
+ {tooltip ? (
+
+
+ {tooltip}
+
+ ) : undefined}
- ();
+ const { node, magic, users, agent, stats } = useLoaderData();
const [showRouting, setShowRouting] = useState(false);
const uiTags = useMemo(() => {
@@ -120,8 +123,8 @@ export default function Page() {
-
Subnets & Routing
+ Subnets & Routing
Subnets let you expose physical network routes onto Tailscale.{' '}
@@ -240,40 +243,156 @@ export default function Page() {
- Machine Details
-
-
-
-
-
-
-
-
-
- {magic ? (
+ Machine Details
+
+ Information about this machine’s network. Used to debug connection
+ issues.
+
+
+
+
+
+
+ {stats ? (
+ <>
+
+
+ >
+ ) : undefined}
+
- ) : undefined}
+
+
+
+ {magic ? (
+
+ ) : undefined}
+
+
+
+ Addresses
+
+
+
+
+ {magic ? (
+
+ ) : undefined}
+ {stats ? (
+ <>
+
+ Client Connectivity
+
+
+
+
+
+
+
+
+ >
+ ) : undefined}
+
);
}
+
+function getIpv4Address(addresses: string[]) {
+ for (const address of addresses) {
+ if (address.startsWith('100.')) {
+ // Return the first CGNAT address
+ return address;
+ }
+ }
+
+ return '—';
+}
+
+function getIpv6Address(addresses: string[]) {
+ for (const address of addresses) {
+ if (address.startsWith('fd')) {
+ // Return the first IPv6 address
+ return address;
+ }
+ }
+
+ return '—';
+}