import { createRequire } from 'node:module';const require = createRequire(import.meta.url);

// src/panels/echo/SpaceInfoPanel/SpaceInfoPanel.tsx
import React45, { useMemo as useMemo9, useState as useState13 } from "react";
import { MulticastObservable as MulticastObservable2 } from "@dxos/async";
import { SpaceState as SpaceState4 } from "@dxos/protocols/proto/dxos/client/services";
import { EdgeReplicationSetting } from "@dxos/protocols/proto/dxos/echo/metadata";
import { useMulticastObservable as useMulticastObservable2 } from "@dxos/react-hooks";
import { Toolbar as Toolbar3 } from "@dxos/react-ui";

// src/components/performance/Panel.tsx
import React from "react";
import { Icon } from "@dxos/react-ui";
import { mx } from "@dxos/ui-theme";
var Panel = ({ children, className, id, icon, title, info, padding = true, maxHeight = 240, open = true, onToggle }) => {
  return /* @__PURE__ */ React.createElement("div", {
    className: "flex flex-col overflow-hidden"
  }, /* @__PURE__ */ React.createElement("div", {
    className: "flex items-center justify-between pli-2 text-sm text-fine cursor-pointer",
    onClick: () => onToggle?.(id, !open)
  }, /* @__PURE__ */ React.createElement("div", {
    className: "flex items-center gap-2 plb-1"
  }, /* @__PURE__ */ React.createElement(Icon, {
    icon
  }), /* @__PURE__ */ React.createElement("span", {
    className: "truncate"
  }, title)), info), children && /* @__PURE__ */ React.createElement("div", {
    style: {
      maxHeight: open ? maxHeight ? `${maxHeight}px` : void 0 : 0
    },
    className: mx("flex flex-col is-full transition-all duration-200 ease-in-out", maxHeight ? "overflow-y-auto" : "bs-full overflow-hidden", padding && "pli-2", className)
  }, children));
};

// src/components/performance/StatsPanel.tsx
import React26, { useEffect as useEffect7, useState as useState9 } from "react";
import { getSyncSummary, useSyncState } from "@dxos/react-client/echo";
import { Icon as Icon4, Toggle } from "@dxos/react-ui";

// src/hooks/useCredentials.tsx
import { useEffect, useState } from "react";
import { useClient } from "@dxos/react-client";
var useCredentials = ({ spaceKey }) => {
  const client = useClient();
  const spacesService = client?.services?.services?.SpacesService;
  if (!spacesService) {
    throw new Error("Client services not available.");
  }
  const [credentials, setCredentials] = useState([]);
  useEffect(() => {
    if (!spaceKey) {
      return;
    }
    const newCredentials = [];
    const stream = spacesService.queryCredentials({
      spaceKey
    });
    stream.subscribe((credential) => {
      newCredentials.push(credential);
      setCredentials([
        ...newCredentials
      ]);
    });
    return () => {
      void stream.close();
    };
  }, [
    spaceKey?.toHex()
  ]);
  return credentials;
};

// src/hooks/useDevtoolsContext.tsx
import React2, { createContext, useContext, useState as useState2 } from "react";
var DevtoolsContext = /* @__PURE__ */ createContext(void 0);
var DevtoolsContextProvider = ({ children }) => {
  const state = useState2({});
  return /* @__PURE__ */ React2.createElement(DevtoolsContext.Provider, {
    value: state
  }, children);
};
var useDevtoolsState = () => {
  const [state] = useContext(DevtoolsContext);
  return state;
};
var useDevtoolsDispatch = () => {
  const [, dispatch] = useContext(DevtoolsContext);
  return dispatch;
};

// src/hooks/useFeedMessages.tsx
import { useEffect as useEffect2, useState as useState3 } from "react";
import { useDevtools, useStream } from "@dxos/react-client/devtools";
var useFeedMessages = ({ feedKey, maxBlocks = 100 }) => {
  const devtoolsHost = useDevtools();
  const { space } = useDevtoolsState();
  const [messages, setMessages] = useState3([]);
  const { blocks } = useStream(() => devtoolsHost.subscribeToFeedBlocks({
    spaceKey: space?.key,
    feedKey,
    maxBlocks
  }), {}, [
    space,
    feedKey
  ]);
  useEffect2(() => {
    setMessages(blocks ?? []);
  }, [
    blocks
  ]);
  useEffect2(() => {
    setMessages(blocks ?? []);
  }, [
    blocks
  ]);
  return messages;
};

// src/hooks/useMetadata.tsx
import { useDevtools as useDevtools2, useStream as useStream2 } from "@dxos/react-client/devtools";
var useMetadata = () => {
  const devtoolsHost = useDevtools2();
  const metadata = useStream2(() => devtoolsHost.subscribeToMetadata(), {}).metadata;
  return metadata;
};

// src/hooks/useProxiedClient.tsx
import { useState as useState4 } from "react";
import { Client, ClientServicesProxy } from "@dxos/react-client";
import { useAsyncEffect } from "@dxos/react-hooks";
var useProxiedClient = () => {
  const [client, setClient] = useState4();
  useAsyncEffect(async () => {
    const client2 = await createClientProxy();
    setClient(client2);
  }, []);
  return client;
};
var createClientProxy = async () => {
  const port = {
    send: async (message) => window.parent.postMessage({
      data: Array.from(message),
      source: "content-script"
    }, "*"),
    subscribe: (callback) => {
      const handler = (event) => {
        const message = event.data;
        if (typeof message !== "object" || message === null || message.source !== "dxos-client") {
          return;
        }
        callback(new Uint8Array(message.data));
      };
      window.addEventListener("message", handler);
      return () => window.removeEventListener("message", handler);
    }
  };
  const services = new ClientServicesProxy(port);
  const client = new Client({
    services
  });
  await client.initialize();
  return client;
};

// src/hooks/useRoutes.tsx
import React8, { useCallback } from "react";
import { useNavigate, useRoutes as useRouterRoutes } from "react-router-dom";

// src/containers/DataSpaceSelector.tsx
import * as localForage from "localforage";
import React3 from "react";
import { invariant } from "@dxos/invariant";
import { SpaceId, useSpaces } from "@dxos/react-client/echo";
import { useAsyncEffect as useAsyncEffect2 } from "@dxos/react-hooks";
import { Select } from "@dxos/react-ui";
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/devtools/src/containers/DataSpaceSelector.tsx";
var DataSpaceSelector = () => {
  const spaces = useSpaces({
    all: true
  });
  const spacesInfo = useSpacesInfo();
  const { space } = useDevtoolsState();
  const setState = useDevtoolsDispatch();
  const handleSelect = (spaceId) => {
    const space2 = spaceId ? spaces.find((space3) => space3.id === spaceId) : void 0;
    const spaceInfo = space2 ? spacesInfo.find((spaceInfo2) => spaceInfo2.key.equals(space2.key)) : void 0;
    setState((state) => ({
      ...state,
      space: space2,
      spaceInfo
    }));
    if (spaceId) {
      void localForage.setItem("dxos.devtools.spaceId", spaceId);
    }
  };
  useAsyncEffect2(async () => {
    const spaceId = await localForage.getItem("dxos.devtools.spaceId");
    if (spaceId) {
      invariant(SpaceId.isValid(spaceId), void 0, {
        F: __dxlog_file,
        L: 39,
        S: void 0,
        A: [
          "SpaceId.isValid(spaceId)",
          ""
        ]
      });
      handleSelect(spaceId);
    }
  }, []);
  const getLabel = (space2) => {
    return space2?.isOpen ? space2?.properties.name ?? "New space" : "(closed)";
  };
  return /* @__PURE__ */ React3.createElement(Select.Root, {
    value: space?.id,
    onValueChange: (id) => {
      id && handleSelect?.(id);
    }
  }, /* @__PURE__ */ React3.createElement(Select.TriggerButton, {
    placeholder: "Select space"
  }), /* @__PURE__ */ React3.createElement(Select.Portal, null, /* @__PURE__ */ React3.createElement(Select.Content, null, /* @__PURE__ */ React3.createElement(Select.Viewport, null, spaces.map((space2) => /* @__PURE__ */ React3.createElement(Select.Option, {
    key: space2.id,
    value: space2.id
  }, /* @__PURE__ */ React3.createElement("div", {
    className: "flex items-center gap-2"
  }, /* @__PURE__ */ React3.createElement("span", {
    className: "font-mono text-neutral-250"
  }, space2.id.slice(0, 6)), getLabel(space2))))), /* @__PURE__ */ React3.createElement(Select.Arrow, null))));
};

// src/containers/EdgeSelector.tsx
import defaultsDeep from "lodash.defaultsdeep";
import React5, { useMemo as useMemo2 } from "react";
import { SaveConfig, Storage } from "@dxos/config";
import { useConfig } from "@dxos/react-client";

// src/containers/VaultSelector.tsx
import React4, { useMemo } from "react";
var targets = [
  "default",
  "ws://localhost:5001",
  "http://localhost:3967"
].map((value) => ({
  value,
  label: value
}));
var parseTarget = (value) => targets.find((target) => target.value === value) ?? targets[0];
var getTarget = () => {
  const searchProps = new URLSearchParams(window.location.search);
  const target = searchProps.get("target");
  return parseTarget(target ?? "");
};
var VaultSelector = () => {
  const vault = useMemo(() => getTarget(), [
    window.location.search
  ]);
  const handleSetVault = (value) => {
    const url = new URL(window.location.href);
    const target = parseTarget(value);
    url.searchParams.set("target", target.value);
    window.location.href = url.href;
  };
  return /* @__PURE__ */ React4.createElement(Select2, {
    value: vault.value,
    items: targets,
    onValueChange: (value) => handleSetVault(value)
  });
};

// src/containers/EdgeSelector.tsx
var edgeServers = [
  {
    value: "https://edge.dxos.workers.dev",
    label: "Dev"
  },
  {
    value: "https://edge-main.dxos.workers.dev",
    label: "Main"
  },
  {
    value: "https://edge-labs.dxos.workers.dev",
    label: "Labs"
  },
  {
    value: "https://edge-production.dxos.workers.dev",
    label: "Production"
  }
];
var EdgeSelector = () => {
  const config = useConfig();
  const target = useMemo2(() => getTarget(), [
    window.location.search
  ]);
  const handleSetSignalServer = async (value) => {
    const existing = await Storage();
    await SaveConfig(defaultsDeep({
      runtime: {
        services: {
          edge: {
            url: value
          }
        }
      }
    }, existing));
    window.location.reload();
  };
  return /* @__PURE__ */ React5.createElement(Select2, {
    disabled: target.value !== "default",
    value: config.values.runtime?.services?.edge?.url,
    items: edgeServers,
    onValueChange: (value) => handleSetSignalServer(value)
  });
};

// src/containers/RootContainer.tsx
import React6, { Suspense } from "react";
import { Link, Outlet, useLocation } from "react-router-dom";
import { DeviceKind, useDevices, useIdentity } from "@dxos/react-client/halo";
import { Icon as Icon2, ScrollArea } from "@dxos/react-ui";
import { mx as mx2 } from "@dxos/ui-theme";
var RootContainer = () => {
  const { pathname } = useLocation();
  return /* @__PURE__ */ React6.createElement("div", {
    className: "flex is-full bs-full overflow-hidden"
  }, /* @__PURE__ */ React6.createElement(Sidebar, null), /* @__PURE__ */ React6.createElement("div", {
    className: "flex flex-col grow overflow-hidden"
  }, /* @__PURE__ */ React6.createElement(ErrorBoundary, {
    key: pathname
  }, /* @__PURE__ */ React6.createElement(Suspense, null, /* @__PURE__ */ React6.createElement(Outlet, null)))));
};
var Sidebar = () => {
  const { pathname } = useLocation();
  const sections = useSections();
  return /* @__PURE__ */ React6.createElement(ScrollArea.Root, {
    orientation: "vertical",
    classNames: "is-[180px] border-ie border-separator"
  }, /* @__PURE__ */ React6.createElement(ScrollArea.Viewport, {
    classNames: "gap-4 divide-y divide-separator"
  }, sections.map((section) => /* @__PURE__ */ React6.createElement("div", {
    key: section.id
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "flex text-sm pis-4 plb-1"
  }, section.title), /* @__PURE__ */ React6.createElement("div", null, section.items?.map(({ id, title, icon }) => /* @__PURE__ */ React6.createElement("div", {
    key: id,
    className: mx2("flex items-center pis-4 gap-2", id === pathname && "bg-activeSurface")
  }, /* @__PURE__ */ React6.createElement(Icon2, {
    icon,
    size: 4
  }), /* @__PURE__ */ React6.createElement(Link, {
    to: id,
    className: "grow"
  }, /* @__PURE__ */ React6.createElement("span", null, title))))))), /* @__PURE__ */ React6.createElement("div", {
    className: "grow"
  }), /* @__PURE__ */ React6.createElement(Footer, null)));
};
var Footer = () => {
  const identity = useIdentity();
  const devices = useDevices();
  const device = devices.find(({ kind }) => kind === DeviceKind.CURRENT);
  return /* @__PURE__ */ React6.createElement("div", {
    className: "flex p-2"
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "flex flex-col is-full text-sm text-neutral-500"
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "grid grid-cols-2 gap-2"
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "text-neutral-300 text-right"
  }, "Identity"), /* @__PURE__ */ React6.createElement("div", {
    className: "font-mono"
  }, identity?.identityKey.truncate())), /* @__PURE__ */ React6.createElement("div", {
    className: "grid grid-cols-2 gap-2"
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "text-neutral-300 text-right"
  }, "Device"), /* @__PURE__ */ React6.createElement("div", {
    className: "font-mono"
  }, device?.deviceKey.truncate())), /* @__PURE__ */ React6.createElement("div", {
    className: "grid grid-cols-2 gap-2"
  }, /* @__PURE__ */ React6.createElement("div", {
    className: "text-neutral-300 text-right"
  }, "Version"), /* @__PURE__ */ React6.createElement("div", {
    className: "font-mono"
  }, process.env.PACKAGE_VERSION ?? "DEV"))));
};

// src/containers/SpaceSelector.tsx
import * as localForage2 from "localforage";
import React7 from "react";
import { PublicKey } from "@dxos/react-client";
import { useSpaces as useSpaces2 } from "@dxos/react-client/echo";
import { useIdentity as useIdentity2 } from "@dxos/react-client/halo";
import { useAsyncEffect as useAsyncEffect3 } from "@dxos/react-hooks";
import { humanize } from "@dxos/util";
var SpaceSelector = () => {
  const spaces = useSpaces2({
    all: true
  });
  const { space, haloSpaceKey: stateHaloSpaceKey } = useDevtoolsState();
  const spacesInfo = useSpacesInfo();
  const setState = useDevtoolsDispatch();
  const identity = useIdentity2();
  const handleSelect = (spaceKey) => {
    setState((state) => {
      const haloSpaceKey = identity?.spaceKey;
      if (haloSpaceKey && spaceKey?.equals(haloSpaceKey)) {
        return {
          ...state,
          haloSpaceKey: spaceKey,
          space: void 0,
          spaceInfo: void 0
        };
      } else {
        return {
          ...state,
          space: spaceKey ? spaces.find((space2) => space2.key.equals(spaceKey)) : void 0,
          spaceInfo: spaceKey ? spacesInfo.find((spaceInfo) => spaceInfo.key.equals(spaceKey)) : void 0,
          haloSpaceKey: void 0
        };
      }
    });
    if (spaceKey) {
      void localForage2.setItem("dxos.devtools.spaceKey", spaceKey.toHex());
    }
  };
  useAsyncEffect3(async () => {
    const spaceKeyHex = await localForage2.getItem("dxos.devtools.spaceKey");
    if (spaceKeyHex) {
      handleSelect(PublicKey.fromHex(spaceKeyHex));
    }
  }, []);
  const getLabel = (key) => {
    if (identity?.spaceKey && key.equals(identity.spaceKey)) {
      return "HALO";
    }
    const space2 = spaces.find((space3) => space3.key.equals(key));
    return space2?.properties.name ?? humanize(key);
  };
  const spaceKeys = spaces.map((space2) => space2.key);
  identity?.spaceKey && spaceKeys.push(identity.spaceKey);
  return /* @__PURE__ */ React7.createElement(PublicKeySelector, {
    placeholder: "Select space",
    getLabel,
    keys: spaceKeys,
    value: stateHaloSpaceKey || space?.key,
    onChange: handleSelect
  });
};

// src/panels/client/index.ts
import { lazy } from "react";
var ConfigPanel = lazy(() => import("./ConfigPanel-GNGC46TP.mjs"));
var DiagnosticsPanel = lazy(() => import("./DiagnosticsPanel-HPEMLB7X.mjs"));
var LoggingPanel = lazy(() => import("./LoggingPanel-4FRWBSIO.mjs"));
var StoragePanel = lazy(() => import("./StoragePanel-DXPNVOM4.mjs"));
var TracingPanel = lazy(() => import("./TracingPanel-7OP4XP4H.mjs"));

// src/panels/echo/index.ts
import { lazy as lazy2 } from "react";
var AutomergePanel = lazy2(() => import("./AutomergePanel-P33RZHSN.mjs"));
var FeedsPanel = lazy2(() => import("./FeedsPanel-SYXP6MXO.mjs"));
var MembersPanel = lazy2(() => import("./MembersPanel-MCIZEFNM.mjs"));
var MetadataPanel = lazy2(() => import("./MetadataPanel-FG3F5ZCX.mjs"));
var ObjectsPanel = lazy2(() => import("./ObjectsPanel-MVKDRPJ6.mjs"));
var QueuesPanel = lazy2(() => import("./QueuesPanel-RDA57MXF.mjs"));
var SpaceInfoPanel = lazy2(() => import("./SpaceInfoPanel-ZQWN5HK6.mjs"));
var SpaceListPanel = lazy2(() => import("./SpaceListPanel-6XGSN2IX.mjs"));
var SchemaPanel = lazy2(() => import("./SchemaPanel-OZE6OX4Z.mjs"));

// src/panels/halo/index.ts
import { lazy as lazy3 } from "react";
var CredentialsPanel = lazy3(() => import("./CredentialsPanel-IOU6GXPS.mjs"));
var DeviceListPanel = lazy3(() => import("./DeviceListPanel-5X2FUIAZ.mjs"));
var IdentityPanel = lazy3(() => import("./IdentityPanel-XE2KAGSW.mjs"));
var KeyringPanel = lazy3(() => import("./KeyringPanel-77YQDU4M.mjs"));

// src/panels/mesh/index.ts
import { lazy as lazy4 } from "react";
var SignalPanel = lazy4(() => import("./SignalPanel-7A2Y3EXN.mjs"));
var SwarmPanel = lazy4(() => import("./SwarmPanel-URFQQQZK.mjs"));
var NetworkPanel = lazy4(() => import("./NetworkPanel-V75FT7Q3.mjs"));

// src/panels/edge/index.ts
import { lazy as lazy5 } from "react";
var EdgeDashboardPanel = lazy5(() => import("./EdgeDashboardPanel-NFKBGLUU.mjs"));
var InvocationTraceContainer = lazy5(() => import("./InvocationTraceContainer-3NBUQX4X.mjs"));
var InvocationTracePanel = lazy5(() => import("./InvocationTracePanel-ZNDOOTGI.mjs"));
var TestingPanel = lazy5(() => import("./TestingPanel-JNRVFV4U.mjs"));
var WorkflowPanel = lazy5(() => import("./WorkflowPanel-4YUDOUID.mjs"));

// src/hooks/useRoutes.tsx
var namespace = "devtools";
var useRoutes = () => {
  const navigate = useNavigate();
  const handleSelectSpace = useCallback(() => navigate("/echo/space"), [
    navigate
  ]);
  const handleSelectFeed = useCallback(() => navigate("/echo/feeds"), [
    navigate
  ]);
  return useRouterRoutes([
    {
      path: "/",
      element: /* @__PURE__ */ React8.createElement(RootContainer, null),
      children: [
        {
          path: "/client",
          children: [
            {
              path: "/client/config",
              element: /* @__PURE__ */ React8.createElement(ConfigPanel, null)
            },
            {
              path: "/client/storage",
              element: /* @__PURE__ */ React8.createElement(StoragePanel, null)
            },
            {
              path: "/client/logs",
              element: /* @__PURE__ */ React8.createElement(LoggingPanel, null)
            },
            {
              path: "/client/diagnostics",
              element: /* @__PURE__ */ React8.createElement(DiagnosticsPanel, null)
            },
            {
              path: "/client/tracing",
              element: /* @__PURE__ */ React8.createElement(TracingPanel, null)
            }
          ]
        },
        {
          path: "/halo",
          children: [
            {
              path: "/halo/identity",
              element: /* @__PURE__ */ React8.createElement(IdentityPanel, null)
            },
            {
              path: "/halo/devices",
              element: /* @__PURE__ */ React8.createElement(DeviceListPanel, null)
            },
            {
              path: "/halo/keyring",
              element: /* @__PURE__ */ React8.createElement(KeyringPanel, null)
            },
            {
              path: "/halo/credentials",
              element: /* @__PURE__ */ React8.createElement(CredentialsPanel, null)
            }
          ]
        },
        {
          path: "/echo",
          children: [
            {
              path: "/echo/spaces",
              element: /* @__PURE__ */ React8.createElement(SpaceListPanel, {
                onSelect: handleSelectSpace
              })
            },
            {
              path: "/echo/space",
              element: /* @__PURE__ */ React8.createElement(SpaceInfoPanel, {
                onSelectFeed: handleSelectFeed,
                onSelectPipeline: handleSelectFeed
              })
            },
            {
              path: "/echo/feeds",
              element: /* @__PURE__ */ React8.createElement(FeedsPanel, null)
            },
            {
              path: "/echo/objects",
              element: /* @__PURE__ */ React8.createElement(ObjectsPanel, null)
            },
            {
              path: "/echo/automerge",
              element: /* @__PURE__ */ React8.createElement(AutomergePanel, null)
            },
            {
              path: "/echo/members",
              element: /* @__PURE__ */ React8.createElement(MembersPanel, null)
            },
            {
              path: "/echo/metadata",
              element: /* @__PURE__ */ React8.createElement(MetadataPanel, null)
            }
          ]
        },
        {
          path: "/mesh",
          children: [
            {
              path: "/mesh/signal",
              element: /* @__PURE__ */ React8.createElement(SignalPanel, null)
            },
            {
              path: "/mesh/swarm",
              element: /* @__PURE__ */ React8.createElement(SwarmPanel, null)
            },
            {
              path: "/mesh/network",
              element: /* @__PURE__ */ React8.createElement(NetworkPanel, null)
            }
          ]
        },
        {
          path: "/edge",
          children: [
            {
              path: "/edge/workflows",
              element: /* @__PURE__ */ React8.createElement(WorkflowPanel, null)
            },
            {
              path: "/edge/dashboard",
              element: /* @__PURE__ */ React8.createElement(EdgeDashboardPanel, null)
            },
            {
              path: "/edge/traces",
              element: /* @__PURE__ */ React8.createElement(InvocationTracePanel, null)
            },
            {
              path: "/edge/testing",
              element: /* @__PURE__ */ React8.createElement(TestingPanel, null)
            }
          ]
        }
      ]
    }
  ]);
};

// src/hooks/useSections.tsx
var useSections = () => {
  return [
    {
      id: "client",
      title: "Client",
      icon: "ph--users--regular",
      items: [
        {
          id: "/client/config",
          title: "Config",
          icon: "ph--gear--regular"
        },
        {
          id: "/client/storage",
          title: "Storage",
          icon: "ph--hard-drives--regular"
        },
        {
          id: "/client/logs",
          title: "Logs",
          icon: "ph--receipt--regular"
        },
        {
          id: "/client/diagnostics",
          title: "Diagnostics",
          icon: "ph--chart-line--regular"
        },
        {
          id: "/client/tracing",
          title: "Tracing",
          icon: "ph--fire-simple--regular"
        }
      ]
    },
    {
      id: "halo",
      title: "HALO",
      icon: "ph--identification-badge--regular",
      items: [
        {
          id: "/halo/identity",
          title: "Identity",
          icon: "ph--identification-badge--regular"
        },
        {
          id: "/halo/devices",
          title: "Devices",
          icon: "ph--devices--regular"
        },
        {
          id: "/halo/keyring",
          title: "Keyring",
          icon: "ph--key--regular"
        },
        {
          id: "/halo/credentials",
          title: "Credentials",
          icon: "ph--credit-card--regular"
        }
      ]
    },
    {
      id: "echo",
      title: "ECHO",
      icon: "ph--database--regular",
      items: [
        {
          id: "/echo/spaces",
          title: "Spaces",
          icon: "ph--graph--regular"
        },
        {
          id: "/echo/space",
          title: "Space",
          icon: "ph--planet--regular"
        },
        {
          id: "/echo/feeds",
          title: "Feeds",
          icon: "ph--queue--regular"
        },
        {
          id: "/echo/objects",
          title: "Objects",
          icon: "ph--database--regular"
        },
        {
          id: "/echo/automerge",
          title: "Automerge",
          icon: "ph--gear-six--regular"
        },
        {
          id: "/echo/members",
          title: "Members",
          icon: "ph--users--regular"
        },
        {
          id: "/echo/metadata",
          title: "Metadata",
          icon: "ph--hard-drive--regular"
        }
      ]
    },
    {
      id: "mesh",
      title: "MESH",
      icon: "ph--graph--regular",
      items: [
        {
          id: "/mesh/signal",
          title: "Signal",
          icon: "ph--wifi-high--regular"
        },
        {
          id: "/mesh/swarm",
          title: "Swarm",
          icon: "ph--users-three--regular"
        },
        {
          id: "/mesh/network",
          title: "Network",
          icon: "ph--polygon--regular"
        }
      ]
    },
    {
      id: "agent",
      title: "AGENT",
      icon: "ph--robot--regular",
      items: [
        {
          id: "/agent/dashboard",
          title: "Dashboard",
          icon: "ph--computer-tower--regular"
        },
        {
          id: "/agent/search",
          title: "Search",
          icon: "ph--magnifying-glass--regular"
        }
      ]
    },
    {
      id: "edge",
      title: "EDGE",
      icon: "ph--cloud--regular",
      items: [
        {
          id: "/edge/workflows",
          title: "Workflows",
          icon: "ph--function--regular"
        },
        {
          id: "/edge/dashboard",
          title: "Dashboard",
          icon: "ph--computer-tower--regular"
        },
        {
          id: "/edge/traces",
          title: "Traces",
          icon: "ph--line-segments--regular"
        },
        {
          id: "/edge/testing",
          title: "Testing",
          icon: "ph--flask--regular"
        }
      ]
    }
  ];
};

// src/hooks/useSpacesInfo.tsx
import { useDevtools as useDevtools3, useStream as useStream3 } from "@dxos/react-client/devtools";
var useSpacesInfo = () => {
  const devtoolsHost = useDevtools3();
  const spaces = useStream3(() => devtoolsHost.subscribeToSpaces({}), {}).spaces ?? [];
  return spaces;
};
var useSpaceInfo = (spaceKey) => {
  const spaces = useSpacesInfo();
  const space = spaces.find((space2) => space2.key.equals(spaceKey));
  return space;
};

// src/hooks/useSignal.tsx
import { useMemo as useMemo3 } from "react";
import { useDevtools as useDevtools4 } from "@dxos/react-client/devtools";
var useSignal = () => {
  const devtoolsHost = useDevtools4();
  const signalResponses = useMemo3(() => {
    const signalOutput = devtoolsHost.subscribeToSignal();
    const signalResponses2 = [];
    signalOutput.subscribe((response) => {
      signalResponses2.push(response);
      return [
        ...signalResponses2
      ];
    });
  }, []);
  return signalResponses;
};

// src/hooks/useStats.ts
import { useEffect as useEffect3, useState as useState5 } from "react";
import { SpaceState } from "@dxos/client/echo";
import { log } from "@dxos/log";
import { useClient as useClient2 } from "@dxos/react-client";
import { useAsyncEffect as useAsyncEffect4 } from "@dxos/react-hooks";
import { TRACE_PROCESSOR } from "@dxos/tracing";
import { DiagnosticsChannel } from "@dxos/tracing";
import { get } from "@dxos/util";
var __dxlog_file2 = "/__w/dxos/dxos/packages/devtools/devtools/src/hooks/useStats.ts";
var usePerformanceObserver = (entryTypes) => {
  const [entries, setEntries] = useState5();
  useEffect3(() => {
    const po = new PerformanceObserver((list) => {
      setEntries(list.getEntries());
    });
    po.observe({
      entryTypes
    });
    return () => po.disconnect();
  }, []);
  return entries;
};
var useStats = () => {
  const client = useClient2();
  const [stats, setStats] = useState5({});
  const [update, forceUpdate] = useState5({});
  const performanceEntries = usePerformanceObserver([
    // https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Performance_data#performance_entries
    "first-input",
    "longtask",
    "largest-contentful-paint",
    "paint"
  ]);
  useAsyncEffect4(async () => {
    const begin = performance.now();
    const objects = TRACE_PROCESSOR.findResourcesByClassName("RepoProxy").flatMap((r) => Object.values(r.instance.deref()?.handles ?? {})).map((handle) => handle.doc()).filter(Boolean);
    const database = {
      spaces: client.spaces.get().length,
      objects: objects.length,
      documents: 0,
      documentsToReconcile: 0
    };
    if ("measureUserAgentSpecificMemory" in window.performance) {
    }
    const memory = window.performance.memory;
    if (memory && typeof memory === "object" && "usedJSHeapSize" in memory) {
      memory.used = memory.usedJSHeapSize / memory.jsHeapSizeLimit;
    }
    log("collected stats", {
      elapsed: performance.now() - begin
    }, {
      F: __dxlog_file2,
      L: 126,
      S: void 0,
      C: (f, a) => f(...a)
    });
    setStats((stats2) => Object.assign({}, stats2, {
      performanceEntries,
      diagnostics: TRACE_PROCESSOR.getDiagnostics(),
      memory,
      database
    }));
  }, [
    update
  ]);
  useAsyncEffect4(async () => {
    const begin = performance.now();
    const diagnostics = await client.diagnostics();
    const resources = get(diagnostics, "services.diagnostics.trace.resources");
    const queries = Object.values(resources).filter((res) => res.className === "QueryState").map((res) => {
      return res.info;
    });
    const syncStates = await Promise.all(client.spaces.get().filter((space) => space.state.get() === SpaceState.SPACE_READY).map((space) => space.internal.db.coreDatabase.getSyncState()));
    const documentsToReconcile = syncStates.flatMap((s) => s.peers?.map((p) => p.differentDocuments + p.missingOnLocal + p.missingOnRemote) ?? []).reduce((acc, x) => acc + x, 0);
    log("collected stats", {
      elapsed: performance.now() - begin
    }, {
      F: __dxlog_file2,
      L: 163,
      S: void 0,
      C: (f, a) => f(...a)
    });
    setStats((stats2) => Object.assign({}, stats2, {
      queries,
      database: Object.assign({}, stats2.database, {
        documentsToReconcile
      })
    }));
  }, [
    update
  ]);
  useEffect3(() => {
    const stream = client.services.services.NetworkService.queryStatus();
    stream.subscribe((network) => {
      setStats((stats2) => Object.assign({}, stats2, {
        network
      }));
    });
    return () => {
      void stream.close();
    };
  }, []);
  useEffect3(() => {
    const stream = client.services.services.EdgeAgentService.queryEdgeStatus();
    stream.subscribe((edge) => {
      setStats((stats2) => Object.assign({}, stats2, {
        edge
      }));
    });
    return () => {
      void stream.close();
    };
  }, []);
  const echoStatsDiagnostic = useDiagnostic({
    id: "echo-stats",
    instanceTag: "shared-worker"
  }, 1e3);
  if (stats.database && echoStatsDiagnostic) {
    stats.database.documents = echoStatsDiagnostic.loadedDocsCount;
    stats.database.dataStats = echoStatsDiagnostic.dataStats;
  }
  return [
    stats,
    () => forceUpdate({})
  ];
};
var removeEmpty = (obj) => {
  const maybeTruncateKey = (str) => str.length > 32 ? str.slice(0, 8) : str;
  return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== void 0 && v !== null && v !== false && !(Array.isArray(v) && v.length === 0)).map(([k, v]) => [
    k,
    v === Object(v) ? removeEmpty(v) : typeof v === "string" ? maybeTruncateKey(v) : v
  ]));
};
var useDiagnostic = (request, refreshInterval) => {
  const [data, setData] = useState5();
  useEffect3(() => {
    const channel = new DiagnosticsChannel();
    const fetch = async () => {
      try {
        const { data: data2 } = await channel.fetch(request);
        setData(data2);
      } catch (error) {
      }
    };
    void fetch();
    const interval = setInterval(fetch, refreshInterval);
    return () => {
      clearInterval(interval);
    };
  }, []);
  return data;
};

// src/components/performance/panels/Database/DatabasePanel.tsx
import React10 from "react";

// src/components/performance/panels/Table.tsx
import React9 from "react";
(function(Unit4) {
  Unit4.KB = (n) => ((n ?? 0) / 1e3).toFixed(2);
})(Unit || (Unit = {}));
var Table = ({ rows }) => {
  return /* @__PURE__ */ React9.createElement("div", {
    className: "is-full text-xs font-mono"
  }, rows.map(([prefix, label, value, unit], i) => /* @__PURE__ */ React9.createElement("div", {
    key: i,
    className: "grid grid-cols-[1rem_2fr_1fr_3rem]"
  }, /* @__PURE__ */ React9.createElement("div", {
    className: "p-1 text-subdued"
  }, prefix), /* @__PURE__ */ React9.createElement("div", {
    className: "p-1 truncate"
  }, label), /* @__PURE__ */ React9.createElement("div", {
    className: "p-1 text-right"
  }, value), /* @__PURE__ */ React9.createElement("div", {
    className: "p-1 text-subdued"
  }, unit))));
};
var Unit;

// src/components/performance/panels/Database/DatabasePanel.tsx
var DatabasePanel = ({ database, ...props }) => {
  const interval = database?.dataStats?.meta?.rateAverageOverSeconds ? ` (${database?.dataStats?.meta?.rateAverageOverSeconds}s)` : "";
  const storageStats = database?.dataStats?.storage;
  const rows = [
    [
      "#",
      "objects",
      database?.objects ?? 0
    ],
    [
      "#",
      "documents",
      database?.documents ?? 0
    ],
    [
      "#",
      "documents (syncing)",
      database?.documentsToReconcile ?? 0
    ],
    [
      "\u03BC",
      `read rate ${interval}`,
      storageStats?.reads?.countPerSecond ?? 0,
      "op/s"
    ],
    [
      "\u03BC",
      "read duration",
      storageStats?.reads?.opDuration ?? 0,
      "ms"
    ],
    [
      "\u03BC",
      "read chunk size",
      Unit.KB(storageStats?.reads?.payloadSize),
      "KB"
    ],
    [
      "\u03BC",
      `write rate ${interval}`,
      storageStats?.writes?.countPerSecond ?? 0,
      "op/s"
    ],
    [
      "\u03BC",
      "write duration",
      storageStats?.writes?.opDuration ?? 0,
      "ms"
    ],
    [
      "\u03BC",
      "write chunk size",
      Unit.KB(storageStats?.writes?.payloadSize),
      "KB"
    ]
  ];
  return /* @__PURE__ */ React10.createElement(Panel, {
    ...props,
    icon: "ph--database--regular",
    title: "Database",
    info: /* @__PURE__ */ React10.createElement("div", {
      className: "flex items-center gap-2"
    }, database?.spaces ?? 0, " Space(s)"),
    maxHeight: 0
  }, /* @__PURE__ */ React10.createElement(Table, {
    rows
  }));
};

// src/components/performance/panels/Database/ReplicatorPanel.tsx
import React11 from "react";
var ReplicatorPanel = ({ database, ...props }) => {
  const interval = database?.dataStats?.meta?.rateAverageOverSeconds ? ` (${database?.dataStats?.meta?.rateAverageOverSeconds}s)` : "";
  const replicatorStats = database?.dataStats?.replicator;
  const rows = [
    [
      "\u03BC",
      `recv ${interval}`,
      replicatorStats?.receivedMessages?.countPerSecond ?? 0,
      "op/s"
    ],
    [
      "\u03BC",
      "recv size",
      Unit.KB(replicatorStats?.receivedMessages?.payloadSize),
      "KB"
    ],
    [
      "\u03BC",
      `send ${interval}`,
      replicatorStats?.sentMessages?.countPerSecond ?? 0,
      "op/s"
    ],
    [
      "\u03BC",
      "sent size",
      Unit.KB(replicatorStats?.sentMessages?.payloadSize),
      "KB"
    ],
    [
      "\u03BC",
      "send failures",
      replicatorStats?.sentMessages?.failedPerSecond ?? 0,
      "err/s"
    ],
    [
      "\u03BC",
      "send duration",
      replicatorStats?.sentMessages?.opDuration ?? 0,
      "ms"
    ]
  ];
  return /* @__PURE__ */ React11.createElement(Panel, {
    ...props,
    icon: "ph--database--regular",
    title: "DB replicator",
    info: /* @__PURE__ */ React11.createElement("div", {
      className: "flex items-center gap-2"
    }, replicatorStats?.connections ?? 0, " Connection(s)"),
    maxHeight: 0
  }, /* @__PURE__ */ React11.createElement(Table, {
    rows
  }));
};

// src/components/performance/panels/Database/ReplicatorMessagesPanel.tsx
import React12 from "react";
var ReplicatorMessagesPanel = ({ database, ...props }) => {
  let recvTotal = 0;
  let sentTotal = 0;
  const replicatorStats = database?.dataStats?.replicator;
  const rows = Object.entries(replicatorStats?.countByMessage ?? {}).flatMap(([type, counts]) => {
    sentTotal += counts.sent;
    recvTotal += counts.received;
    const avgSize = replicatorStats?.avgSizeByMessage[type];
    const size = avgSize !== void 0 ? ` (${Unit.KB(avgSize)} KB)` : "";
    return [
      [
        "\u2191",
        type + size,
        counts.sent
      ],
      [
        "\u2193",
        type + size,
        counts.received
      ]
    ];
  }).sort(([d1, t1], [d2, t2]) => {
    const cmp = d1.localeCompare(d2);
    return cmp === 0 ? t1.localeCompare(t2) : cmp;
  });
  return /* @__PURE__ */ React12.createElement(Panel, {
    ...props,
    icon: "ph--database--regular",
    title: "DB messages",
    info: /* @__PURE__ */ React12.createElement("div", {
      className: "flex gap-1 items-center"
    }, /* @__PURE__ */ React12.createElement("span", null, /* @__PURE__ */ React12.createElement("span", null, recvTotal), /* @__PURE__ */ React12.createElement("span", {
      className: "text-green-500",
      title: "Received"
    }, "\u2193")), /* @__PURE__ */ React12.createElement("span", null, /* @__PURE__ */ React12.createElement("span", null, sentTotal), /* @__PURE__ */ React12.createElement("span", {
      className: "text-orange-500",
      title: "Sent"
    }, "\u2191"))),
    maxHeight: 0
  }, /* @__PURE__ */ React12.createElement(Table, {
    rows
  }));
};

// src/components/performance/panels/EdgePanel.tsx
import React13, { useEffect as useEffect4, useMemo as useMemo4, useState as useState6 } from "react";
import { asyncTimeout, scheduleTask } from "@dxos/async";
import { createEdgeIdentity } from "@dxos/client/edge";
import { Context } from "@dxos/context";
import { EdgeStatus as WsStatus } from "@dxos/protocols/proto/dxos/client/services";
import { useClient as useClient3 } from "@dxos/react-client";
import { IconButton } from "@dxos/react-ui";
var __dxlog_file3 = "/__w/dxos/dxos/packages/devtools/devtools/src/components/performance/panels/EdgePanel.tsx";
var IDENTITY_WAIT_TIMEOUT = 3e4;
var EdgePanel = ({ edge, ...props }) => {
  const client = useClient3();
  const [edgeStatus, setEdgeStatus] = useState6();
  const handleRefresh = async () => {
    const status = await client.edge.getStatus();
    setEdgeStatus(status);
  };
  const handleCopyRaw = async () => {
    await navigator.clipboard.writeText(JSON.stringify(edgeStatus, null, 2));
  };
  useEffect4(() => {
    const ctx = new Context(void 0, {
      F: __dxlog_file3,
      L: 35
    });
    scheduleTask(ctx, async () => {
      await asyncTimeout(client.spaces.waitUntilReady(), IDENTITY_WAIT_TIMEOUT);
      client.edge.setIdentity(createEdgeIdentity(client));
      await handleRefresh();
    });
    return () => {
      void ctx.dispose();
    };
  }, []);
  const rows = useMemo4(() => getHealthReportTable(edgeStatus, edge?.status), [
    edgeStatus,
    edge?.status
  ]);
  return /* @__PURE__ */ React13.createElement(Panel, {
    ...props,
    icon: "ph--cloud--regular",
    title: "EDGE",
    info: /* @__PURE__ */ React13.createElement("div", {
      className: "flex items-center gap-2"
    }, " ", edgeStatus?.problems.length === 0 ? "\u2705" : "\u274C"),
    maxHeight: 0
  }, /* @__PURE__ */ React13.createElement("div", {
    className: "flex flex-col is-full gap-2 text-xs"
  }, /* @__PURE__ */ React13.createElement("div", {
    className: "flex items-center gap-2"
  }, /* @__PURE__ */ React13.createElement(IconButton, {
    icon: "ph--arrow-clockwise--regular",
    label: "refresh",
    onClick: handleRefresh
  }), /* @__PURE__ */ React13.createElement(IconButton, {
    icon: "ph--copy--regular",
    label: "copy raw",
    onClick: handleCopyRaw
  })), /* @__PURE__ */ React13.createElement(Table, {
    rows
  }), (edgeStatus?.problems?.length ?? 0) > 0 && /* @__PURE__ */ React13.createElement("div", {
    className: "flex flex-col pli-2"
  }, /* @__PURE__ */ React13.createElement("span", null, "Issues"), /* @__PURE__ */ React13.createElement("ol", {
    className: "pli-2 list-decimal"
  }, edgeStatus?.problems?.map((problem, idx) => /* @__PURE__ */ React13.createElement("li", {
    key: idx,
    className: "text-description"
  }, problem))))));
};
var getHealthReportTable = (status, wsStatus) => {
  const isConnected = wsStatus?.state === WsStatus.ConnectionState.CONNECTED;
  const rows = [
    [
      isConnected ? "\u2705" : "\u274C",
      "web socket",
      isConnected ? "Connected" : "Disconnected"
    ],
    ...!isConnected ? [] : [
      [
        "",
        "uptime",
        wsStatus?.uptime?.toFixed(0) ?? "N/A",
        "s"
      ],
      [
        "",
        "RTT",
        wsStatus?.rtt?.toFixed(0) ?? "N/A",
        "ms"
      ],
      [
        "",
        "up",
        Unit.KB(wsStatus?.rateBytesUp ?? 0),
        "KB/s"
      ],
      [
        "",
        "down",
        Unit.KB(wsStatus?.rateBytesDown ?? 0),
        "KB/s"
      ]
    ]
  ];
  if (!status) {
    return rows;
  }
  rows.push([
    (status.router.connectedDevices?.length ?? 0) > 0 && !status.router.fetchError ? "\u2705" : "\u274C",
    "router",
    `Devices: ${status.router.connectedDevices?.length ?? 0}`
  ], [
    status.agent.agentStatus === "active" ? "\u2705" : "\u274C",
    "agent",
    status.agent.agentStatus ?? ""
  ], [
    !status.spaces.fetchError ? "\u2705" : "\u274C",
    "spaces",
    `Spaces: ${Object.keys(status.spaces.data ?? {}).length ?? 0}`
  ], ...Object.entries(status.spaces.data ?? {}).map(([spaceId, space]) => [
    space.diagnostics?.redFlags?.length > 0 || space.fetchError ? "\u274C" : "\u2705",
    spaceId
  ]));
  return rows;
};

// src/components/performance/panels/LoggingPanel.tsx
import React14, { useCallback as useCallback2, useEffect as useEffect5, useState as useState7 } from "react";
import { LogLevel, log as log2, shortLevelName, shouldLog } from "@dxos/log";
import { IconButton as IconButton2, Input, ScrollArea as ScrollArea2, Toolbar } from "@dxos/react-ui";
import { mx as mx3 } from "@dxos/ui-theme";
var __dxlog_file4 = "/__w/dxos/dxos/packages/devtools/devtools/src/components/performance/panels/LoggingPanel.tsx";
var LoggingPanel2 = ({ maxLines = 100, ...props }) => {
  const [text, setText] = useState7("intent-dispatcher:DEBUG");
  const [active, setActive] = useState7(false);
  const [entries, setEntries] = useState7([]);
  useEffect5(() => {
    if (!active) {
      return;
    }
    log2.config({
      filter: text
    }, void 0, {
      F: __dxlog_file4,
      L: 23,
      S: void 0,
      C: (f, a) => f(...a)
    });
    const dispose = log2.addProcessor((config, entry) => {
      if (shouldLog(entry, config.filters)) {
        setEntries((entries2) => [
          ...entries2,
          entry
        ].slice(0, maxLines));
      }
    }, void 0, {
      F: __dxlog_file4,
      L: 24,
      S: void 0,
      C: (f, a) => f(...a)
    });
    return () => {
      dispose();
    };
  }, [
    active,
    text,
    maxLines
  ]);
  const handleClear = useCallback2(() => {
    setEntries([]);
  }, []);
  return /* @__PURE__ */ React14.createElement(Panel, {
    ...props,
    icon: "ph--list--regular",
    title: "Logging",
    maxHeight: 0
  }, /* @__PURE__ */ React14.createElement(Toolbar.Root, {
    classNames: "gap-2"
  }, /* @__PURE__ */ React14.createElement(Input.Root, null, /* @__PURE__ */ React14.createElement(Input.TextInput, {
    placeholder: "Filter",
    value: text,
    onChange: (ev) => setText(ev.target.value)
  })), /* @__PURE__ */ React14.createElement(Input.Root, null, /* @__PURE__ */ React14.createElement(Input.Switch, {
    checked: active,
    onCheckedChange: setActive
  })), /* @__PURE__ */ React14.createElement(Toolbar.IconButton, {
    icon: "ph--x--regular",
    iconOnly: true,
    label: "Clear",
    onClick: handleClear
  })), /* @__PURE__ */ React14.createElement(ScrollArea2.Root, {
    orientation: "vertical",
    classNames: "max-bs-[240px]"
  }, /* @__PURE__ */ React14.createElement(ScrollArea2.Viewport, {
    classNames: "text-xs"
  }, entries.map((entry, index) => /* @__PURE__ */ React14.createElement("div", {
    key: index,
    className: "group pli-1 grid grid-cols-[1rem_1fr_1fr_min-content] items-center"
  }, /* @__PURE__ */ React14.createElement("div", {
    className: mx3(entry.level > LogLevel.WARN ? "text-errorText" : entry.level > LogLevel.INFO ? "text-warningText" : entry.level > LogLevel.VERBOSE ? "text-infoText" : "text-successText")
  }, shortLevelName[entry.level]), /* @__PURE__ */ React14.createElement("div", {
    className: "truncate text-subdued"
  }, entry.meta?.F?.split("/").pop()?.split(".")[0]), /* @__PURE__ */ React14.createElement("div", {
    className: "truncate cursor-pointer",
    title: entry.message
  }, entry.message), /* @__PURE__ */ React14.createElement(IconButton2, {
    icon: "ph--clipboard--regular",
    iconOnly: true,
    size: 4,
    label: "Copy",
    variant: "ghost",
    classNames: "cursor-pointer opacity-50 group-hover:opacity-100",
    onClick: () => navigator.clipboard.writeText(JSON.stringify(entry, null, 2))
  }))))));
};

// src/components/performance/panels/MemoryPanel.tsx
import React15 from "react";
import { mx as mx4 } from "@dxos/ui-theme";
import { Unit as Unit2 } from "@dxos/util";
var MEM_WARNING = 40 / 100;
var MemoryPanel = ({ memory, ...props }) => {
  return /* @__PURE__ */ React15.createElement(Panel, {
    ...props,
    icon: "ph--cpu--regular",
    title: "Memory",
    info: /* @__PURE__ */ React15.createElement("div", {
      className: "flex items-center gap-2"
    }, /* @__PURE__ */ React15.createElement("span", {
      title: "Used (heap size)"
    }, String(Unit2.Megabyte(memory?.usedJSHeapSize ?? 0))), /* @__PURE__ */ React15.createElement("span", {
      title: "Allocated (heap size)"
    }, String(Unit2.Megabyte(memory?.totalJSHeapSize ?? 0))), memory?.used !== void 0 && /* @__PURE__ */ React15.createElement("span", {
      title: "Used (available)",
      className: mx4(memory?.used > MEM_WARNING && "text-red-500")
    }, String(Unit2.Percent(memory?.used ?? 0))))
  });
};

// src/components/performance/panels/NetworkPanel.tsx
import React16 from "react";
import { ConnectionState } from "@dxos/network-manager";
var NetworkPanel2 = ({ network, ...props }) => {
  const swarmCount = network?.connectionInfo?.length ?? 0;
  const connectionCount = network?.connectionInfo?.reduce((acc, info) => acc + (info.connections?.filter((conn) => conn.state === ConnectionState.CONNECTED).length ?? 0), 0) ?? 0;
  return /* @__PURE__ */ React16.createElement(Panel, {
    ...props,
    icon: "ph--wifi-high--regular",
    title: "Network",
    info: /* @__PURE__ */ React16.createElement("div", {
      className: "flex items-center gap-2"
    }, /* @__PURE__ */ React16.createElement("span", {
      title: "Active Connection(s)"
    }, connectionCount, " Connection(s)"), /* @__PURE__ */ React16.createElement("span", {
      title: "Active Swarm(s)"
    }, swarmCount, " Swarm(s)"))
  });
};

// src/components/performance/panels/PerformancePanel.tsx
import React18 from "react";

// src/components/performance/util.tsx
import React17 from "react";
import { mx as mx5 } from "@dxos/ui-theme";
import { Unit as Unit3 } from "@dxos/util";
var SLOW_TIME = 250;
var Duration = ({ duration }) => /* @__PURE__ */ React17.createElement("span", {
  className: mx5(duration > SLOW_TIME && "text-red-500")
}, String(Unit3.Duration(duration)));

// src/components/performance/panels/PerformancePanel.tsx
var PerformancePanel = ({ entries, ...props }) => {
  if (!entries?.length) {
    return null;
  }
  return /* @__PURE__ */ React18.createElement(Panel, {
    ...props,
    icon: "ph--hourglass-simple-low--regular",
    title: "Performance",
    info: /* @__PURE__ */ React18.createElement("span", null, entries?.length.toLocaleString())
  }, /* @__PURE__ */ React18.createElement("table", {
    className: "table-fixed is-full text-xs font-mono"
  }, /* @__PURE__ */ React18.createElement("tbody", null, entries?.map((entry, i) => /* @__PURE__ */ React18.createElement("tr", {
    key: i
  }, /* @__PURE__ */ React18.createElement("td", {
    className: "p-1 text-right"
  }, [
    entry.entryType,
    entry.name
  ].filter(Boolean).join("/")), /* @__PURE__ */ React18.createElement("td", {
    className: "p-1 is-[80px]"
  }), /* @__PURE__ */ React18.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, /* @__PURE__ */ React18.createElement(Duration, {
    duration: entry.duration
  })))))));
};

// src/components/performance/panels/QueriesPanel.tsx
import React19 from "react";
import { Icon as Icon3 } from "@dxos/react-ui";
var QueriesPanel = ({ queries, ...props }) => {
  if (!queries?.length) {
    return null;
  }
  return /* @__PURE__ */ React19.createElement(Panel, {
    ...props,
    icon: "ph--tree-view--regular",
    title: "Queries",
    info: /* @__PURE__ */ React19.createElement("span", null, queries.length.toLocaleString())
  }, /* @__PURE__ */ React19.createElement("table", {
    className: "table-fixed is-full text-xs font-mono"
  }, /* @__PURE__ */ React19.createElement("tbody", null, queries.map((query, i) => /* @__PURE__ */ React19.createElement("tr", {
    key: i
  }, /* @__PURE__ */ React19.createElement("td", {
    className: "p-1 is-[24px]"
  }, /* @__PURE__ */ React19.createElement(Icon3, {
    icon: query.active ? "ph--check--regular" : "ph--x--regular",
    classNames: [
      !query.active && "opacity-30"
    ]
  })), /* @__PURE__ */ React19.createElement("td", {
    className: "p-1 truncate",
    title: JSON.stringify(removeEmpty(query.filter), void 0, 2)
  }), /* @__PURE__ */ React19.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, query.metrics.objectsReturned.toLocaleString()), /* @__PURE__ */ React19.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, /* @__PURE__ */ React19.createElement(Duration, {
    duration: query.metrics.executionTime
  })))))));
};

// src/components/performance/panels/RawQueriesPanel.tsx
import React20 from "react";
var RawQueriesPanel = ({ queries, ...props }) => {
  const keys = Array.from(queries.keys()).sort();
  return /* @__PURE__ */ React20.createElement(Panel, {
    ...props,
    icon: "ph--tree-view--regular",
    title: "Query types",
    info: /* @__PURE__ */ React20.createElement("span", null, keys.length.toLocaleString())
  }, /* @__PURE__ */ React20.createElement("table", {
    className: "table-fixed is-full text-xs font-mono"
  }, /* @__PURE__ */ React20.createElement("tbody", null, keys.map((key, i) => /* @__PURE__ */ React20.createElement("tr", {
    key: i
  }, /* @__PURE__ */ React20.createElement("td", {
    className: "p-1 overflow-hidden"
  }, /* @__PURE__ */ React20.createElement("pre", {
    className: "font-2xs font-thin"
  }, key)), /* @__PURE__ */ React20.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, queries.get(key).toLocaleString()))))));
};

// src/components/performance/panels/SpansPanel.tsx
import React21 from "react";
var SpansPanel = ({ spans, ...props }) => {
  if (!spans?.length) {
    return null;
  }
  return /* @__PURE__ */ React21.createElement(Panel, {
    ...props,
    icon: "ph--align-center-horizontal-simple--regular",
    title: "Spans",
    info: /* @__PURE__ */ React21.createElement("span", null, spans?.length.toLocaleString())
  }, /* @__PURE__ */ React21.createElement("table", {
    className: "table-fixed is-full text-xs font-mono"
  }, /* @__PURE__ */ React21.createElement("tbody", null, spans?.map(({ id, resourceId, methodName, startTs, endTs }) => endTs && startTs && /* @__PURE__ */ React21.createElement("tr", {
    key: id
  }, /* @__PURE__ */ React21.createElement("td", {
    className: "p-1"
  }, methodName), /* @__PURE__ */ React21.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, resourceId), /* @__PURE__ */ React21.createElement("td", {
    className: "p-1 is-[80px] text-right"
  }, /* @__PURE__ */ React21.createElement(Duration, {
    duration: parseInt(endTs) - parseInt(startTs)
  })))))));
};

// src/components/performance/panels/SyncStatus/Space.tsx
import React22 from "react";
import { useClient as useClient4 } from "@dxos/react-client";
import { SpaceState as SpaceState2, useSpace } from "@dxos/react-client/echo";
import { mx as mx6 } from "@dxos/ui-theme";
var getSpaceDisplayName = (space, { personal } = {}) => {
  return space.state.get() === SpaceState2.SPACE_READY && (space.properties.name?.length ?? 0) > 0 ? space.properties.name : personal ? "Personal Space" : "New space";
};
var SpaceRowContainer = ({ spaceId, state }) => {
  const client = useClient4();
  const space = useSpace(spaceId);
  if (!space) {
    return null;
  }
  const spaceName = getSpaceDisplayName(space, {
    personal: space === client.spaces.default
  });
  return /* @__PURE__ */ React22.createElement(SpaceRow, {
    spaceId,
    spaceName,
    state
  });
};
var SpaceRow = ({ spaceId, spaceName, state: { localDocumentCount, remoteDocumentCount, missingOnLocal, missingOnRemote, totalDocumentCount, unsyncedDocumentCount } }) => {
  const isSynced = unsyncedDocumentCount === 0;
  const tooltip = [
    `Space: ${spaceName}`,
    `SpaceId: ${spaceId}`,
    `Status: ${isSynced ? "done" : "syncing"}`,
    `Total documents: ${totalDocumentCount}`,
    `Unsynced documents: ${unsyncedDocumentCount}`,
    `Local documents: ${localDocumentCount} (missing: ${missingOnLocal})`,
    `Remote documents: ${remoteDocumentCount} (missing: ${missingOnRemote})`
  ].join("\n");
  return /* @__PURE__ */ React22.createElement("div", {
    className: "flex items-center mx-0.5 gap-1 cursor-pointer",
    title: tooltip,
    onClick: () => {
      void navigator.clipboard.writeText(spaceId);
    }
  }, /* @__PURE__ */ React22.createElement("span", {
    className: "is-1/2 truncate"
  }, spaceName), /* @__PURE__ */ React22.createElement("span", {
    className: mx6("pli-1 plb-0.5 rounded text-xs shrink-0")
  }, isSynced ? `\u2705 total: ${totalDocumentCount}` : `\u2195 syncing: ${unsyncedDocumentCount}`));
};

// src/components/performance/panels/SyncStatus/SyncStatus.tsx
import React23 from "react";
import { IconButton as IconButton3 } from "@dxos/react-ui";
import { mx as mx7 } from "@dxos/ui-theme";
var SyncStatus = ({ classNames, state }) => {
  const entries = Object.entries(state);
  const handleCopyRaw = () => {
    void navigator.clipboard.writeText(JSON.stringify(state, null, 2));
  };
  return /* @__PURE__ */ React23.createElement("div", {
    className: mx7("flex flex-col is-full gap-2 text-xs", classNames)
  }, /* @__PURE__ */ React23.createElement("div", {
    className: "flex items-center gap-2"
  }, /* @__PURE__ */ React23.createElement(IconButton3, {
    icon: "ph--copy--regular",
    label: "copy raw",
    onClick: handleCopyRaw
  })), /* @__PURE__ */ React23.createElement("div", null, entries.map(([spaceId, state2]) => /* @__PURE__ */ React23.createElement(SpaceRowContainer, {
    key: spaceId,
    spaceId,
    state: state2
  }))));
};

// src/components/performance/panels/SyncStatus/SyncStatusPanel.tsx
import React24 from "react";
var SyncStatusPanel = ({ state, summary, debug, ...props }) => {
  return /* @__PURE__ */ React24.createElement(Panel, {
    ...props,
    icon: "ph--git-diff--regular",
    title: "Sync"
  }, /* @__PURE__ */ React24.createElement(SyncStatus, {
    state,
    summary,
    debug
  }));
};

// src/components/performance/panels/TimeSeries.tsx
import ChartStreaming from "@robloche/chartjs-plugin-streaming";
import { Chart, registerables } from "chart.js";
import React25, { createRef, useEffect as useEffect6, useState as useState8 } from "react";
import "chartjs-adapter-luxon";
Chart.register(...registerables);
Chart.register(ChartStreaming);
var SAMPLE_RATE = 2;
var TimeSeries = (props) => {
  const canvasRef = /* @__PURE__ */ createRef();
  const [chart, setChart] = useState8();
  useEffect6(() => {
    if (!canvasRef.current) {
      return void 0;
    }
    const chart2 = new Chart(canvasRef.current, {
      type: "line",
      data: {
        // https://nagix.github.io/chartjs-plugin-streaming/master/samples/charts/line-horizontal.html
        datasets: [
          {
            indexAxis: "x",
            fill: false,
            borderColor: "rgba(200, 0, 0, 0.5)",
            pointRadius: 0,
            tension: 0.2,
            data: [
              Date.now()
            ]
          }
        ]
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: false,
        layout: {
          padding: {
            top: 10
          }
        },
        scales: {
          x: {
            type: "realtime",
            // alignToPixels: true,
            realtime: {
              duration: 3e4,
              ttl: void 0
            }
          },
          y: {
            min: 0,
            max: 125,
            ticks: {
              stepSize: 30,
              includeBounds: false
            }
          }
        },
        plugins: {
          legend: {
            display: false
          },
          // https://nagix.github.io/chartjs-plugin-streaming/master/guide/options.html#options
          // https://nagix.github.io/chartjs-plugin-streaming/master/guide/data-feed-models.html#push-model-listening-based
          streaming: {
            frameRate: 30
          }
        }
      }
    });
    setChart(chart2);
    return () => chart2.destroy();
  }, []);
  useEffect6(() => {
    if (!chart) {
      return;
    }
    let last = 0;
    let running = true;
    const times = [];
    const refreshLoop = () => {
      if (!running) {
        return;
      }
      window.requestAnimationFrame(() => {
        const now = performance.now();
        while (times.length > 0 && times[0] <= now - 1e3) {
          times.shift();
        }
        times.push(now);
        const t = Date.now();
        if (!last || t - last > 1e3 / SAMPLE_RATE) {
          chart.data.datasets[0].data.push({
            x: t,
            y: times.length
          });
          last = t;
        }
        refreshLoop();
      });
    };
    refreshLoop();
    return () => {
      running = false;
    };
  }, [
    chart
  ]);
  return /* @__PURE__ */ React25.createElement(Panel, {
    ...props,
    icon: "ph--clock-countdown--regular",
    title: "FPS",
    padding: false
  }, /* @__PURE__ */ React25.createElement("div", {
    className: "relative is-full bs-[160px]"
  }, /* @__PURE__ */ React25.createElement("canvas", {
    ref: canvasRef
  })));
};

// src/components/performance/StatsPanel.tsx
var LOCAL_STORAGE_KEY = "dxos.org/plugin/debug/panels";
var PANEL_KEYS = [
  "ts",
  "performance",
  "spans",
  "edge",
  "queries",
  "rawQueries",
  "database",
  "logging",
  "memory",
  "replicator",
  "replicatorMessages",
  "sync"
];
var StatsPanel = ({ stats, onRefresh, children }) => {
  const [live, setLive] = useState9(false);
  const handleToggleLive = () => setLive((live2) => !live2);
  useEffect7(() => {
    if (live && onRefresh) {
      const interval = setInterval(onRefresh, 5e3);
      return () => clearInterval(interval);
    }
  }, [
    live,
    onRefresh
  ]);
  const rawQueries = (stats?.queries ?? []).reduce((acc, query) => {
    const raw = removeEmpty(query.filter);
    delete raw.options;
    raw.type = raw.type?.itemId;
    const str = JSON.stringify(raw);
    const num = acc.get(str) ?? 0;
    acc.set(str, num + 1);
    return acc;
  }, /* @__PURE__ */ new Map());
  const spans = [
    ...stats?.diagnostics?.spans ?? []
  ];
  spans.reverse();
  const queries = [
    ...stats?.queries ?? []
  ];
  queries.reverse();
  const syncState = useSyncState();
  const syncSummary = getSyncSummary(syncState);
  const [panelState, setPanelState] = useState9(() => PANEL_KEYS.reduce((acc, key) => {
    acc[key] = localStorage?.getItem(`${LOCAL_STORAGE_KEY}/${key}`) === "true";
    return acc;
  }, {}));
  const handleToggle = (id, open) => {
    setPanelState({
      ...panelState,
      [id]: open
    });
    localStorage?.setItem(`${LOCAL_STORAGE_KEY}/${id}`, String(open));
  };
  const props = (id) => ({
    id,
    open: panelState[id],
    onToggle: handleToggle
  });
  return /* @__PURE__ */ React26.createElement("div", {
    role: "none",
    className: "flex flex-col is-full bs-full max-bs-[calc(var(--radix-popover-content-available-height)-2*var(--dx-modalLine))] overflow-y-auto divide-y divide-separator"
  }, /* @__PURE__ */ React26.createElement(Panel, {
    id: "main",
    icon: "ph--chart-bar--regular",
    title: "Stats",
    info: /* @__PURE__ */ React26.createElement(Toggle, {
      pressed: live,
      classNames: "p-0 bg-transparent",
      density: "fine",
      value: "ghost",
      onClick: handleToggleLive
    }, /* @__PURE__ */ React26.createElement(Icon4, {
      icon: live ? "ph--pause--regular" : "ph--play--regular",
      size: 4
    }))
  }), /* @__PURE__ */ React26.createElement(LoggingPanel2, props("logging")), /* @__PURE__ */ React26.createElement(MemoryPanel, {
    id: "memory",
    memory: stats?.memory
  }), /* @__PURE__ */ React26.createElement(NetworkPanel2, {
    id: "network",
    network: stats?.network
  }), /* @__PURE__ */ React26.createElement(EdgePanel, {
    id: "edge",
    open: panelState.edge,
    edge: stats?.edge,
    onToggle: handleToggle
  }), /* @__PURE__ */ React26.createElement(PerformancePanel, {
    id: "performance",
    open: panelState.performance,
    onToggle: handleToggle,
    entries: stats?.performanceEntries
  }), /* @__PURE__ */ React26.createElement(DatabasePanel, {
    id: "database",
    open: panelState.database,
    onToggle: handleToggle,
    database: stats?.database
  }), /* @__PURE__ */ React26.createElement(ReplicatorPanel, {
    id: "replicator",
    open: panelState.replicator,
    onToggle: handleToggle,
    database: stats?.database
  }), /* @__PURE__ */ React26.createElement(ReplicatorMessagesPanel, {
    id: "replicatorMessages",
    open: panelState.replicatorMessages,
    onToggle: handleToggle,
    database: stats?.database
  }), /* @__PURE__ */ React26.createElement(SpansPanel, {
    id: "spans",
    open: panelState.spans,
    onToggle: handleToggle,
    spans
  }), /* @__PURE__ */ React26.createElement(QueriesPanel, {
    id: "queries",
    open: panelState.queries,
    onToggle: handleToggle,
    queries
  }), /* @__PURE__ */ React26.createElement(RawQueriesPanel, {
    id: "rawQueries",
    open: panelState.rawQueries,
    onToggle: handleToggle,
    queries: rawQueries
  }), /* @__PURE__ */ React26.createElement(SyncStatusPanel, {
    id: "sync",
    open: panelState.sync,
    onToggle: handleToggle,
    state: syncState,
    summary: syncSummary,
    debug: true
  }), /* @__PURE__ */ React26.createElement(TimeSeries, {
    id: "ts",
    open: panelState.ts,
    onToggle: handleToggle
  }), children);
};

// src/components/Bitbar.tsx
import React27 from "react";
import { useResizeDetector } from "react-resize-detector";
import { mx as mx8 } from "@dxos/ui-theme";
import { BitField, range } from "@dxos/util";
var Bitbar = ({ value, length = value.length * 8, size = 16, margin = 2, height = size, className }) => {
  const { ref, width } = useResizeDetector();
  const buckets = Math.min(Math.floor((width ?? 0) / (size + margin)), length);
  const getColor = (index) => {
    const start = Math.floor(index * length / buckets);
    let end = Math.ceil((index + 1) * length / buckets);
    if (end === start) {
      end = start + 1;
    }
    const count = BitField.count(value, start, end);
    const percent = count / (end - start);
    if (percent === 1) {
      return "bg-green-500";
    } else if (percent > 0.5) {
      return "bg-green-300";
    } else if (percent > 0) {
      return "bg-green-100";
    } else {
      return "bg-transparent";
    }
  };
  return /* @__PURE__ */ React27.createElement("div", {
    ref,
    className: mx8("flex shrink-0 is-full", className),
    style: {
      minHeight: height
    }
  }, range(buckets).map((index) => /* @__PURE__ */ React27.createElement("div", {
    key: index,
    style: {
      width: size,
      marginRight: margin
    },
    className: getColor(index)
  })));
};

// src/components/BooleanIcon.tsx
import React28 from "react";
import { Icon as Icon5 } from "@dxos/react-ui";
var BooleanIcon = ({ value }) => /* @__PURE__ */ React28.createElement(Icon5, {
  icon: value ? "ph--check--regular" : "ph--stop--regular"
});

// src/components/ControlledSelector.tsx
import React29 from "react";
import { Select as Select3 } from "@dxos/react-ui";
var ControlledSelector = (props) => {
  return /* @__PURE__ */ React29.createElement(Select3.Root, {
    value: props.value,
    onValueChange: props.setValue
  }, /* @__PURE__ */ React29.createElement(Select3.TriggerButton, {
    placeholder: props.placeholder ?? "Select space"
  }), /* @__PURE__ */ React29.createElement(Select3.Portal, null, /* @__PURE__ */ React29.createElement(Select3.Content, null, /* @__PURE__ */ React29.createElement(Select3.Viewport, null, props.values.map((mode) => /* @__PURE__ */ React29.createElement(Select3.Option, {
    key: mode,
    value: mode
  }, /* @__PURE__ */ React29.createElement("div", {
    className: "flex items-center gap-2"
  }, /* @__PURE__ */ React29.createElement("span", {
    className: "font-mono text-neutral-250"
  }, mode))))), /* @__PURE__ */ React29.createElement(Select3.Arrow, null))));
};

// src/components/ErrorBoundary.tsx
import React30, { Component } from "react";
import { log as log3 } from "@dxos/log";
var __dxlog_file5 = "/__w/dxos/dxos/packages/devtools/devtools/src/components/ErrorBoundary.tsx";
var ErrorPopup = ({ error, onReset }) => {
  return /* @__PURE__ */ React30.createElement("div", null, error.message);
};
var ErrorBoundary = class extends Component {
  constructor(props) {
    super(props);
    this.state = {
      hasError: false
    };
  }
  static getDerivedStateFromError(error) {
    log3.catch(error, void 0, {
      F: __dxlog_file5,
      L: 62,
      S: this,
      C: (f, a) => f(...a)
    });
    return {
      hasError: true,
      error
    };
  }
  render() {
    if (this.state.hasError) {
      return /* @__PURE__ */ React30.createElement(ErrorPopup, {
        error: this.state.error,
        onReset: () => {
          this.setState({
            hasError: false
          });
        }
      });
    }
    return this.props.children;
  }
};

// src/components/JsonView.tsx
import React31 from "react";
import { PublicKey as PublicKey2 } from "@dxos/keys";
import { schema } from "@dxos/protocols/proto";
import { SyntaxHighlighter } from "@dxos/react-ui-syntax-highlighter";
import { arrayToBuffer } from "@dxos/util";
var JsonView = ({ data, truncate = true }) => {
  return /* @__PURE__ */ React31.createElement(SyntaxHighlighter, {
    language: "json"
  }, JSON.stringify(data, replacer(truncate), 2));
};
var replacer = (truncate = false) => (key, value) => {
  if (typeof value === "object") {
    if (truncate) {
      if (value instanceof PublicKey2) {
        return value.truncate();
      }
    }
    if (value instanceof Uint8Array) {
      return arrayToBuffer(value).toString("hex");
    }
    if (value?.type === "Buffer") {
      return Buffer.from(value.data).toString("hex");
    }
    if (value?.["@type"] === "google.protobuf.Any") {
      try {
        const codec = schema.getCodecForType(value.type_url);
        return {
          "@type": value.type_url,
          ...codec.decode(value.value)
        };
      } catch {
      }
    }
  }
  return value;
};

// src/components/MasterDetailTable.tsx
import React33, { useCallback as useCallback3, useEffect as useEffect8, useMemo as useMemo5, useState as useState10 } from "react";
import { DynamicTable } from "@dxos/react-ui-table";
import { mx as mx9 } from "@dxos/ui-theme";

// src/components/Placeholder.tsx
import React32 from "react";
var Placeholder = ({ label }) => {
  return /* @__PURE__ */ React32.createElement("div", {
    className: "p-1 text-sm text-subdued"
  }, label);
};

// src/components/MasterDetailTable.tsx
var MasterDetailTable = ({ properties, data, detailsTransform, detailsPosition = "right", onSelectionChanged }) => {
  const [selected, setSelected] = useState10(void 0);
  const [transformedData, setTransformedData] = useState10(void 0);
  const [isLoading, setIsLoading] = useState10(false);
  const handleRowClicked = useCallback3((row) => {
    setSelected(row);
    onSelectionChanged?.(row.id);
  }, [
    onSelectionChanged,
    setSelected
  ]);
  useEffect8(() => {
    if (!selected) {
      setTransformedData(void 0);
      return;
    }
    if (!detailsTransform) {
      setTransformedData(selected);
      return;
    }
    const result = detailsTransform(selected);
    if (result instanceof Promise) {
      setIsLoading(true);
      result.then((data2) => {
        setTransformedData(data2);
      }).catch((error) => {
        setTransformedData({
          error: String(error)
        });
      }).finally(() => {
        setIsLoading(false);
      });
    } else {
      setTransformedData(result);
    }
  }, [
    selected,
    detailsTransform
  ]);
  const gridLayout = useMemo5(() => {
    return detailsPosition === "right" ? "grid grid-cols-[2fr_1fr]" : "grid grid-rows-[3fr_4fr]";
  }, [
    detailsPosition
  ]);
  const features = useMemo5(() => ({
    selection: {
      enabled: true,
      mode: "single"
    },
    dataEditable: false
  }), []);
  return /* @__PURE__ */ React33.createElement("div", {
    className: mx9("bs-full divide-y divide-separator", gridLayout)
  }, /* @__PURE__ */ React33.createElement(DynamicTable, {
    properties,
    rows: data,
    features,
    onRowClick: handleRowClicked
  }), /* @__PURE__ */ React33.createElement("div", {
    className: mx9("overflow-auto text-sm", detailsPosition === "right" && "border-separator border-is")
  }, selected ? isLoading ? /* @__PURE__ */ React33.createElement("p", {
    className: mx9("font-mono text-xs p-1")
  }, "Loading details...") : /* @__PURE__ */ React33.createElement(JsonView, {
    data: transformedData
  }) : /* @__PURE__ */ React33.createElement(Placeholder, {
    label: "Details"
  })));
};

// src/components/PanelContainer.tsx
import React34 from "react";
import { mx as mx10 } from "@dxos/ui-theme";
var PanelContainer = ({ classNames, children, toolbar, footer }) => {
  return /* @__PURE__ */ React34.createElement("div", {
    className: "flex flex-col bs-full overflow-hidden divide-y divide-separator"
  }, toolbar, /* @__PURE__ */ React34.createElement("div", {
    className: mx10("flex flex-col bs-full overflow-auto", classNames)
  }, children), footer);
};

// src/components/PropertiesTable.tsx
import { sentenceCase } from "change-case";
import { format } from "date-fns/format";
import { formatDistance } from "date-fns/formatDistance";
import React35 from "react";
import { log as log4 } from "@dxos/log";
var __dxlog_file6 = "/__w/dxos/dxos/packages/devtools/devtools/src/components/PropertiesTable.tsx";
var styles = {
  key: "text-mono text-green-500",
  unit: "text-sm text-neutral-400 ml-1"
};
var PropertySchemaFormat = {
  key: () => ({
    format: (value) => /* @__PURE__ */ React35.createElement("span", {
      className: styles.key
    }, value.truncate())
  }),
  number: (unit) => ({
    format: (value) => /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", null, value?.toLocaleString()), /* @__PURE__ */ React35.createElement("span", {
      className: styles.unit
    }, unit))
  }),
  unit: (unit, precision = 2) => ({
    format: (value) => {
      const e = Math.floor(Math.log(value) / Math.log(1024));
      return /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", null, (value / Math.pow(1024, e)).toFixed(precision)), /* @__PURE__ */ React35.createElement("span", {
        className: styles.unit
      }, (e > 0 ? "kMGTP".charAt(e - 1) : "") + unit));
    }
  }),
  percent: (precision = 0) => ({
    format: (value) => /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", null, value?.toFixed(precision)), /* @__PURE__ */ React35.createElement("span", {
      className: styles.unit
    }, "%"))
  }),
  date: (options = {}) => ({
    format: (value) => {
      const date = value instanceof Date ? value : new Date(value);
      const parts = [];
      if (options.format) {
        parts.push(format(date, options.format));
      }
      if (options.relative) {
        parts.push(formatDistance(date, Date.now(), {
          addSuffix: true
        }));
      }
      if (parts.length === 0) {
        parts.push(date.toISOString());
      }
      return /* @__PURE__ */ React35.createElement(React35.Fragment, null, /* @__PURE__ */ React35.createElement("span", null, parts[0]), parts.length > 1 && /* @__PURE__ */ React35.createElement("span", {
        className: styles.unit
      }, "(", parts[1], ")"));
    }
  })
};
var defaultSlots = {
  key: {
    className: "pli-4 align-baseline text-neutral-500 overflow-hidden is-40 text-sm"
  }
};
var PropertiesTable = ({ schema: schema2, object, slots = defaultSlots }) => {
  const format2 = (key, value) => {
    try {
      return schema2?.[key]?.format(value) ?? String(value);
    } catch (err) {
      log4.catch(err, void 0, {
        F: __dxlog_file6,
        L: 99,
        S: void 0,
        C: (f, a) => f(...a)
      });
      return "<error>";
    }
  };
  return /* @__PURE__ */ React35.createElement("table", {
    className: "table-fixed border-collapse"
  }, /* @__PURE__ */ React35.createElement("tbody", null, Object.entries(object).map(([key, value]) => /* @__PURE__ */ React35.createElement("tr", {
    key,
    className: "align-baseline leading-6"
  }, /* @__PURE__ */ React35.createElement("td", {
    className: slots?.key?.className
  }, sentenceCase(key).toLowerCase()), /* @__PURE__ */ React35.createElement("td", null, /* @__PURE__ */ React35.createElement("div", {
    className: "font-mono"
  }, format2(key, value)))))));
};

// src/components/PublicKeySelector.tsx
import React36 from "react";
import { PublicKey as PublicKey3 } from "@dxos/react-client";
import { Select as Select4 } from "@dxos/react-ui";
import { humanize as humanize2 } from "@dxos/util";
var PublicKeySelector = ({ placeholder, getLabel = humanize2, keys, value, onChange }) => {
  return /* @__PURE__ */ React36.createElement(Select4.Root, {
    value: value?.toHex(),
    onValueChange: (id) => {
      id && onChange?.(PublicKey3.fromHex(id));
    }
  }, /* @__PURE__ */ React36.createElement(Select4.TriggerButton, {
    placeholder
  }), /* @__PURE__ */ React36.createElement(Select4.Portal, null, /* @__PURE__ */ React36.createElement(Select4.Content, null, /* @__PURE__ */ React36.createElement(Select4.Viewport, null, removeDuplicates(keys).map((key) => /* @__PURE__ */ React36.createElement(Select4.Option, {
    key: key.toHex(),
    value: key.toHex()
  }, /* @__PURE__ */ React36.createElement("div", {
    className: "flex items-center gap-2"
  }, /* @__PURE__ */ React36.createElement("span", {
    className: "font-mono text-neutral-250"
  }, key.truncate()), getLabel(key))))), /* @__PURE__ */ React36.createElement(Select4.Arrow, null))));
};
var removeDuplicates = (keys) => keys.reduce((result, key) => {
  if (key !== void 0 && !result.some((existing) => existing.equals(key))) {
    result.push(key);
  }
  return result;
}, []);

// src/components/Searchbar.tsx
import React37 from "react";
import { Input as Input2, useControlledState } from "@dxos/react-ui";
var Searchbar = ({ placeholder, value, onChange }) => {
  const [text, setText] = useControlledState(value ?? "", onChange);
  return /* @__PURE__ */ React37.createElement("div", {
    role: "none",
    className: "flex is-full items-center"
  }, /* @__PURE__ */ React37.createElement(Input2.Root, null, /* @__PURE__ */ React37.createElement(Input2.TextInput, {
    placeholder,
    value: text,
    onChange: ({ target }) => setText(target.value)
  })));
};

// src/components/Select.tsx
import React38 from "react";
import { Toolbar as Toolbar2, Select as UiSelect } from "@dxos/react-ui";
var Select2 = ({ items = [], ...props }) => {
  return /* @__PURE__ */ React38.createElement(UiSelect.Root, props, /* @__PURE__ */ React38.createElement(Toolbar2.Button, {
    asChild: true
  }, /* @__PURE__ */ React38.createElement(UiSelect.TriggerButton, {
    placeholder: "Select value"
  })), /* @__PURE__ */ React38.createElement(UiSelect.Portal, null, /* @__PURE__ */ React38.createElement(UiSelect.Content, null, /* @__PURE__ */ React38.createElement(UiSelect.Viewport, null, items?.map(({ value, label }) => /* @__PURE__ */ React38.createElement(UiSelect.Option, {
    key: value,
    value
  }, /* @__PURE__ */ React38.createElement("span", {
    className: "font-mono"
  }, label)))))));
};

// src/components/Tree.tsx
import React39, { useState as useState11 } from "react";
import { mx as mx11 } from "@dxos/ui-theme";
var Tree = ({ data }) => {
  return /* @__PURE__ */ React39.createElement("div", {
    className: "flex is-full plb-2 overflow-auto"
  }, /* @__PURE__ */ React39.createElement(Node, {
    data,
    root: true
  }));
};
var Node = ({ data }) => {
  if (typeof data !== "object" || data === void 0 || data === null) {
    return /* @__PURE__ */ React39.createElement(Scalar, {
      value: data
    });
  }
  if (Array.isArray(data)) {
    return /* @__PURE__ */ React39.createElement("div", {
      className: "flex flex-col space-y-2"
    }, data.map((value, index) => /* @__PURE__ */ React39.createElement(KeyValue, {
      key: index,
      label: String(index),
      data: value,
      className: "text-description font-thin"
    })));
  }
  return /* @__PURE__ */ React39.createElement("div", {
    className: "flex flex-col space-y-2"
  }, Object.entries(data).map(([key, value]) => /* @__PURE__ */ React39.createElement(KeyValue, {
    key,
    label: key,
    data: value,
    className: "bg-groupSurface text-description font-thin"
  })));
};
var KeyValue = ({ label, data, className }) => {
  const [open, setOpen] = useState11(true);
  if (data === void 0) {
    return null;
  }
  return /* @__PURE__ */ React39.createElement("div", {
    className: "flex"
  }, /* @__PURE__ */ React39.createElement(Box, {
    className: mx11("bg-inputSurface text-sm select-none cursor-pointer", className),
    onClick: () => setOpen((open2) => !open2)
  }, label), open && /* @__PURE__ */ React39.createElement(Node, {
    data
  }));
};
var Scalar = ({ value }) => {
  return /* @__PURE__ */ React39.createElement(Box, {
    className: "bg-skySurface text-information rounded-r-sm text-sm font-thin"
  }, value === void 0 && "undefined" || value === null && "null" || typeof value === "string" && value || JSON.stringify(value));
};
var Box = ({ children, className, ...props }) => {
  return /* @__PURE__ */ React39.createElement("div", {
    className: mx11("flex pli-2 font-mono truncate", className),
    ...props
  }, children);
};

// src/components/ObjectViewer.tsx
import React40, { useCallback as useCallback4 } from "react";
import { DXN } from "@dxos/keys";
import { Button, Clipboard, Input as Input3 } from "@dxos/react-ui";
import { SyntaxHighlighter as SyntaxHighlighter2, createElement } from "@dxos/react-ui-syntax-highlighter";
var ObjectViewer = ({ object, id, onNavigate }) => {
  const text = JSON.stringify(object, null, 2);
  const rowRenderer = ({ rows, stylesheet, useInlineStyles }) => {
    const addDxnLinks = (node) => {
      if (isDxnSpanNode(node)) {
        node.tagName = "a";
        node.properties ??= {
          className: []
        };
        node.properties.className.push("underline", "cursor-pointer");
        node.properties.onClick = () => {
          onNavigate?.(DXN.parse(node.children[0].value.slice(1, -1)));
        };
      } else {
        node.children?.forEach(addDxnLinks);
      }
    };
    rows.forEach(addDxnLinks);
    return rows.map((row, index) => {
      return createElement({
        node: row,
        stylesheet,
        style: {},
        useInlineStyles,
        key: index
      });
    });
  };
  const handleCopy = useCallback4(() => {
    void navigator.clipboard.writeText(JSON.stringify(object, null, 2));
  }, [
    object
  ]);
  return /* @__PURE__ */ React40.createElement(React40.Fragment, null, id && /* @__PURE__ */ React40.createElement(Clipboard.Provider, null, /* @__PURE__ */ React40.createElement("div", {
    className: "flex flex-col"
  }, /* @__PURE__ */ React40.createElement(Input3.Root, null, /* @__PURE__ */ React40.createElement("div", {
    role: "none",
    className: "flex flex-col gap-1"
  }, /* @__PURE__ */ React40.createElement("div", {
    role: "none",
    className: "flex gap-1"
  }, /* @__PURE__ */ React40.createElement(Input3.TextInput, {
    disabled: true,
    value: id
  }), /* @__PURE__ */ React40.createElement(Clipboard.IconButton, {
    value: id
  }), /* @__PURE__ */ React40.createElement(Button, {
    value: id,
    onClick: handleCopy
  }, "Copy JSON")))))), /* @__PURE__ */ React40.createElement(SyntaxHighlighter2, {
    language: "json",
    classNames: "text-sm",
    renderer: rowRenderer
  }, text));
};
var isDxnSpanNode = (node) => {
  return node.type === "element" && node.tagName === "span" && node.children?.length === 1 && node.children[0].type === "text" && typeof node.children[0].value === "string" && node.children[0].value.match(/^"(dxn:[^"]+)"$/);
};

// src/panels/echo/SpaceInfoPanel/FeedTable.tsx
import React41, { useMemo as useMemo6 } from "react";
import { Format } from "@dxos/echo/internal";
import { useDevtools as useDevtools5, useStream as useStream4 } from "@dxos/react-client/devtools";
import { DynamicTable as DynamicTable2 } from "@dxos/react-ui-table";

// src/util/textBitbar.ts
import { BitField as BitField2, range as range2 } from "@dxos/util";
var createTextBitbar = (value, length, maxBars = 20) => {
  if (!value || !length) {
    return "";
  }
  return range2(maxBars).map((i) => {
    const start = Math.floor(i * length / maxBars);
    let end = Math.ceil((i + 1) * length / maxBars);
    if (end === start) {
      end = start + 1;
    }
    const count = BitField2.count(value, start, end);
    const percent = count / (end - start);
    if (percent === 1) {
      return "\u2593";
    } else if (percent > 0.5) {
      return "\u2592";
    } else if (percent > 0) {
      return "\u2591";
    } else {
      return " ";
    }
  }).join(" ");
};

// src/panels/echo/SpaceInfoPanel/FeedTable.tsx
var FeedTable = ({ onSelect }) => {
  const { space } = useDevtoolsState();
  const setContext = useDevtoolsDispatch();
  const feedKeys = [
    ...space?.internal.data.pipeline?.controlFeeds ?? [],
    ...space?.internal.data.pipeline?.dataFeeds ?? []
  ];
  const devtoolsHost = useDevtools5();
  const { feeds = [] } = useStream4(() => devtoolsHost.subscribeToFeeds({
    feedKeys
  }), {}, [
    feedKeys
  ]);
  const maxLength = feeds.reduce((max, feed) => feed?.length > max ? feed.length : max, 0);
  const properties = useMemo6(() => [
    {
      name: "feedKey",
      format: Format.TypeFormat.DID
    },
    {
      name: "progress",
      format: Format.TypeFormat.String,
      size: 600
    }
  ], []);
  const rows = useMemo6(() => {
    return feeds.map((feed) => ({
      id: feed.feedKey.toString(),
      feedKey: feed.feedKey.toString(),
      progress: createTextBitbar(feed.downloaded, maxLength),
      _original: {
        ...feed,
        maxLength
      }
    }));
  }, [
    feeds,
    maxLength
  ]);
  const handleRowClick = (row) => {
    if (row?._original !== void 0) {
      setContext((ctx) => ({
        ...ctx,
        feedKey: row._original?.feedKey
      }));
      onSelect?.(row._original);
    }
  };
  const features = useMemo6(() => ({
    selection: {
      enabled: true,
      mode: "single"
    }
  }), []);
  return /* @__PURE__ */ React41.createElement(DynamicTable2, {
    properties,
    rows,
    features,
    onRowClick: handleRowClick
  });
};

// src/panels/echo/SpaceInfoPanel/PipelineTable.tsx
import React42, { useCallback as useCallback5, useMemo as useMemo7 } from "react";
import { Format as Format2 } from "@dxos/echo/internal";
import { PublicKey as PublicKey4 } from "@dxos/keys";
import { DynamicTable as DynamicTable3 } from "@dxos/react-ui-table";
import { Timeframe } from "@dxos/timeframe";
import { ComplexSet } from "@dxos/util";
var PipelineTable = ({ state, metadata, onSelect }) => {
  const setContext = useDevtoolsDispatch();
  const properties = useMemo7(() => [
    {
      name: "feedKey",
      format: Format2.TypeFormat.DID
    },
    {
      name: "type",
      format: Format2.TypeFormat.SingleSelect,
      size: 80,
      config: {
        options: [
          {
            id: "control",
            title: "control",
            color: "blue"
          },
          {
            id: "data",
            title: "data",
            color: "sky"
          }
        ]
      }
    },
    {
      name: "own",
      format: Format2.TypeFormat.Boolean,
      size: 90
    },
    {
      name: "genesis",
      format: Format2.TypeFormat.Boolean,
      size: 100
    },
    {
      name: "start",
      format: Format2.TypeFormat.Number,
      size: 100
    },
    {
      name: "target",
      format: Format2.TypeFormat.Number,
      size: 100
    },
    {
      name: "processed",
      format: Format2.TypeFormat.Number,
      size: 100
    },
    {
      name: "total",
      format: Format2.TypeFormat.Number,
      size: 100
    },
    {
      name: "progress",
      format: Format2.TypeFormat.String,
      size: 100
    }
  ], []);
  const getType = (feedKey) => {
    if (metadata) {
      return {
        genesis: feedKey.equals(metadata?.genesisFeed),
        own: feedKey.equals(metadata?.controlFeed) || feedKey.equals(metadata?.dataFeed)
      };
    }
    return {
      genesis: false,
      own: false
    };
  };
  const rows = useMemo7(() => {
    const controlKeys = Array.from(new ComplexSet(PublicKey4.hash, [
      ...state.controlFeeds ?? [],
      ...Timeframe.merge(state.currentControlTimeframe ?? new Timeframe(), state.targetControlTimeframe ?? new Timeframe(), state.totalControlTimeframe ?? new Timeframe(), state.knownControlTimeframe ?? new Timeframe()).frames().map(([key]) => key)
    ]));
    const dataKeys = Array.from(new ComplexSet(PublicKey4.hash, [
      ...state.dataFeeds ?? [],
      ...Timeframe.merge(state.currentDataTimeframe ?? new Timeframe(), state.targetDataTimeframe ?? new Timeframe(), state.totalDataTimeframe ?? new Timeframe(), state.knownDataTimeframe ?? new Timeframe()).frames().map(([key]) => key)
    ]));
    const tableRows = [
      ...controlKeys.map((feedKey) => {
        const start = 0;
        const processed = state.currentControlTimeframe?.get(feedKey);
        const target = state.targetControlTimeframe?.get(feedKey);
        const total = state.totalControlTimeframe?.get(feedKey);
        const percent = ((processed ?? 0) - start) / ((target ?? 0) - start) * 100;
        const progress = !isNaN(percent) ? `${Math.min(percent, 100).toFixed(0)}%` : void 0;
        return {
          id: feedKey.toString(),
          feedKey,
          type: "control",
          ...getType(feedKey),
          start,
          processed,
          target,
          total,
          progress
        };
      }),
      ...dataKeys.map((feedKey) => {
        const start = state.startDataTimeframe?.get(feedKey) ?? 0;
        const processed = state.currentDataTimeframe?.get(feedKey);
        const target = state.targetDataTimeframe?.get(feedKey);
        const total = state.totalDataTimeframe?.get(feedKey);
        const percent = ((processed ?? 0) - start) / ((target ?? 0) - start) * 100;
        const progress = !isNaN(percent) ? `${Math.min(percent, 100).toFixed(0)}%` : void 0;
        return {
          id: feedKey.toString(),
          feedKey,
          type: "data",
          ...getType(feedKey),
          start,
          processed,
          target,
          total,
          progress
        };
      })
    ];
    return tableRows;
  }, [
    state,
    metadata
  ]);
  const handleRowClicked = useCallback5((row) => {
    if (row) {
      setContext((ctx) => ({
        ...ctx,
        feedKey: row.feedKey
      }));
      onSelect?.(row);
    } else {
      setContext((ctx) => ({
        ...ctx,
        feedKey: void 0
      }));
    }
  }, [
    onSelect,
    setContext
  ]);
  const features = useMemo7(() => ({
    selection: {
      enabled: true,
      mode: "single"
    }
  }), []);
  return /* @__PURE__ */ React42.createElement(DynamicTable3, {
    properties,
    rows,
    features,
    onRowClick: handleRowClicked
  });
};

// src/panels/echo/SpaceInfoPanel/SpaceProperties.tsx
import React43, { useMemo as useMemo8 } from "react";
import { MulticastObservable } from "@dxos/async";
import { SpaceState as SpaceState3 } from "@dxos/protocols/proto/dxos/client/services";
import { useMulticastObservable } from "@dxos/react-hooks";
import { Timeframe as Timeframe2 } from "@dxos/timeframe";
var SpaceProperties = ({ space, metadata }) => {
  const pipelineState = useMulticastObservable(space?.pipeline ?? MulticastObservable.empty());
  const details = useMemo8(async () => {
    if (!metadata) {
      return;
    }
    const pipeline = space?.internal.data?.pipeline;
    const currentEpochNumber = pipeline?.currentEpoch?.subject.assertion.number;
    const appliedEpochNumber = pipeline?.appliedEpoch?.subject.assertion.number;
    const epochTimeframe = pipeline?.currentEpoch?.subject.assertion.timeframe ?? new Timeframe2();
    const targetControlMessages = pipeline?.targetControlTimeframe?.totalMessages() ?? 0;
    const currentControlMessages = pipeline?.currentControlTimeframe?.totalMessages() ?? 0;
    const controlProgress = Math.min(currentControlMessages / targetControlMessages, 1) * 100;
    const startDataMessages = pipeline?.startDataTimeframe?.totalMessages() ?? 0;
    const targetDataMessages = pipeline?.targetDataTimeframe?.totalMessages() ?? 0;
    const currentDataMessages = pipeline?.currentDataTimeframe?.totalMessages() ?? 0;
    const dataProgress = Math.min(Math.abs((currentDataMessages - startDataMessages) / (targetDataMessages - startDataMessages) || 1), 1) * 100;
    const { open, ready } = space?.internal.data?.metrics ?? {};
    const startupTime = open && ready && ready.getTime() - open.getTime();
    return {
      key: metadata.key,
      state: SpaceState3[space.state.get()],
      startupTime,
      controlProgress,
      dataProgress,
      currentEpoch: currentEpochNumber === appliedEpochNumber ? currentEpochNumber : `${currentEpochNumber} (${appliedEpochNumber})`,
      epochCreated: pipeline?.currentEpoch?.issuanceDate,
      epochMutations: pipeline?.currentEpoch?.subject.assertion.timeframe.totalMessages(),
      mutationsSinceEpoch: pipeline?.totalDataTimeframe?.newMessages(epochTimeframe)
    };
  }, [
    space,
    metadata,
    pipelineState
  ]);
  const schema2 = useMemo8(() => ({
    key: PropertySchemaFormat.key(),
    startupTime: PropertySchemaFormat.number("ms"),
    controlProgress: PropertySchemaFormat.percent(1),
    dataProgress: PropertySchemaFormat.percent(1),
    epochCreated: PropertySchemaFormat.date({
      format: "MM:dd HH:mm:ss",
      relative: true
    }),
    epochMutations: PropertySchemaFormat.number(),
    mutationsSinceEpoch: PropertySchemaFormat.number()
  }), []);
  if (!details) {
    return null;
  }
  return /* @__PURE__ */ React43.createElement(PropertiesTable, {
    schema: schema2,
    object: details
  });
};

// src/panels/echo/SpaceInfoPanel/SyncStateInfo.tsx
import React44, { useEffect as useEffect9, useState as useState12 } from "react";
import { Context as Context2 } from "@dxos/context";
var __dxlog_file7 = "/__w/dxos/dxos/packages/devtools/devtools/src/panels/echo/SpaceInfoPanel/SyncStateInfo.tsx";
var SyncStateInfo = ({ space }) => {
  const [syncState, setSyncState] = useState12({});
  useEffect9(() => {
    if (space) {
      return space.internal.db.coreDatabase.subscribeToSyncState(Context2.default(void 0, {
        F: __dxlog_file7,
        L: 22
      }), (syncState2) => {
        setSyncState(syncState2);
      });
    }
  }, [
    space
  ]);
  return /* @__PURE__ */ React44.createElement("div", {
    className: "p-2 text-sm"
  }, /* @__PURE__ */ React44.createElement("p", {
    className: "text-base"
  }, "Sync state"), /* @__PURE__ */ React44.createElement(JsonView, {
    data: syncState
  }));
};

// src/panels/echo/SpaceInfoPanel/SpaceInfoPanel.tsx
var SpaceInfoPanel2 = (props) => {
  const [, forceUpdate] = useState13({});
  const state = useDevtoolsState();
  const space = props.space ?? state.space;
  const spacesInfo = useSpacesInfo();
  const metadata = space?.key && spacesInfo.find((info) => info.key.equals(space?.key));
  const pipelineState = useMulticastObservable2(space?.pipeline ?? MulticastObservable2.empty());
  const toggleActive = async () => {
    const state2 = space.state.get();
    if (state2 === SpaceState4.SPACE_INACTIVE) {
      await space.open();
    } else {
      await space.close();
    }
  };
  const toggleEdgeReplication = async () => {
    await space?.internal.setEdgeReplicationPreference(space?.internal.data.edgeReplication === EdgeReplicationSetting.ENABLED ? EdgeReplicationSetting.DISABLED : EdgeReplicationSetting.ENABLED);
    setTimeout(() => forceUpdate({}), 500);
  };
  const toolbar = useMemo9(() => /* @__PURE__ */ React45.createElement(Toolbar3.Root, null, !props.space && /* @__PURE__ */ React45.createElement(DataSpaceSelector, null), /* @__PURE__ */ React45.createElement(Toolbar3.IconButton, {
    icon: "ph--arrow-clockwise--regular",
    iconOnly: true,
    label: "Refresh",
    onClick: () => forceUpdate({})
  }), /* @__PURE__ */ React45.createElement("div", {
    className: "grow"
  }), /* @__PURE__ */ React45.createElement(Toolbar3.Button, {
    onClick: toggleActive
  }, space?.state.get() === SpaceState4.SPACE_INACTIVE ? "Open" : "Close"), /* @__PURE__ */ React45.createElement(Toolbar3.Button, {
    onClick: toggleEdgeReplication
  }, space?.internal.data.edgeReplication === EdgeReplicationSetting.ENABLED ? "Disable backup to EDGE" : "Enable backup to EDGE")), [
    props.space,
    space?.state,
    space?.internal.data.edgeReplication
  ]);
  return /* @__PURE__ */ React45.createElement(PanelContainer, {
    toolbar
  }, space && metadata && /* @__PURE__ */ React45.createElement("div", null, /* @__PURE__ */ React45.createElement(SpaceProperties, {
    space,
    metadata
  }), /* @__PURE__ */ React45.createElement("div", {
    className: "bs-24"
  }, /* @__PURE__ */ React45.createElement(PipelineTable, {
    state: pipelineState ?? {},
    metadata,
    onSelect: props.onSelectPipeline
  })), /* @__PURE__ */ React45.createElement("div", {
    className: "bs-48"
  }, /* @__PURE__ */ React45.createElement(FeedTable, {
    onSelect: props.onSelectFeed
  })), /* @__PURE__ */ React45.createElement("div", {
    className: "border-bs border-separator"
  }, /* @__PURE__ */ React45.createElement(SyncStateInfo, {
    space
  }))));
};

// src/panels/echo/SpaceInfoPanel/index.ts
var SpaceInfoPanel_default = SpaceInfoPanel2;

export {
  useCredentials,
  DevtoolsContext,
  DevtoolsContextProvider,
  useDevtoolsState,
  useDevtoolsDispatch,
  useFeedMessages,
  useMetadata,
  useProxiedClient,
  DataSpaceSelector,
  Panel,
  StatsPanel,
  Bitbar,
  BooleanIcon,
  ControlledSelector,
  ErrorBoundary,
  JsonView,
  Placeholder,
  MasterDetailTable,
  PanelContainer,
  PropertySchemaFormat,
  defaultSlots,
  PropertiesTable,
  PublicKeySelector,
  Searchbar,
  Select2 as Select,
  Tree,
  Node,
  KeyValue,
  ObjectViewer,
  getTarget,
  VaultSelector,
  EdgeSelector,
  RootContainer,
  SpaceSelector,
  ConfigPanel,
  DiagnosticsPanel,
  LoggingPanel,
  StoragePanel,
  TracingPanel,
  SyncStateInfo,
  SpaceInfoPanel_default,
  AutomergePanel,
  FeedsPanel,
  MembersPanel,
  MetadataPanel,
  ObjectsPanel,
  QueuesPanel,
  SpaceInfoPanel,
  SpaceListPanel,
  SchemaPanel,
  CredentialsPanel,
  DeviceListPanel,
  IdentityPanel,
  KeyringPanel,
  SignalPanel,
  SwarmPanel,
  NetworkPanel,
  EdgeDashboardPanel,
  InvocationTraceContainer,
  InvocationTracePanel,
  TestingPanel,
  WorkflowPanel,
  namespace,
  useRoutes,
  useSections,
  useSpacesInfo,
  useSpaceInfo,
  useSignal,
  usePerformanceObserver,
  useStats,
  removeEmpty
};
//# sourceMappingURL=chunk-DWYIZZUB.mjs.map
