import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
import {
  Bitbar,
  JsonView,
  PanelContainer
} from "./chunk-DWYIZZUB.mjs";

// src/panels/client/StoragePanel/StoragePanel.tsx
import bytes from "bytes";
import React, { useEffect, useMemo, useState } from "react";
import { log } from "@dxos/log";
import { BlobMeta } from "@dxos/protocols/proto/dxos/echo/blob";
import { PublicKey, useClient } from "@dxos/react-client";
import { useDevtools, useStream } from "@dxos/react-client/devtools";
import { useAsyncEffect } from "@dxos/react-hooks";
import { DropdownMenu, Icon, Toolbar, Tree, TreeItem } from "@dxos/react-ui";
import { BitField } from "@dxos/util";
var __dxlog_file = "/__w/dxos/dxos/packages/devtools/devtools/src/panels/client/StoragePanel/StoragePanel.tsx";
var getInfoTree = (storageInfo, feedInfo, snapshots, blobs) => [
  {
    id: "origin",
    iconName: "ph--git-commit--regular",
    Element: /* @__PURE__ */ React.createElement(TreeItemText, {
      primary: "origin",
      secondary: `${bytes.format(storageInfo.originUsage)} / ${bytes.format(storageInfo.usageQuota)} ${formatPercent(storageInfo.originUsage / storageInfo.usageQuota)}`
    }),
    items: [
      {
        id: "storage",
        iconName: "ph--hard-drive--regular",
        Element: /* @__PURE__ */ React.createElement(TreeItemText, {
          primary: storageInfo.type,
          secondary: bytes.format(storageInfo.storageUsage)
        }),
        items: [
          {
            id: "feeds",
            iconName: "ph--queue--regular",
            Element: /* @__PURE__ */ React.createElement(TreeItemText, {
              primary: "feeds",
              secondary: feedInfo.feeds?.length ?? 0
            }),
            items: feedInfo.feeds?.map((feed) => ({
              id: feed.feedKey.toHex(),
              iconName: "ph--rows--regular",
              Element: /* @__PURE__ */ React.createElement(TreeItemText, {
                primary: feed.feedKey.truncate(),
                secondary: bytes.format(feed.bytes)
              }),
              value: {
                kind: "feed",
                feed
              }
            }))
          },
          {
            id: "blobs",
            iconName: "ph--files--regular",
            Element: /* @__PURE__ */ React.createElement(TreeItemText, {
              primary: "blobs",
              secondary: blobs.length
            }),
            items: blobs.map((blob) => ({
              id: PublicKey.from(blob.id).toHex(),
              iconName: "ph--file-archive--regular",
              Element: /* @__PURE__ */ React.createElement(TreeItemText, {
                primary: PublicKey.from(blob.id).truncate(),
                secondary: bytes.format(blob.length)
              }),
              value: {
                kind: "blob",
                blob
              }
            }))
          },
          {
            id: "snapshots",
            iconName: "ph--bookmarks--regular",
            Element: /* @__PURE__ */ React.createElement(TreeItemText, {
              primary: "snapshots",
              secondary: snapshots.length
            }),
            items: snapshots.map((snapshot) => ({
              id: snapshot.key,
              iconName: "ph--bookmark--regular",
              Element: /* @__PURE__ */ React.createElement(TreeItemText, {
                primary: snapshot.key,
                secondary: bytes.format(snapshot.size)
              }),
              value: {
                kind: "snapshot"
              }
            }))
          }
        ]
      }
    ]
  }
];
var StoragePanel = () => {
  const devtoolsHost = useDevtools();
  const [isRefreshing, setIsRefreshing] = useState(false);
  const [storageInfo, setStorageInfo] = useState();
  const [snapshotInfo, setSnapshotInfo] = useState();
  const [blobsInfo, setBlobsInfo] = useState();
  const feeds = useStream(() => devtoolsHost.subscribeToFeeds({}), {}, []);
  const client = useClient();
  const services = client.services.services;
  if (!services) {
    return null;
  }
  const [selected, setSelected] = useState();
  const refresh = async () => {
    setIsRefreshing(true);
    let retry = false;
    let storageInfo2;
    let snapshotInfo2;
    let blobsInfo2;
    try {
      storageInfo2 = await devtoolsHost.getStorageInfo();
    } catch (err) {
      log.catch(err, void 0, {
        F: __dxlog_file,
        L: 138,
        S: void 0,
        C: (f, a) => f(...a)
      });
      retry = true;
    }
    try {
      snapshotInfo2 = await devtoolsHost.getSnapshots();
    } catch (err) {
      log.catch(err, void 0, {
        F: __dxlog_file,
        L: 145,
        S: void 0,
        C: (f, a) => f(...a)
      });
      retry = true;
    }
    try {
      blobsInfo2 = await devtoolsHost.getBlobs();
    } catch (err) {
      log.catch(err, void 0, {
        F: __dxlog_file,
        L: 152,
        S: void 0,
        C: (f, a) => f(...a)
      });
      retry = true;
    }
    setBlobsInfo(blobsInfo2);
    setStorageInfo(storageInfo2);
    setSnapshotInfo(snapshotInfo2);
    setIsRefreshing(false);
    if (retry) {
      setTimeout(refresh, 500);
    }
  };
  useAsyncEffect(refresh, []);
  const items = useMemo(() => getInfoTree(storageInfo ?? {
    type: "",
    originUsage: 0,
    storageUsage: 0,
    usageQuota: 0
  }, feeds, snapshotInfo?.snapshots ?? [], blobsInfo?.blobs ?? []), [
    storageInfo,
    snapshotInfo,
    blobsInfo
  ]);
  useEffect(() => {
    if (!selected) {
      return;
    }
    const build = (items2) => {
      for (const item of items2) {
        if (item.id !== void 0 && item.id === selected.id) {
          setSelected(item);
          return;
        }
        if (item.items) {
          build(item.items);
        }
      }
    };
    build(items);
  }, [
    items
  ]);
  const selectedValue = selected?.value;
  return /* @__PURE__ */ React.createElement(PanelContainer, {
    classNames: "grid grid-cols-2 divide-x divide-separator",
    toolbar: /* @__PURE__ */ React.createElement(Toolbar.Root, null, /* @__PURE__ */ React.createElement(Toolbar.Button, {
      onClick: refresh,
      disabled: isRefreshing
    }, "Refresh"), /* @__PURE__ */ React.createElement("div", {
      className: "grow"
    }), /* @__PURE__ */ React.createElement(DropdownMenu.Root, null, /* @__PURE__ */ React.createElement(DropdownMenu.Trigger, {
      asChild: true
    }, /* @__PURE__ */ React.createElement(Toolbar.Button, null, "Reset Storage")), /* @__PURE__ */ React.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React.createElement(DropdownMenu.Content, {
      side: "top"
    }, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, /* @__PURE__ */ React.createElement(DropdownMenu.Item, {
      onClick: async () => {
        await services?.SystemService?.reset();
        location.reload();
      }
    }, "Confirm Reset Storage?")), /* @__PURE__ */ React.createElement(DropdownMenu.Arrow, null)))))
  }, /* @__PURE__ */ React.createElement(DataTree, {
    items,
    onSelect: setSelected
  }), selectedValue && /* @__PURE__ */ React.createElement("div", {
    className: "flex flex-col divide-y divide-separator grow overflow-auto scrollbar-thin"
  }, selectedValue.kind === "blob" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", {
    className: "p-1"
  }, "Downloaded ", formatPercent(calculateBlobProgress(selectedValue.blob))), /* @__PURE__ */ React.createElement(Bitbar, {
    value: selectedValue.blob.bitfield ?? new Uint8Array(),
    length: Math.ceil(selectedValue.blob.length / selectedValue.blob.chunkSize),
    className: "m-2"
  }), /* @__PURE__ */ React.createElement(JsonView, {
    data: selectedValue.blob
  })), selectedValue.kind === "feed" && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Bitbar, {
    value: selectedValue.feed.downloaded ?? new Uint8Array(),
    length: Math.ceil(selectedValue.feed.length ?? 0),
    className: "m-2"
  }), /* @__PURE__ */ React.createElement(JsonView, {
    data: selectedValue.feed
  }))));
};
var calculateBlobProgress = (blob) => {
  if (blob.state === BlobMeta.State.FULLY_PRESENT) {
    return 1;
  }
  return BitField.count(blob.bitfield ?? new Uint8Array(), 0, (blob.bitfield?.length ?? 0) * 8) / Math.ceil(blob.length / blob.chunkSize);
};
var formatPercent = (ratio) => (ratio * 100).toFixed(0) + "%";
var TreeItemText = ({ primary, secondary }) => /* @__PURE__ */ React.createElement("div", {
  className: "flex gap-2 overflow-hidden whitespace-nowrap"
}, /* @__PURE__ */ React.createElement("span", {
  className: "font-mono"
}, primary), /* @__PURE__ */ React.createElement("span", {
  className: "text-neutral-400"
}, secondary));
var DataTree = ({ items = [], onSelect }) => {
  return /* @__PURE__ */ React.createElement(Tree.Root, {
    classNames: "p-2"
  }, /* @__PURE__ */ React.createElement(DataItems, {
    items,
    onSelect
  }));
};
var DataItems = ({ items = [], onSelect }) => {
  return /* @__PURE__ */ React.createElement(React.Fragment, null, items.map((item) => {
    const { id, iconName, Element, items: items2 } = item;
    return /* @__PURE__ */ React.createElement(TreeItem.Root, {
      key: id,
      collapsible: !!items2?.length,
      open: true
    }, /* @__PURE__ */ React.createElement("div", {
      role: "none",
      className: "flex grow items-center gap-2 font-mono",
      onClick: () => onSelect(item)
    }, /* @__PURE__ */ React.createElement(Icon, {
      icon: iconName
    }), Element), /* @__PURE__ */ React.createElement(TreeItem.Body, {
      className: "pis-4"
    }, /* @__PURE__ */ React.createElement(Tree.Branch, null, items2 && /* @__PURE__ */ React.createElement(DataItems, {
      items: items2,
      onSelect
    }))));
  }));
};

// src/panels/client/StoragePanel/index.ts
var StoragePanel_default = StoragePanel;
export {
  StoragePanel_default as default
};
//# sourceMappingURL=StoragePanel-DXPNVOM4.mjs.map
