import {
  observeResize
} from "./chunk-6VGP7A7A.js";
import {
  EventHandler
} from "./chunk-SGIVSRN5.js";
import {
  useAsyncEffect_default,
  useMemoObjectDeep_default
} from "./chunk-NMUGRJT3.js";
import {
  StateContextProvider_default,
  createStateContext_default,
  useDeps_default,
  useMemoObjectShallow_default,
  useUpdatingRef_default
} from "./chunk-W3FLVN5D.js";
import "./chunk-JSBRDJBE.js";

// src/js-tools/react-utils/state/history/HistoryContextProvider.tsx
import { useMemo } from "react";

// src/js-tools/react-utils/state/history/contexts.ts
var HistoryContext = createStateContext_default();

// src/js-tools/react-utils/state/history/HistoryContextProvider.tsx
import { jsx } from "react/jsx-runtime";
function HistoryContextProvider({
  children,
  maxAge
}) {
  return useMemo(
    () => /* @__PURE__ */ jsx(
      StateContextProvider_default,
      {
        contexts: [HistoryContext],
        values: [
          {
            history: [],
            future: [],
            maxAge
          }
        ],
        children
      }
    ),
    [children, maxAge]
  );
}
var HistoryContextProvider_default = HistoryContextProvider;

// src/js-tools/react-utils/state/history/useRedoHistory.ts
import { useContext, useMemo as useMemo2 } from "react";
function useRedoHistory() {
  const [{ history, future }, setHistoryContext] = useContext(HistoryContext);
  return useMemo2(
    () => [
      future.length > 0,
      () => {
        if (future.length > 0) {
          const action = future[future.length - 1];
          const reverseAction = action();
          setHistoryContext({
            history: [
              ...history,
              () => {
                reverseAction();
                return action;
              }
            ],
            future: future.slice(0, future.length - 1)
          });
        }
      }
    ],
    [history, future, setHistoryContext]
  );
}
var useRedoHistory_default = useRedoHistory;

// src/js-tools/react-utils/state/history/useResetHistory.ts
import { useCallback, useContext as useContext2 } from "react";
function useResetHistory() {
  const [, setHistoryContext] = useContext2(HistoryContext);
  return useCallback(() => {
    setHistoryContext({
      history: [],
      future: []
    });
  }, [setHistoryContext]);
}
var useResetHistory_default = useResetHistory;

// src/js-tools/react-utils/state/history/useUndoHistory.ts
import { useContext as useContext3, useMemo as useMemo3 } from "react";
function useUndoHistory() {
  const [{ history, future }, setHistoryContext] = useContext3(HistoryContext);
  return useMemo3(
    () => [
      history.length > 0,
      () => {
        if (history.length > 0) {
          const action = history[history.length - 1]();
          setHistoryContext({
            history: history.slice(0, history.length - 1),
            future: [...future, action]
          });
        }
      }
    ],
    [history, future, setHistoryContext]
  );
}
var useUndoHistory_default = useUndoHistory;

// src/js-tools/react-utils/state/history/useWriteHistory.ts
import { useCallback as useCallback2, useContext as useContext4 } from "react";
function useWriteHistory() {
  const [{ history, maxAge }, setHistoryContext] = useContext4(HistoryContext);
  return useCallback2(
    (forwardAction) => {
      const reverseAction = forwardAction();
      setHistoryContext({
        history: [
          ...history.slice(
            Math.max(0, history.length - maxAge + 1),
            history.length
          ),
          () => {
            reverseAction();
            return forwardAction;
          }
        ],
        future: []
      });
    },
    [history, maxAge, setHistoryContext]
  );
}
var useWriteHistory_default = useWriteHistory;

// src/js-tools/react-utils/hooks/useElementResizeEffect.ts
import { useEffect } from "react";
function useElementResizeEffect(effect, divRef, options) {
  const debounce3 = options?.debounce;
  const effectRef = useUpdatingRef_default(effect);
  useEffect(() => {
    const element = divRef.current;
    if (element) {
      let destructor;
      const adjust = () => {
        const el = divRef.current;
        if (el) {
          const rect = el.getBoundingClientRect();
          if (destructor) {
            destructor();
          }
          destructor = effectRef.current(rect.width, rect.height);
        }
      };
      return observeResize(element, adjust, {
        debounce: debounce3 || 66
      });
    }
  }, [divRef, debounce3, effectRef]);
}
var useElementResizeEffect_default = useElementResizeEffect;

// src/js-tools/react-utils/hooks/useCallbackOnce.ts
import { useMemo as useMemo4 } from "react";
function useCallbackOnce(effect, deps = []) {
  const effectRef = useUpdatingRef_default(effect);
  const seq = useDeps_default(deps);
  return useMemo4(() => {
    void seq;
    const calledRef = { current: false };
    return (...args) => {
      if (!calledRef.current) {
        calledRef.current = true;
        return effectRef.current(...args);
      }
    };
  }, [effectRef, seq]);
}
var useCallbackOnce_default = useCallbackOnce;

// src/js-tools/react-utils/components/popup/Popup.tsx
import { useCallback as useCallback3, useContext as useContext5, useMemo as useMemo5 } from "react";

// src/js-tools/react-utils/components/popup/contexts.ts
var PopupContext = createStateContext_default();

// src/js-tools/react-utils/components/popup/Popup.tsx
import { jsx as jsx2 } from "react/jsx-runtime";
function Popup({
  className,
  style
}) {
  style = useMemoObjectDeep_default(style);
  const [{ popup, isPersistent }, setPopupCtx] = useContext5(PopupContext);
  const close = useCallback3(() => {
    if (!isPersistent) {
      setPopupCtx({
        popup: null,
        isPersistent: false
      });
    }
  }, [isPersistent, setPopupCtx]);
  return useMemo5(
    () => /* @__PURE__ */ jsx2(
      "div",
      {
        className,
        style: {
          ...style,
          position: "absolute",
          top: 0,
          left: 0,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          width: "100%",
          height: "100%",
          opacity: popup ? 1 : 0,
          pointerEvents: popup ? "auto" : "none"
        },
        onClick: close,
        children: /* @__PURE__ */ jsx2(
          "div",
          {
            style: {
              display: "contents"
            },
            onClick: (e) => e.stopPropagation(),
            children: popup
          }
        )
      }
    ),
    [className, style, popup, close]
  );
}
var Popup_default = Popup;

// src/js-tools/react-utils/components/popup/PopupContextProvider.tsx
import { useMemo as useMemo6 } from "react";
import { jsx as jsx3 } from "react/jsx-runtime";
function PopupContextProvider({ children }) {
  return useMemo6(
    () => /* @__PURE__ */ jsx3(
      StateContextProvider_default,
      {
        contexts: [PopupContext],
        values: [{ popup: null, isPersistent: false }],
        children
      }
    ),
    [children]
  );
}
var PopupContextProvider_default = PopupContextProvider;

// src/js-tools/react-utils/components/popup/usePopupIsPersistent.ts
import { useContext as useContext6 } from "react";
function usePopupIsPersistent() {
  const [{ isPersistent }] = useContext6(PopupContext);
  return isPersistent;
}
var usePopupIsPersistent_default = usePopupIsPersistent;

// src/js-tools/react-utils/components/popup/useSetPopup.ts
import { useCallback as useCallback4, useContext as useContext7 } from "react";
function useSetPopup() {
  const [, setPopupCtx] = useContext7(PopupContext);
  return useCallback4(
    (popup, isPersistent = false) => {
      setPopupCtx({ popup, isPersistent });
    },
    [setPopupCtx]
  );
}
var useSetPopup_default = useSetPopup;

// src/js-tools/react-utils/state/loading/LoadingContextProvider.tsx
import { useMemo as useMemo7 } from "react";

// src/js-tools/react-utils/state/loading/contexts.ts
var LoadingContext = createStateContext_default();

// src/js-tools/react-utils/state/loading/task-handler.ts
var TaskHandler = class {
  constructor(task) {
    this._handler = Promise.resolve();
    this._cancelled = false;
    this._task = task;
  }
  async start(ctx) {
    this._handler = this.execute(ctx);
    return await this._handler;
  }
  async stop(ctx) {
    this._cancelled = true;
    return await this.cancel(ctx);
  }
  async execute(ctx) {
    await ctx;
    if (!this._cancelled) {
      this._destructor = await this._task();
    }
  }
  async cancel(ctx) {
    await this._handler;
    await ctx;
    if (this._destructor) {
      await this._destructor();
    }
  }
};
var task_handler_default = TaskHandler;

// src/js-tools/react-utils/state/loading/task-manager.ts
var TaskManager = class {
  constructor() {
    this._active = Promise.resolve();
  }
  add(task) {
    const taskContext = new task_handler_default(task);
    this._active = taskContext.start(this._active);
    return () => {
      this._active = taskContext.stop(this._active);
    };
  }
};
var task_manager_default = TaskManager;

// src/js-tools/react-utils/state/loading/LoadingContextProvider.tsx
import { jsx as jsx4 } from "react/jsx-runtime";
function LoadingContextProvider({ children }) {
  const taskManager = useMemo7(() => new task_manager_default(), []);
  return useMemo7(
    () => /* @__PURE__ */ jsx4(
      StateContextProvider_default,
      {
        contexts: [LoadingContext],
        values: [
          {
            isLoading: false,
            progress: 0,
            message: null,
            taskManager
          }
        ],
        children
      }
    ),
    [children, taskManager]
  );
}
var LoadingContextProvider_default = LoadingContextProvider;

// src/js-tools/react-utils/state/loading/useAddLoadingTask.ts
import { useCallback as useCallback5, useContext as useContext8 } from "react";
function useAddLoadingTask() {
  const [{ taskManager }, setLoadingCtx] = useContext8(LoadingContext);
  const startTimer = useCallback5((minimumElapsedTime) => {
    if (!minimumElapsedTime) {
      return () => null;
    } else {
      const start = Date.now();
      return async () => {
        const end = Date.now();
        const elapsed = end - start;
        if (elapsed < minimumElapsedTime) {
          await new Promise((resolve) => {
            setTimeout(() => {
              resolve();
            }, minimumElapsedTime - elapsed);
          });
        }
      };
    }
  }, []);
  return useCallback5(
    (task, { minimumElapsedTime } = {}) => {
      return taskManager.add(async () => {
        setLoadingCtx({
          isLoading: true,
          progress: 0,
          message: null
        });
        const stopTimer = startTimer(minimumElapsedTime || 0);
        let destructor;
        try {
          destructor = await task();
        } catch (e) {
          destructor = void 0;
          console.error(e);
        } finally {
          await stopTimer();
          setLoadingCtx({
            isLoading: false,
            progress: 0
          });
        }
        return destructor;
      });
    },
    [taskManager, startTimer, setLoadingCtx]
  );
}
var useAddLoadingTask_default = useAddLoadingTask;

// src/js-tools/react-utils/state/loading/useLoadingEffect.ts
import { useEffect as useEffect2 } from "react";
function useLoadingEffect(getTask, deps, { minimumElapsedTime } = {}) {
  const loadTask = useAddLoadingTask_default();
  const getTaskRef = useUpdatingRef_default(getTask);
  const userSeq = useDeps_default(deps);
  useEffect2(() => {
    const task = getTaskRef.current();
    if (task) {
      return loadTask(task, { minimumElapsedTime });
    }
  }, [
    minimumElapsedTime,
    loadTask,
    getTaskRef,
    /* effect dep */
    userSeq
  ]);
}
var useLoadingEffect_default = useLoadingEffect;

// src/js-tools/react-utils/state/loading/useLoadingMemo.ts
import { useEffect as useEffect3, useState } from "react";
function useLoadingMemo(getCreator, deps, { minimumElapsedTime } = {}) {
  const loadTask = useAddLoadingTask_default();
  const [state, setState] = useState(null);
  const getCreatorRef = useUpdatingRef_default(getCreator);
  const userSeq = useDeps_default(deps);
  useEffect3(() => {
    const task = async () => {
      setState(await getCreatorRef.current()());
    };
    return loadTask(task, { minimumElapsedTime });
  }, [
    minimumElapsedTime,
    loadTask,
    getCreatorRef,
    /* effect dep */
    userSeq
  ]);
  return state;
}
var useLoadingMemo_default = useLoadingMemo;

// src/js-tools/react-utils/state/loading/useLoadingStatus.ts
import { useCallback as useCallback6, useContext as useContext9 } from "react";
function useLoadingStatus() {
  const [{ progress, isLoading, message }, setLoadingCtx] = useContext9(LoadingContext);
  const setLoadingStatus = useCallback6(
    (status) => {
      setLoadingCtx((prevState) => ({
        ...prevState,
        ...status
      }));
    },
    [setLoadingCtx]
  );
  return [{ isLoading, progress, message }, setLoadingStatus];
}
var useLoadingStatus_default = useLoadingStatus;

// src/js-tools/react-utils/components/dropdown/Dropdown.tsx
import { debounce } from "lodash-es";
import {
  useCallback as useCallback7,
  useContext as useContext10,
  useEffect as useEffect4,
  useId,
  useMemo as useMemo8,
  useRef
} from "react";

// src/js-tools/react-utils/components/dropdown/contexts.ts
var DropdownGroupContext = createStateContext_default();
var DropdownContext = createStateContext_default();

// src/js-tools/react-utils/components/dropdown/Dropdown.tsx
import { jsx as jsx5 } from "react/jsx-runtime";
var initialValue = {
  dropdownId: "",
  expanded: false,
  labelDiv: null,
  clientSize: {
    width: 0,
    height: 0
  }
};
function _Dropdown({
  children,
  autoExpand = false
}) {
  const [{ hasGroup }, setDropdownGroupCtx] = useContext10(DropdownGroupContext);
  const [, setDropdownCtx] = useContext10(DropdownContext);
  const dropdownId = useId();
  const divRef = useRef(null);
  useEffect4(() => {
    setDropdownCtx({
      dropdownId
    });
    return () => {
      setDropdownCtx({
        dropdownId: ""
      });
    };
  }, [dropdownId, setDropdownCtx]);
  useEffect4(() => {
    function onClick2(e) {
      const div = divRef.current;
      if (div && e.target) {
        if (!div.contains(e.target)) {
          setDropdownCtx({
            expanded: false
          });
        }
      }
    }
    if (!hasGroup) {
      document.addEventListener("click", onClick2, { capture: true });
    }
    const onResize = debounce(() => {
      const div = divRef.current;
      if (div) {
        setDropdownCtx({
          clientSize: {
            width: window.document.documentElement.clientWidth,
            height: window.document.documentElement.clientHeight
          }
        });
      }
    }, 100);
    window.addEventListener("resize", onResize);
    onResize();
    setDropdownCtx({
      labelDiv: divRef.current
    });
    return () => {
      if (!hasGroup) {
        document.removeEventListener("click", onClick2, {
          capture: true
        });
      }
      window.removeEventListener("resize", onResize);
      setDropdownCtx({
        labelDiv: null
      });
    };
  }, [divRef, hasGroup, setDropdownCtx]);
  const onClick = useCallback7(() => {
    if (hasGroup) {
      setDropdownGroupCtx(({ expanded }) => ({
        expanded: !expanded
      }));
    } else {
      setDropdownCtx(({ expanded }) => ({
        expanded: !expanded
      }));
    }
  }, [hasGroup, setDropdownCtx, setDropdownGroupCtx]);
  const onMouseEnter = useCallback7(() => {
    if (hasGroup) {
      setDropdownGroupCtx({
        focusedId: dropdownId
      });
    }
    if (autoExpand) {
      if (hasGroup) {
        setDropdownGroupCtx({
          expanded: true
        });
      } else {
        setDropdownCtx({
          expanded: true
        });
      }
    }
  }, [autoExpand, hasGroup, dropdownId, setDropdownCtx, setDropdownGroupCtx]);
  const onMouseLeave = useCallback7(() => {
    if (autoExpand) {
      if (hasGroup) {
        setDropdownGroupCtx({
          expanded: false
        });
      } else {
        setDropdownCtx({
          expanded: false
        });
      }
    }
  }, [autoExpand, hasGroup, setDropdownCtx, setDropdownGroupCtx]);
  return useMemo8(
    () => /* @__PURE__ */ jsx5("div", { className: "relative", ref: divRef, children: /* @__PURE__ */ jsx5(
      "div",
      {
        className: "contents",
        onClick,
        onMouseEnter,
        onMouseLeave,
        children
      }
    ) }),
    [children, divRef, onClick, onMouseEnter, onMouseLeave]
  );
}
function DropdownWithContext(props) {
  props = useMemoObjectShallow_default(props);
  return /* @__PURE__ */ jsx5(StateContextProvider_default, { contexts: [DropdownContext], values: [initialValue], children: /* @__PURE__ */ jsx5(_Dropdown, { ...props }) });
}
var Dropdown_default = DropdownWithContext;

// src/js-tools/react-utils/components/dropdown/DropdownExpand.tsx
import { useContext as useContext11, useMemo as useMemo9 } from "react";
import { jsx as jsx6 } from "react/jsx-runtime";
function DropdownExpand({
  children,
  persistOnClick = false,
  expandPos = "bottom-left",
  minClientMargin = "16px",
  className = ""
}) {
  const [{ expanded: groupExpanded, focusedId, hasGroup }] = useContext11(DropdownGroupContext);
  const [{ expanded: singleExpanded, labelDiv, clientSize, dropdownId }] = useContext11(DropdownContext);
  const expanded = useMemo9(
    () => hasGroup ? groupExpanded && dropdownId === focusedId : singleExpanded,
    [hasGroup, groupExpanded, dropdownId, focusedId, singleExpanded]
  );
  const positionStyles = useMemo9(() => {
    switch (expandPos) {
      case "bottom-left":
        return {
          top: "100%",
          left: "0px"
        };
      case "bottom-right":
        return {
          top: "100%",
          right: "0px"
        };
      case "right":
        return {
          top: "0px",
          left: "100%"
        };
      case "left":
        return {
          top: "0px",
          right: "100%"
        };
    }
  }, [expandPos]);
  const maxHeight = useMemo9(() => {
    if (labelDiv && expanded) {
      const rect = labelDiv.getBoundingClientRect();
      switch (expandPos) {
        case "bottom-left":
        case "bottom-right":
          return `calc(${clientSize.height - rect.bottom}px - ${minClientMargin})`;
        case "right":
        case "left":
          return `calc(${clientSize.height - rect.top}px - ${minClientMargin})`;
      }
    }
  }, [expandPos, minClientMargin, labelDiv, clientSize, expanded]);
  const maxWidth = useMemo9(() => {
    if (labelDiv && expanded) {
      const rect = labelDiv.getBoundingClientRect();
      switch (expandPos) {
        case "bottom-left":
          return `calc(${clientSize.width - rect.left}px - ${minClientMargin})`;
        case "bottom-right":
          return `calc(${rect.right}px - ${minClientMargin})`;
        case "right":
          return `calc(${clientSize.width - rect.right}px - ${minClientMargin})`;
        case "left":
          return `calc(${rect.left}px - ${minClientMargin})`;
      }
    }
  }, [expandPos, minClientMargin, labelDiv, clientSize, expanded]);
  return useMemo9(
    () => /* @__PURE__ */ jsx6(
      "div",
      {
        className,
        style: {
          maxWidth,
          maxHeight,
          position: "absolute",
          minWidth: "100%",
          ...!expanded && { display: "none" },
          ...positionStyles
        },
        "aria-hidden": !expanded,
        onClick: (e) => {
          if (persistOnClick) {
            e.stopPropagation();
          }
        },
        children
      }
    ),
    [
      children,
      persistOnClick,
      className,
      positionStyles,
      maxHeight,
      maxWidth,
      expanded
    ]
  );
}
var DropdownExpand_default = DropdownExpand;

// src/js-tools/react-utils/components/dropdown/DropdownGroup.tsx
import { useContext as useContext12, useEffect as useEffect5, useMemo as useMemo10, useRef as useRef2 } from "react";
import { jsx as jsx7 } from "react/jsx-runtime";
var initialValue2 = { focusedId: "", expanded: false, hasGroup: true };
function _DropdownGroup({ children }) {
  const [, setDropdownGroupCtx] = useContext12(DropdownGroupContext);
  const divRef = useRef2(null);
  useEffect5(() => {
    function onClick(e) {
      const div = divRef.current;
      if (div && e.target) {
        if (!div.contains(e.target)) {
          setDropdownGroupCtx({
            expanded: false
          });
        }
      }
    }
    document.addEventListener("click", onClick, { capture: true });
    return () => {
      document.removeEventListener("click", onClick, {
        capture: true
      });
    };
  }, [divRef, setDropdownGroupCtx]);
  return useMemo10(
    () => /* @__PURE__ */ jsx7("div", { className: "contents", ref: divRef, children }),
    [children, divRef]
  );
}
function DropdownGroupWithContext(props) {
  props = useMemoObjectShallow_default(props);
  return /* @__PURE__ */ jsx7(StateContextProvider_default, { contexts: [DropdownGroupContext], values: [initialValue2], children: /* @__PURE__ */ jsx7(_DropdownGroup, { ...props }) });
}
var DropdownGroup_default = DropdownGroupWithContext;

// src/js-tools/react-utils/hooks/useDrag.ts
import throttle from "lodash-es/throttle";
import { useEffect as useEffect6, useState as useState2 } from "react";
function useDrag(elemRef, effect, options = {}) {
  const { minWait } = options;
  const [isDragging, setIsDragging] = useState2(false);
  const [dragStart, setDragStart] = useState2(null);
  const [dragEnd, setDragEnd] = useState2(null);
  const effectRef = useUpdatingRef_default(effect);
  useEffect6(() => {
    const element = elemRef.current;
    if (!element) return;
    let dragStartCoords = null;
    let rect = null;
    const clampCoords = (e) => [
      Math.max(Math.min(e.clientX - rect.left, rect.width), 0),
      Math.max(Math.min(e.clientY - rect.top, rect.height), 0)
    ];
    const onDragMove = throttle((e) => {
      if (dragStartCoords && rect) {
        setDragEnd(clampCoords(e));
      }
    }, minWait || 0);
    const onDragEnd = (e) => {
      if (dragStartCoords && rect) {
        const coords = clampCoords(e);
        if (dragStartCoords[0] !== coords[0] || dragStartCoords[1] !== coords[1]) {
          effectRef.current(dragStartCoords, coords);
        }
      }
      setIsDragging(false);
      setDragStart(null);
      setDragEnd(null);
      dragStartCoords = null;
      rect = null;
      window.removeEventListener("mousemove", onDragMove);
      window.removeEventListener("mouseup", onDragEnd);
      onDragMove.cancel();
    };
    const onDragStart = (e) => {
      rect = element.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      dragStartCoords = [x, y];
      setIsDragging(true);
      setDragStart([x, y]);
      setDragEnd([x, y]);
      window.addEventListener("mousemove", onDragMove);
      window.addEventListener("mouseup", onDragEnd);
    };
    element.addEventListener("mousedown", onDragStart);
    return () => {
      element.removeEventListener("mousedown", onDragStart);
      window.removeEventListener("mousemove", onDragMove);
      window.removeEventListener("mouseup", onDragEnd);
      onDragMove.cancel();
      if (dragStartCoords) {
        setIsDragging(false);
        setDragStart(null);
        setDragEnd(null);
      }
    };
  }, [elemRef, minWait, effectRef]);
  return [isDragging, dragStart, dragEnd];
}
var useDrag_default = useDrag;

// src/js-tools/react-utils/components/window/Window.tsx
import { useCallback as useCallback8, useEffect as useEffect7, useMemo as useMemo11, useState as useState3 } from "react";

// src/js-tools/react-utils/components/window/contexts.ts
import { createContext } from "react";
var WindowContext = createContext({
  selectedTabId: null,
  setTabId: () => null
});

// src/js-tools/react-utils/components/window/Window.tsx
import { jsx as jsx8 } from "react/jsx-runtime";
function Window({
  activeTabId = null,
  onTabChanged,
  children
}) {
  const [selectedTabId, setSelectedTabId] = useState3(null);
  useEffect7(() => {
    setSelectedTabId(activeTabId ?? null);
  }, [activeTabId, setSelectedTabId]);
  const setTabId = useCallback8(
    (i) => {
      setSelectedTabId(i);
      if (onTabChanged) {
        onTabChanged(i);
      }
    },
    [onTabChanged]
  );
  return useMemo11(
    () => /* @__PURE__ */ jsx8(WindowContext.Provider, { value: { selectedTabId, setTabId }, children }),
    [children, selectedTabId, setTabId]
  );
}
var Window_default = Window;

// src/js-tools/react-utils/components/window/WindowTab.tsx
import { useContext as useContext13, useMemo as useMemo12 } from "react";
import { jsx as jsx9 } from "react/jsx-runtime";
function Tab({ tabId, children }) {
  const { setTabId } = useContext13(WindowContext);
  return useMemo12(
    () => /* @__PURE__ */ jsx9(
      "div",
      {
        style: {
          display: "contents"
        },
        onClick: () => setTabId(tabId),
        children
      }
    ),
    [tabId, children, setTabId]
  );
}
var WindowTab_default = Tab;

// src/js-tools/react-utils/components/window/WindowTabContent.tsx
import { useContext as useContext14, useMemo as useMemo13 } from "react";
import { jsx as jsx10 } from "react/jsx-runtime";
function WindowTabContent({
  forTab,
  children
}) {
  const { selectedTabId } = useContext14(WindowContext);
  return useMemo13(
    () => /* @__PURE__ */ jsx10(
      "div",
      {
        style: {
          display: forTab === selectedTabId ? "contents" : "none"
        },
        children
      }
    ),
    [forTab, selectedTabId, children]
  );
}
var WindowTabContent_default = WindowTabContent;

// src/js-tools/react-utils/hooks/useSearchParams.ts
import isFunction from "lodash-es/isFunction";
import { useCallback as useCallback9, useEffect as useEffect8, useMemo as useMemo14, useRef as useRef3, useState as useState4 } from "react";
var listenerCount = 0;
var intervalId = null;
var eventHandler = new EventHandler();
function createLocationChecker() {
  let prevHref = "";
  return () => {
    const { href } = window.location;
    if (href !== prevHref) {
      prevHref = href;
      eventHandler.dispatchEvent("locationChanged");
    }
  };
}
function listen() {
  ++listenerCount;
  if (!intervalId) {
    intervalId = setInterval(createLocationChecker(), 100);
  }
  return () => {
    --listenerCount;
    if (listenerCount === 0 && intervalId) {
      clearInterval(intervalId);
      intervalId = null;
    }
  };
}
function useSearchParams() {
  const searchParamsRef = useRef3(new URLSearchParams());
  const [searchParams, _setSearchParams] = useState4(
    () => searchParamsRef.current = new URLSearchParams(
      window.location.search.slice(1)
    )
  );
  useEffect8(() => {
    const updateSearch = () => {
      searchParamsRef.current = new URLSearchParams(
        window.location.search.slice(1)
      );
      _setSearchParams(searchParamsRef.current);
    };
    const removeListener = eventHandler.addListener(
      "locationChanged",
      updateSearch
    );
    const unlisten = listen();
    return () => {
      unlisten();
      removeListener();
    };
  }, [_setSearchParams]);
  const setSearchParams = useCallback9(
    (params, {
      removeParams
    } = {}) => {
      const { origin, pathname } = window.location;
      const oldParams = searchParamsRef.current;
      const newParams = new URLSearchParams(
        isFunction(params) ? params(oldParams) : params
      );
      oldParams.forEach((value, key) => {
        if (!newParams.has(key)) {
          newParams.set(key, value);
        }
      });
      if (removeParams) {
        for (const paramName of removeParams) {
          newParams.delete(paramName);
        }
      }
      window.history.replaceState(
        null,
        "",
        `${origin}${pathname}?${newParams.toString()}`
      );
    },
    [searchParamsRef]
  );
  return useMemo14(
    () => [searchParams, setSearchParams],
    [searchParams, setSearchParams]
  );
}
var useSearchParams_default = useSearchParams;

// src/js-tools/react-utils/hooks/useClientSize.ts
import debounce2 from "lodash-es/debounce";
import { useEffect as useEffect9, useState as useState5 } from "react";
function useClientSize(w = 0, h = 0) {
  const [width, setWidth] = useState5(w);
  const [height, setHeight] = useState5(h);
  useEffect9(() => {
    const onResize = debounce2(function() {
      setWidth(window.innerWidth);
      setHeight(window.innerHeight);
    }, 33);
    onResize();
    window.addEventListener("resize", onResize);
    return () => {
      onResize.cancel();
      window.removeEventListener("resize", onResize);
    };
  }, []);
  return [width, height];
}
var useClientSize_default = useClientSize;

// src/js-tools/react-utils/components/virtual-list/VirtualList.tsx
import { useEffect as useEffect10, useState as useState6 } from "react";
import { Fragment, jsx as jsx11, jsxs } from "react/jsx-runtime";
function VirtualList({
  containerRef,
  itemCount,
  itemHeight,
  overscan = 10,
  scrollTo,
  renderItem
}) {
  const [scrollTop, setScrollTop] = useState6(0);
  const [containerHeight, setContainerHeight] = useState6(0);
  useElementResizeEffect_default(
    (_w, h) => setContainerHeight(h),
    containerRef
  );
  useEffect10(() => {
    const el = containerRef.current;
    if (!el) return;
    const onScroll = () => setScrollTop(el.scrollTop);
    el.addEventListener("scroll", onScroll, { passive: true });
    setScrollTop(el.scrollTop);
    return () => el.removeEventListener("scroll", onScroll);
  }, [containerRef]);
  useEffect10(() => {
    if (scrollTo == null) return;
    containerRef.current?.scrollTo({ top: scrollTo * itemHeight, behavior: "smooth" });
  }, [scrollTo, itemHeight, containerRef]);
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan);
  const endIndex = Math.min(itemCount, Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan);
  const topPad = startIndex * itemHeight;
  const bottomPad = (itemCount - endIndex) * itemHeight;
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    /* @__PURE__ */ jsx11("div", { style: { height: topPad } }),
    Array.from({ length: endIndex - startIndex }, (_, i) => renderItem(startIndex + i)),
    /* @__PURE__ */ jsx11("div", { style: { height: bottomPad } })
  ] });
}
var VirtualList_default = VirtualList;

// src/js-tools/react-utils/components/chart/Chart.tsx
import {
  useCallback as useCallback10,
  useContext as useContext15,
  useMemo as useMemo15,
  useRef as useRef4,
  useState as useState7
} from "react";

// src/js-tools/react-utils/components/chart/contexts.ts
import { createContext as createContext2 } from "react";
var ChartContext = createStateContext_default();
var ChartDataContext = createContext2({
  data: []
});
var ChartLayoutContext = createContext2({
  xAxis: {
    min: 0,
    max: 100,
    useLog: false
  },
  yAxis: {
    min: 0,
    max: 100,
    useLog: false
  }
});
var ChartAxisContext = createContext2({
  dim: "x",
  tickSize: 0,
  ticks: []
});
var ChartContainerContext = createContext2(null);

// src/js-tools/react-utils/components/chart/Chart.tsx
import { jsx as jsx12, jsxs as jsxs2 } from "react/jsx-runtime";
function Chart_({
  children,
  data,
  layout: {
    margin,
    marginLeft,
    marginRight,
    marginTop,
    marginBottom,
    xAxis = {},
    yAxis = {}
  } = {}
}) {
  marginLeft = marginLeft || margin || 0;
  marginRight = marginRight || margin || 0;
  marginTop = marginTop || margin || 0;
  marginBottom = marginBottom || margin || 0;
  const { min: xMin = 0, max: xMax = 100, useLog: xUseLog = false } = xAxis;
  const { min: yMin = 0, max: yMax = 100, useLog: yUseLog = false } = yAxis;
  const [{ width, height, eventHandler: eventHandler2 }, setChartCtx] = useContext15(ChartContext);
  const svgRef = useRef4(null);
  const [containerEl, setContainerEl] = useState7(null);
  useElementResizeEffect_default(
    (width2, height2) => {
      setChartCtx({
        width: width2,
        height: height2
      });
    },
    svgRef
  );
  const computeCoord = useCallback10(
    (e) => {
      const rect = e.target;
      const domRect = rect.getBoundingClientRect();
      const x = (e.clientX - domRect.left) / domRect.width;
      const y = 1 - (e.clientY - domRect.top) / domRect.height;
      if (x >= 0 && x <= 1 && y >= 0 && y <= 1) {
        const xData = xUseLog ? Math.expm1(x * Math.log1p(xMax - xMin)) + xMin : x * (xMax - xMin) + xMin;
        const yData = yUseLog ? Math.expm1(y * Math.log1p(yMax - yMin)) + yMin : y * (yMax - yMin) + yMin;
        return [xData, yData];
      } else {
        return null;
      }
    },
    [xMin, xMax, yMin, yMax, xUseLog, yUseLog]
  );
  const onMouseDown = useCallback10(
    (e) => {
      const coord = computeCoord(e);
      if (coord) {
        eventHandler2.dispatchEvent("mousedown", {
          x: coord[0],
          y: coord[1]
        });
      }
    },
    [eventHandler2, computeCoord]
  );
  const onMouseMove = useCallback10(
    (e) => {
      const coord = computeCoord(e);
      if (coord) {
        eventHandler2.dispatchEvent("mousemove", {
          x: coord[0],
          y: coord[1]
        });
      }
    },
    [eventHandler2, computeCoord]
  );
  const onMouseUp = useCallback10(() => {
    eventHandler2.dispatchEvent("mouseup");
  }, [eventHandler2]);
  const onMouseLeave = useCallback10(() => {
    eventHandler2.dispatchEvent("mouseleave");
  }, [eventHandler2]);
  return useMemo15(
    () => /* @__PURE__ */ jsx12(
      "div",
      {
        ref: setContainerEl,
        style: {
          position: "relative",
          width: "100%",
          height: "100%",
          overflow: "hidden",
          paddingLeft: marginLeft,
          paddingRight: marginRight,
          paddingTop: marginTop,
          paddingBottom: marginBottom
        },
        children: /* @__PURE__ */ jsx12(ChartContainerContext.Provider, { value: containerEl, children: /* @__PURE__ */ jsx12(
          ChartLayoutContext.Provider,
          {
            value: {
              xAxis: {
                min: xMin,
                max: xMax,
                useLog: xUseLog
              },
              yAxis: {
                min: yMin,
                max: yMax,
                useLog: yUseLog
              }
            },
            children: /* @__PURE__ */ jsx12(
              ChartDataContext.Provider,
              {
                value: {
                  data
                },
                children: /* @__PURE__ */ jsxs2(
                  "svg",
                  {
                    "aria-hidden": width === 0 || height === 0,
                    ref: svgRef,
                    width: "100%",
                    height: "100%",
                    xmlns: "http://www.w3.org/2000/svg",
                    overflow: "visible",
                    style: {
                      opacity: width === 0 || height === 0 ? 0 : 1
                    },
                    children: [
                      /* @__PURE__ */ jsx12(
                        "rect",
                        {
                          x: 0,
                          y: 0,
                          width: "100%",
                          height: "100%",
                          fill: "transparent",
                          strokeWidth: 0,
                          onMouseDown,
                          onMouseMove,
                          onMouseLeave,
                          onMouseUp
                        }
                      ),
                      /* @__PURE__ */ jsx12("g", { style: { pointerEvents: "none" }, children })
                    ]
                  }
                )
              }
            )
          }
        ) })
      }
    ),
    [
      children,
      data,
      marginLeft,
      marginRight,
      marginTop,
      marginBottom,
      xMin,
      xMax,
      yMin,
      yMax,
      xUseLog,
      yUseLog,
      svgRef,
      width,
      height,
      containerEl,
      onMouseDown,
      onMouseMove,
      onMouseLeave,
      onMouseUp
    ]
  );
}
function Chart(props) {
  const memoProps = useMemoObjectShallow_default(props);
  return useMemo15(
    () => /* @__PURE__ */ jsx12(
      StateContextProvider_default,
      {
        contexts: [ChartContext],
        values: [
          {
            width: 0,
            height: 0,
            eventHandler: new EventHandler()
          }
        ],
        children: /* @__PURE__ */ jsx12(Chart_, { ...memoProps })
      }
    ),
    [memoProps]
  );
}
var Chart_default = Chart;

// src/js-tools/react-utils/components/chart/Axis/Axis.tsx
import { useContext as useContext18, useMemo as useMemo16 } from "react";

// src/js-tools/react-utils/components/chart/hooks/useChartProjectX.ts
import { useCallback as useCallback11, useContext as useContext16 } from "react";
function useChartProjectX() {
  const [{ width }] = useContext16(ChartContext);
  const {
    xAxis: { min, max, useLog }
  } = useContext16(ChartLayoutContext);
  return useCallback11(
    (x) => {
      if (max === min) return width * 0.5;
      return width * (useLog ? Math.log1p(x - min) / Math.log1p(max - min) : (x - min) / (max - min));
    },
    [width, min, max, useLog]
  );
}
var useChartProjectX_default = useChartProjectX;

// src/js-tools/react-utils/components/chart/hooks/useChartProjectY.ts
import { useCallback as useCallback12, useContext as useContext17 } from "react";
function useChartProjectY() {
  const [{ height }] = useContext17(ChartContext);
  const {
    yAxis: { min, max, useLog }
  } = useContext17(ChartLayoutContext);
  return useCallback12(
    (y) => {
      if (max === min) return height * 0.5;
      return height * (1 - (useLog ? Math.log1p(y - min) / Math.log1p(max - min) : (y - min) / (max - min)));
    },
    [height, min, max, useLog]
  );
}
var useChartProjectY_default = useChartProjectY;

// src/js-tools/react-utils/components/chart/Axis/Axis.tsx
import { jsx as jsx13, jsxs as jsxs3 } from "react/jsx-runtime";
function Axis({
  dim,
  interval,
  tickSize,
  className,
  children,
  stroke,
  strokeWidth
}) {
  const [{ width, height }] = useContext18(ChartContext);
  const {
    xAxis: { min: xMin, max: xMax, useLog: xUseLog },
    yAxis: { min: yMin, max: yMax, useLog: yUseLog }
  } = useContext18(ChartLayoutContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const [x1, y1, x2, y2] = useMemo16(() => {
    switch (dim) {
      case "x":
        return [0, height, width, height];
      case "y":
        return [0, height, 0, 0];
    }
  }, [dim, width, height]);
  const ticks = useMemo16(() => {
    switch (dim) {
      case "x": {
        const valRange = xMax - xMin;
        const values = xUseLog && valRange > 0 ? (() => {
          const result = [xMin];
          const logMax = Math.ceil(Math.log10(valRange));
          for (let i = Math.floor(Math.log10(1)); i <= logMax; i++) {
            const v = Math.pow(10, i) + xMin;
            if (v > xMin && v < xMax) result.push(v);
          }
          result.push(xMax);
          return result;
        })() : [
          xMin,
          ...Array(Math.ceil(valRange / interval) - 1).fill(0).map((_, i) => (i + 1) * interval + xMin),
          xMax
        ];
        return values.map((v) => ({
          x: projectX(v),
          y: projectY(yMin),
          value: v
        }));
      }
      case "y": {
        const valRange = yMax - yMin;
        const values = yUseLog && valRange > 0 ? (() => {
          const result = [yMin];
          const logMax = Math.ceil(Math.log10(valRange));
          for (let i = Math.floor(Math.log10(1)); i <= logMax; i++) {
            const v = Math.pow(10, i) + yMin;
            if (v > yMin && v < yMax) result.push(v);
          }
          result.push(yMax);
          return result;
        })() : [
          yMin,
          ...Array(Math.ceil(valRange / interval) - 1).fill(0).map((_, i) => (i + 1) * interval + yMin),
          yMax
        ];
        return values.map((v) => ({
          x: projectX(xMin),
          y: projectY(v),
          value: v
        }));
      }
    }
  }, [
    dim,
    interval,
    xMin,
    xMax,
    yMin,
    yMax,
    xUseLog,
    yUseLog,
    projectX,
    projectY
  ]);
  const [xTickStartOffset, yTickStartOffset, xTickEndOffset, yTickEndOffset] = useMemo16(() => {
    switch (dim) {
      case "x":
        return [0, -tickSize / 2, 0, tickSize / 2];
      case "y":
        return [-tickSize / 2, 0, tickSize / 2, 0];
    }
  }, [dim, tickSize]);
  const axis = useMemo16(
    () => /* @__PURE__ */ jsx13("g", { children: /* @__PURE__ */ jsx13(
      "path",
      {
        className,
        d: `M ${x1} ${y1} L ${x2} ${y2} ${ticks.map((t) => `M ${t.x + xTickStartOffset} ${t.y + yTickStartOffset} L ${t.x + xTickEndOffset} ${t.y + yTickEndOffset}`).join(" ")} `,
        fill: "transparent",
        stroke,
        strokeWidth
      }
    ) }),
    [
      className,
      stroke,
      strokeWidth,
      x1,
      y1,
      x2,
      y2,
      ticks,
      xTickStartOffset,
      yTickStartOffset,
      xTickEndOffset,
      yTickEndOffset
    ]
  );
  return useMemo16(
    () => /* @__PURE__ */ jsx13(
      ChartAxisContext.Provider,
      {
        value: {
          dim,
          tickSize,
          ticks
        },
        children: /* @__PURE__ */ jsxs3("g", { children: [
          axis,
          children
        ] })
      }
    ),
    [children, dim, tickSize, axis, ticks]
  );
}
var Axis_default = Axis;

// src/js-tools/react-utils/components/chart/Axis/AxisTitle.tsx
import { useContext as useContext19, useId as useId2, useMemo as useMemo17 } from "react";
import { jsx as jsx14, jsxs as jsxs4 } from "react/jsx-runtime";
function AxisTitle({
  title,
  className
}) {
  const [{ width, height }] = useContext19(ChartContext);
  const { dim, tickSize } = useContext19(ChartAxisContext);
  const uid = useId2();
  const [x, y] = useMemo17(() => {
    switch (dim) {
      case "x":
        return [width / 2, height + tickSize / 2 + 36];
      case "y":
        return [-tickSize / 2 - 36, height / 2];
    }
  }, [dim, width, height, tickSize]);
  const transform = dim === "y" ? `rotate(-90 ${x} ${y})` : void 0;
  return useMemo17(
    () => /* @__PURE__ */ jsxs4("g", { children: [
      /* @__PURE__ */ jsx14("defs", { children: /* @__PURE__ */ jsx14(
        "text",
        {
          id: `chart-axis-title-${uid}`,
          textAnchor: "middle",
          dominantBaseline: "central",
          className,
          children: title
        }
      ) }),
      /* @__PURE__ */ jsx14(
        "use",
        {
          xlinkHref: `#chart-axis-title-${uid}`,
          x,
          y,
          transform
        }
      )
    ] }),
    [title, className, x, y, transform, uid]
  );
}
var AxisTitle_default = AxisTitle;

// src/js-tools/react-utils/components/chart/Axis/GridLines.tsx
import { useContext as useContext20, useMemo as useMemo18 } from "react";
import { jsx as jsx15 } from "react/jsx-runtime";
function GridLines({
  className,
  stroke,
  strokeWidth
}) {
  const [{ width, height }] = useContext20(ChartContext);
  const { ticks, dim, tickSize } = useContext20(ChartAxisContext);
  const [
    xGridLineStartOffset,
    yGridLineStartOffset,
    xGridLineEndOffset,
    yGridLineEndOffset
  ] = useMemo18(() => {
    switch (dim) {
      case "x":
        return [0, -tickSize / 2, 0, -height];
      case "y":
        return [tickSize / 2, 0, width, 0];
    }
  }, [dim, tickSize, width, height]);
  return useMemo18(
    () => /* @__PURE__ */ jsx15(
      "path",
      {
        className,
        fill: "transparent",
        stroke,
        strokeWidth,
        d: ticks.slice(1).map(
          (t) => `M ${t.x + xGridLineStartOffset} ${t.y + yGridLineStartOffset} L ${t.x + xGridLineEndOffset} ${t.y + yGridLineEndOffset}`
        ).join(" ")
      }
    ),
    [
      className,
      stroke,
      strokeWidth,
      xGridLineStartOffset,
      xGridLineEndOffset,
      yGridLineStartOffset,
      yGridLineEndOffset,
      ticks
    ]
  );
}
var GridLines_default = GridLines;

// src/js-tools/react-utils/components/chart/Axis/TickLabels.tsx
import { useContext as useContext21, useMemo as useMemo19 } from "react";
import { jsx as jsx16 } from "react/jsx-runtime";
function TickLabels({
  className,
  overrideLabels
}) {
  const { dim, tickSize, ticks } = useContext21(ChartAxisContext);
  const [xLabelOffset, yLabelOffset] = useMemo19(() => {
    switch (dim) {
      case "x":
        return [0, 5 + tickSize / 2];
      case "y":
        return [-5 - tickSize / 2, 0];
    }
  }, [dim, tickSize]);
  const labelAnchor = useMemo19(() => {
    switch (dim) {
      case "x":
        return "middle";
      case "y":
        return "end";
    }
  }, [dim]);
  const labelDominantBaseLine = useMemo19(() => {
    switch (dim) {
      case "x":
        return "hanging";
      case "y":
        return "middle";
    }
  }, [dim]);
  return useMemo19(
    () => ticks.map((t, i) => /* @__PURE__ */ jsx16(
      "text",
      {
        className,
        x: t.x + xLabelOffset,
        y: t.y + yLabelOffset,
        textAnchor: labelAnchor,
        dominantBaseline: labelDominantBaseLine,
        style: {
          userSelect: "none"
        },
        children: overrideLabels && overrideLabels(
          t.value,
          t.x + xLabelOffset,
          t.y + yLabelOffset
        ) || t.value
      },
      i
    )),
    [
      className,
      ticks,
      xLabelOffset,
      yLabelOffset,
      labelAnchor,
      labelDominantBaseLine,
      overrideLabels
    ]
  );
}
var TickLabels_default = TickLabels;

// src/js-tools/react-utils/components/chart/Line.tsx
import { useContext as useContext22, useMemo as useMemo20 } from "react";
import { jsx as jsx17 } from "react/jsx-runtime";
function Line({
  datasetName,
  className,
  strokeWidth
}) {
  const { data } = useContext22(ChartDataContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const dataset = useMemo20(() => {
    for (const d of data) {
      if (d.name === datasetName) {
        return d;
      }
    }
  }, [data, datasetName]);
  const projected = useMemo20(
    () => dataset && {
      color: dataset.color,
      x: dataset.x.map((xi) => projectX(xi)),
      y: dataset.y.map((yi) => projectY(yi))
    },
    [dataset, projectX, projectY]
  );
  return useMemo20(
    () => projected && projected.x.length >= 2 && /* @__PURE__ */ jsx17(
      "path",
      {
        className,
        d: `M ${projected.x[0]} ${projected.y[0]} L ${projected.x.slice(1).map((xi, i) => ` ${xi}, ${projected.y[i + 1]}`).join(" ")}`,
        fill: "transparent",
        stroke: projected.color,
        strokeWidth: strokeWidth || 1
      }
    ),
    [className, strokeWidth, projected]
  );
}
var Line_default = Line;

// src/js-tools/react-utils/components/chart/Bar.tsx
import { useContext as useContext26, useMemo as useMemo22 } from "react";

// src/js-tools/react-utils/components/chart/hooks/useAxisParams.ts
import { useContext as useContext23, useMemo as useMemo21 } from "react";
function useAxisParams(axis) {
  const { xAxis, yAxis } = useContext23(ChartLayoutContext);
  return useMemo21(() => {
    switch (axis) {
      case "x":
        return xAxis;
      case "y":
        return yAxis;
    }
  }, [axis, xAxis, yAxis]);
}
var useAxisParams_default = useAxisParams;

// src/js-tools/react-utils/components/chart/hooks/useChartSnap.ts
import { useCallback as useCallback13, useContext as useContext25, useState as useState8 } from "react";

// src/js-tools/react-utils/components/chart/hooks/useChartEvent.ts
import { useContext as useContext24, useEffect as useEffect11 } from "react";
function useChartEvent(type, listener) {
  const [{ eventHandler: eventHandler2 }] = useContext24(ChartContext);
  useEffect11(() => {
    eventHandler2.addListener(type, listener);
    return () => {
      eventHandler2.removeListener(type, listener);
    };
  }, [type, eventHandler2, listener]);
}
var useChartEvent_default = useChartEvent;

// src/js-tools/react-utils/components/chart/hooks/useChartSnap.ts
function findNearestIndex(xs, target) {
  if (xs.length === 0) return -1;
  let best = 0;
  let bestDist = Math.abs(xs[0] - target);
  for (let i = 1; i < xs.length; i++) {
    const d = Math.abs(xs[i] - target);
    if (d < bestDist) {
      bestDist = d;
      best = i;
    }
  }
  return best;
}
function useChartSnap(anchorDataset) {
  const { data } = useContext25(ChartDataContext);
  const [snap, setSnap] = useState8(null);
  const anchor = anchorDataset ? data.find((d) => d.name === anchorDataset) : data[0];
  const onMove = useCallback13(
    (e) => {
      const v = e.value;
      if (!v || !anchor || anchor.x.length === 0) {
        setSnap(null);
        return;
      }
      const i = findNearestIndex(anchor.x, v.x);
      if (i < 0) {
        setSnap(null);
        return;
      }
      const snappedX = anchor.x[i];
      const perSeriesY = {};
      for (const d of data) {
        const j = findNearestIndex(d.x, snappedX);
        if (j >= 0 && d.x[j] === snappedX) {
          perSeriesY[d.name] = d.y[j];
        }
      }
      setSnap({ x: snappedX, perSeriesY });
    },
    [anchor, data]
  );
  const onLeave = useCallback13(() => {
    setSnap(null);
  }, []);
  useChartEvent_default("mousemove", onMove);
  useChartEvent_default("mouseleave", onLeave);
  return snap;
}
var useChartSnap_default = useChartSnap;

// src/js-tools/react-utils/components/chart/Bar.tsx
import { jsx as jsx18 } from "react/jsx-runtime";
function Bar({
  datasetNames,
  className,
  strokeWidth,
  gap = 0,
  mode = "grouped",
  anchorDataset,
  hoverStroke = "rgba(0,0,0,0.85)",
  hoverStrokeWidth = 1.5,
  dimOpacity = 0.45
}) {
  const [{ width }] = useContext26(ChartContext);
  const { data } = useContext26(ChartDataContext);
  const { min } = useAxisParams_default("y");
  const snap = useChartSnap_default(anchorDataset ?? datasetNames[0]);
  const snapX = snap?.x;
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const datasets = useMemo22(
    () => datasetNames.map((datasetName) => {
      for (const d of data) {
        if (d.name === datasetName) {
          return d;
        }
      }
    }).filter((d) => d !== void 0),
    [data, datasetNames]
  );
  const projected = useMemo22(
    () => datasets && datasets.map((dataset) => ({
      color: dataset.color,
      x: dataset.x.map((xi) => projectX(xi)),
      y: dataset.y.map((yi) => projectY(yi))
    })),
    [datasets, projectX, projectY]
  );
  const y0 = useMemo22(() => projectY(min), [min, projectY]);
  const slots = useMemo22(
    () => projected?.map(
      (p) => p.x.map((x, j) => {
        const left = j === 0 ? 0 : (p.x[j - 1] + x) / 2;
        const right = j === p.x.length - 1 ? width : (x + p.x[j + 1]) / 2;
        return { left, right };
      })
    ),
    [projected, width]
  );
  const stackedTopY = useMemo22(() => {
    if (mode !== "stacked") return null;
    const cumulative = [];
    return datasets.map(
      (d, i) => d.y.map((yi, j) => {
        const prev = i === 0 ? 0 : cumulative[j];
        const next = prev + yi;
        cumulative[j] = next;
        return projectY(next + min);
      })
    );
  }, [datasets, projectY, min, mode]);
  return useMemo22(() => {
    if (!projected) return null;
    if (mode === "stacked" && stackedTopY && slots) {
      return projected.map((p, i) => {
        const dataset = datasets[i];
        return /* @__PURE__ */ jsx18("g", { children: p.x.map((_x, j) => {
          const { left, right } = slots[i][j];
          const slot = right - left - gap;
          const topY = stackedTopY[i][j];
          const baseY = i === 0 ? y0 : stackedTopY[i - 1][j];
          const isHovered = snapX !== void 0 && dataset.x[j] === snapX;
          const isDimmed = snapX !== void 0 && !isHovered;
          return /* @__PURE__ */ jsx18(
            "rect",
            {
              className,
              x: left + gap / 2,
              y: topY,
              width: slot,
              height: baseY - topY,
              fill: p.color,
              fillOpacity: isDimmed ? dimOpacity : 1,
              stroke: isHovered ? hoverStroke : void 0,
              strokeWidth: isHovered ? hoverStrokeWidth : strokeWidth || 1
            },
            j
          );
        }) }, i);
      });
    }
    if (!slots) return null;
    return projected.map((p, i) => {
      const dataset = datasets[i];
      return /* @__PURE__ */ jsx18("g", { children: p.x.map((_x, j) => {
        const { left, right } = slots[i][j];
        const groupSlot = (right - left - gap) / projected.length;
        const isHovered = snapX !== void 0 && dataset.x[j] === snapX;
        const isDimmed = snapX !== void 0 && !isHovered;
        return /* @__PURE__ */ jsx18(
          "rect",
          {
            className,
            x: left + gap / 2 + i * groupSlot,
            y: p.y[j],
            width: groupSlot,
            height: y0 - p.y[j],
            fill: p.color,
            fillOpacity: isDimmed ? dimOpacity : 1,
            stroke: isHovered ? hoverStroke : void 0,
            strokeWidth: isHovered ? hoverStrokeWidth : strokeWidth || 1
          },
          j
        );
      }) }, i);
    });
  }, [
    className,
    strokeWidth,
    projected,
    datasets,
    slots,
    gap,
    y0,
    mode,
    stackedTopY,
    snapX,
    hoverStroke,
    hoverStrokeWidth,
    dimOpacity
  ]);
}
var Bar_default = Bar;

// src/js-tools/react-utils/components/chart/Legend.tsx
import { useContext as useContext27, useMemo as useMemo24 } from "react";

// src/js-tools/react-utils/components/chart/annotations/BoxAnnotation.tsx
import { useMemo as useMemo23, useRef as useRef5, useState as useState9 } from "react";
import { jsx as jsx19, jsxs as jsxs5 } from "react/jsx-runtime";
function BoxAnnotation({
  x,
  y,
  padding,
  xAnchor,
  yAnchor,
  className,
  fill,
  stroke,
  strokeWidth,
  children
}) {
  const [{ width, height }, setDims] = useState9({
    width: null,
    height: null
  });
  const svgRef = useRef5(null);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  useElementResizeEffect_default(
    (width2, height2) => {
      setDims({
        width: width2,
        height: height2
      });
      return () => {
        setDims({
          width: null,
          height: null
        });
      };
    },
    svgRef,
    {
      debounce: 10
    }
  );
  const [ox, oy] = useMemo23(() => {
    return [
      projectX(x) + padding - ((width || 0) + padding * 2) * xAnchor,
      projectY(y) + padding - ((height || 0) + padding * 2) * yAnchor
    ];
  }, [xAnchor, yAnchor, x, y, padding, width, height, projectX, projectY]);
  return useMemo23(
    () => /* @__PURE__ */ jsxs5("g", { children: [
      width !== null && height !== null && /* @__PURE__ */ jsx19(
        "rect",
        {
          className,
          fill,
          stroke,
          strokeWidth,
          x: ox - padding,
          y: oy - padding,
          width: width + padding * 2,
          height: height + padding * 2
        }
      ),
      /* @__PURE__ */ jsx19(
        "svg",
        {
          ref: svgRef,
          x: ox,
          y: oy,
          overflow: "visible",
          "aria-hidden": width === null || height === null,
          style: {
            opacity: width === null || height === null ? 0 : 1
          },
          children
        }
      )
    ] }),
    [
      ox,
      oy,
      padding,
      className,
      fill,
      stroke,
      strokeWidth,
      children,
      width,
      height
    ]
  );
}
var BoxAnnotation_default = BoxAnnotation;

// src/js-tools/react-utils/components/chart/Legend.tsx
import { jsx as jsx20, jsxs as jsxs6 } from "react/jsx-runtime";
function Legend({
  lineHeight,
  padding,
  xAnchor = 1,
  yAnchor = 0,
  fill = "white",
  stroke = "black",
  strokeWidth = 1
}) {
  const { data } = useContext27(ChartDataContext);
  const { max: yMax } = useAxisParams_default("y");
  const { max: xMax } = useAxisParams_default("x");
  return useMemo24(
    () => /* @__PURE__ */ jsx20(
      BoxAnnotation_default,
      {
        x: xMax,
        y: yMax,
        padding,
        xAnchor,
        yAnchor,
        fill,
        stroke,
        strokeWidth,
        children: data.map(({ name, color }, i) => /* @__PURE__ */ jsxs6("g", { style: { pointerEvents: "none" }, children: [
          /* @__PURE__ */ jsx20(
            "rect",
            {
              x: 0,
              y: (i + 0.5) * lineHeight - 6,
              width: 30,
              height: 10,
              fill: color,
              strokeWidth: 2
            }
          ),
          /* @__PURE__ */ jsx20(
            "text",
            {
              textAnchor: "start",
              dominantBaseline: "middle",
              x: 35,
              y: (i + 0.5) * lineHeight,
              style: {
                userSelect: "none"
              },
              children: name
            }
          )
        ] }, i))
      }
    ),
    [
      fill,
      stroke,
      strokeWidth,
      padding,
      lineHeight,
      xAnchor,
      yAnchor,
      data,
      xMax,
      yMax
    ]
  );
}
var Legend_default = Legend;

// src/js-tools/react-utils/components/chart/annotations/VLineAnnotation.tsx
import { useContext as useContext28, useMemo as useMemo25 } from "react";
import { jsx as jsx21 } from "react/jsx-runtime";
function VLineAnnotation({
  className,
  x,
  stroke,
  strokeWidth
}) {
  const {
    yAxis: { min, max }
  } = useContext28(ChartLayoutContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const px = useMemo25(() => projectX(x), [x, projectX]);
  const [pyMin, pyMax] = useMemo25(
    () => [projectY(min), projectY(max)],
    [min, max, projectY]
  );
  return useMemo25(
    () => /* @__PURE__ */ jsx21(
      "line",
      {
        className,
        x1: px,
        y1: pyMin,
        x2: px,
        y2: pyMax,
        stroke,
        strokeWidth
      }
    ),
    [px, pyMin, pyMax, className, stroke, strokeWidth]
  );
}
var VLineAnnotation_default = VLineAnnotation;

// src/js-tools/react-utils/components/chart/annotations/HLineAnnotation.tsx
import { useContext as useContext29, useMemo as useMemo26 } from "react";
import { jsx as jsx22 } from "react/jsx-runtime";
function HLineAnnotation({
  className,
  y,
  stroke,
  strokeWidth
}) {
  const {
    xAxis: { min, max }
  } = useContext29(ChartLayoutContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const py = useMemo26(() => projectY(y), [y, projectY]);
  const [pxMin, pxMax] = useMemo26(
    () => [projectX(min), projectX(max)],
    [min, max, projectX]
  );
  return useMemo26(
    () => /* @__PURE__ */ jsx22(
      "line",
      {
        className,
        x1: pxMin,
        y1: py,
        x2: pxMax,
        y2: py,
        stroke,
        strokeWidth
      }
    ),
    [py, pxMin, pxMax, className, stroke, strokeWidth]
  );
}
var HLineAnnotation_default = HLineAnnotation;

// src/js-tools/react-utils/components/chart/annotations/CircleAnnotation.tsx
import { useMemo as useMemo27 } from "react";
import { jsx as jsx23 } from "react/jsx-runtime";
function CircleAnnotation({
  cx,
  cy,
  r,
  className,
  stroke,
  strokeWidth,
  fill
}) {
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const px = useMemo27(() => projectX(cx), [cx, projectX]);
  const py = useMemo27(() => projectY(cy), [cy, projectY]);
  return useMemo27(
    () => /* @__PURE__ */ jsx23(
      "circle",
      {
        cx: px,
        cy: py,
        r,
        className,
        stroke,
        strokeWidth,
        fill
      }
    ),
    [px, py, r, className, stroke, strokeWidth, fill]
  );
}
var CircleAnnotation_default = CircleAnnotation;

// src/js-tools/react-utils/components/chart/Area.tsx
import { useContext as useContext30, useMemo as useMemo28 } from "react";
import { jsx as jsx24 } from "react/jsx-runtime";
function Area({
  datasetName,
  baseline,
  fill,
  fillOpacity = 0.3,
  stroke,
  strokeWidth,
  className
}) {
  const { data } = useContext30(ChartDataContext);
  const {
    yAxis: { min: yMin }
  } = useContext30(ChartLayoutContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const dataset = useMemo28(
    () => data.find((d) => d.name === datasetName),
    [data, datasetName]
  );
  const projected = useMemo28(() => {
    if (!dataset) return null;
    const xs = dataset.x.map((xi) => projectX(xi));
    const ys = dataset.y.map((yi) => projectY(yi));
    const baselineY = projectY(baseline ?? yMin);
    return { color: dataset.color, xs, ys, baselineY };
  }, [dataset, projectX, projectY, baseline, yMin]);
  return useMemo28(() => {
    if (!projected || projected.xs.length < 2) return null;
    const { xs, ys, baselineY, color } = projected;
    const top = xs.map((x, i) => `${i === 0 ? "M" : "L"} ${x} ${ys[i]}`).join(" ");
    const d = `${top} L ${xs[xs.length - 1]} ${baselineY} L ${xs[0]} ${baselineY} Z`;
    return /* @__PURE__ */ jsx24(
      "path",
      {
        className,
        d,
        fill: fill ?? color,
        fillOpacity,
        stroke,
        strokeWidth
      }
    );
  }, [projected, className, fill, fillOpacity, stroke, strokeWidth]);
}
var Area_default = Area;

// src/js-tools/react-utils/components/chart/Crosshair.tsx
import { useContext as useContext31, useMemo as useMemo29 } from "react";
import { jsx as jsx25, jsxs as jsxs7 } from "react/jsx-runtime";
function Crosshair({
  anchorDataset,
  className,
  stroke = "black",
  strokeWidth = 1,
  dotRadius = 4
}) {
  const snap = useChartSnap_default(anchorDataset);
  const { data } = useContext31(ChartDataContext);
  const {
    yAxis: { min: yMin, max: yMax }
  } = useContext31(ChartLayoutContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  return useMemo29(() => {
    if (!snap) return null;
    const px = projectX(snap.x);
    const yTop = projectY(yMax);
    const yBottom = projectY(yMin);
    return /* @__PURE__ */ jsxs7("g", { style: { pointerEvents: "none" }, children: [
      /* @__PURE__ */ jsx25(
        "line",
        {
          className,
          x1: px,
          y1: yTop,
          x2: px,
          y2: yBottom,
          stroke,
          strokeWidth
        }
      ),
      data.map((d) => {
        const y = snap.perSeriesY[d.name];
        if (y === void 0) return null;
        return /* @__PURE__ */ jsx25(
          "circle",
          {
            cx: px,
            cy: projectY(y),
            r: dotRadius,
            fill: d.color,
            stroke,
            strokeWidth
          },
          d.name
        );
      })
    ] });
  }, [
    snap,
    data,
    projectX,
    projectY,
    yMin,
    yMax,
    className,
    stroke,
    strokeWidth,
    dotRadius
  ]);
}
var Crosshair_default = Crosshair;

// src/js-tools/react-utils/components/chart/Tooltip.tsx
import { useContext as useContext32, useMemo as useMemo30 } from "react";
import { createPortal } from "react-dom";
import { jsx as jsx26, jsxs as jsxs8 } from "react/jsx-runtime";
function defaultRenderRow(name, _x, y, color) {
  return /* @__PURE__ */ jsxs8(
    "div",
    {
      style: {
        display: "flex",
        alignItems: "center",
        gap: 6,
        fontSize: 12,
        lineHeight: 1.4
      },
      children: [
        /* @__PURE__ */ jsx26(
          "span",
          {
            style: {
              display: "inline-block",
              width: 10,
              height: 10,
              background: color,
              borderRadius: 2,
              flexShrink: 0
            }
          }
        ),
        /* @__PURE__ */ jsxs8("span", { children: [
          name,
          ": ",
          y
        ] })
      ]
    },
    name
  );
}
function Tooltip({
  anchorDataset,
  offsetX = 12,
  offsetY = 0,
  renderRow = defaultRenderRow,
  className,
  style
}) {
  const snap = useChartSnap_default(anchorDataset);
  const container = useContext32(ChartContainerContext);
  const { data } = useContext32(ChartDataContext);
  const [{ height: chartHeight }] = useContext32(ChartContext);
  const projectX = useChartProjectX_default();
  const projectY = useChartProjectY_default();
  const node = useMemo30(() => {
    if (!snap || !container) return null;
    const px = projectX(snap.x);
    const firstName = data.find((d) => snap.perSeriesY[d.name] !== void 0)?.name;
    const py = firstName !== void 0 ? projectY(snap.perSeriesY[firstName]) : chartHeight / 2;
    const containerRect = container.getBoundingClientRect();
    const svg = container.querySelector("svg");
    const svgRect = svg ? svg.getBoundingClientRect() : containerRect;
    const left = svgRect.left - containerRect.left + px + offsetX;
    const top = svgRect.top - containerRect.top + py + offsetY;
    const adjustedStyle = {
      position: "absolute",
      left,
      top,
      pointerEvents: "none",
      background: "white",
      border: "1px solid #d1d5db",
      borderRadius: 4,
      padding: "6px 8px",
      boxShadow: "0 1px 3px rgba(0,0,0,0.1)",
      zIndex: 10,
      ...style
    };
    return createPortal(
      /* @__PURE__ */ jsxs8("div", { className, style: adjustedStyle, children: [
        /* @__PURE__ */ jsxs8("div", { style: { fontSize: 12, fontWeight: 600, marginBottom: 4 }, children: [
          "x: ",
          snap.x
        ] }),
        data.map((d) => {
          const y = snap.perSeriesY[d.name];
          if (y === void 0) return null;
          return renderRow(d.name, snap.x, y, d.color);
        })
      ] }),
      container
    );
  }, [
    snap,
    container,
    data,
    projectX,
    projectY,
    chartHeight,
    offsetX,
    offsetY,
    renderRow,
    className,
    style
  ]);
  return node;
}
var Tooltip_default = Tooltip;
export {
  Chart_default as Chart,
  Area_default as ChartArea,
  Axis_default as ChartAxis,
  AxisTitle_default as ChartAxisTitle,
  Bar_default as ChartBar,
  BoxAnnotation_default as ChartBoxAnnotation,
  CircleAnnotation_default as ChartCircleAnnotation,
  Crosshair_default as ChartCrosshair,
  GridLines_default as ChartGridLines,
  HLineAnnotation_default as ChartHLineAnnotation,
  Legend_default as ChartLegend,
  Line_default as ChartLine,
  TickLabels_default as ChartTickLabels,
  Tooltip_default as ChartTooltip,
  VLineAnnotation_default as ChartVLineAnnotation,
  Dropdown_default as Dropdown,
  DropdownExpand_default as DropdownExpand,
  DropdownGroup_default as DropdownGroup,
  HistoryContextProvider_default as HistoryContextProvider,
  LoadingContextProvider_default as LoadingContextProvider,
  Popup_default as Popup,
  PopupContextProvider_default as PopupContextProvider,
  StateContextProvider_default as StateContextProvider,
  VirtualList_default as VirtualList,
  Window_default as Window,
  WindowTab_default as WindowTab,
  WindowTabContent_default as WindowTabContent,
  createStateContext_default as createStateContext,
  useAddLoadingTask_default as useAddLoadingTask,
  useAsyncEffect_default as useAsyncEffect,
  useCallbackOnce_default as useCallbackOnce,
  useAxisParams_default as useChartAxisParams,
  useChartEvent_default as useChartEvent,
  useChartProjectX_default as useChartProjectX,
  useChartProjectY_default as useChartProjectY,
  useChartSnap_default as useChartSnap,
  useClientSize_default as useClientSize,
  useDeps_default as useDeps,
  useDrag_default as useDrag,
  useElementResizeEffect_default as useElementResizeEffect,
  useLoadingEffect_default as useLoadingEffect,
  useLoadingMemo_default as useLoadingMemo,
  useLoadingStatus_default as useLoadingStatus,
  useMemoObjectDeep_default as useMemoObjectDeep,
  useMemoObjectShallow_default as useMemoObjectShallow,
  usePopupIsPersistent_default as usePopupIsPersistent,
  useRedoHistory_default as useRedoHistory,
  useResetHistory_default as useResetHistory,
  useSearchParams_default as useSearchParams,
  useSetPopup_default as useSetPopup,
  useUndoHistory_default as useUndoHistory,
  useUpdatingRef_default as useUpdatingRef,
  useWriteHistory_default as useWriteHistory
};
