import { cn, WidgetProblemBadge, WidgetChart } from './chunk-HRROD36H.mjs';
export { AGG_OPTIONS, AddCalcDropdown, AddFilterDropdown, CLAUSE_META, CONTENT_SOURCES, ClausePopover, ComposerRegistry, CustomSlotPopover, DEFAULT_DIAGNOSTICS_DEBOUNCE_MS, DefaultFrontmatterWidget, GROUPBY_OPTIONS, GoalRings, METRIC_OPTIONS, MULTI_VALUE_TYPES, ProposedMetricBadge, QueryBlockView, QueryValue, ROLLUP_OPTIONS, RowsResultsChrome, RowsTable, SERIES_COLORS, SOURCE_OPTIONS, SUGGESTION_BINDINGS, StackedBar, TIME_OPTIONS, TokenSlotPill, TopList, UNIT_OPTIONS, WHERE_AGGREGATORS, WHERE_METRICS, WHERE_OPERATORS, WidgetChart, WidgetProblemBadge, WqlBars, WqlComposer, WqlDiagnosticsStrip, WqlEmptyState, WqlQueryInspectorModal, WqlTable, WqlTimeseries, ZoneDistribution, activeCursorSection, addRuntimePanel, blockContentId, blockTypesFromBlocks, catalogIdsFromBlocks, clauseToWql, clauseValue, clausesToWql, cn, compactNumber, composerRegistry, configureOverlayActions, cursorFocusExtension, dateRangeSlot, defaultClauses, defaultMetricsClauses, diagnoseClauses, dispatchAddRuntimePanel, dispatchExpandRuntimePanel, dispatchGutterHighlights, dispatchRemoveRuntimePanel, editingWidgetsField, editorPreset, editorTheme, embedPreviewDecorations, expandRuntimePanel, extractBlockQueries, fenceCompletion, findQueryBlockSection, findWorkoutErrors, forceSectionParse, formatTimestamp, frontmatterPreview, frontmatterPreviewField, getClauseMeta, getCursorFocusState, getOverlayActions, getSuggestionBinding, gutterHighlightsField, gutterUnified, handleFenceAutoWrap, hashCode, inlineButtonDecoration, invalidateSuggestions, lineIdsExtension, linkOpen, loadSuggestions, markdownSyntaxHiding, markdownTablePreview, mergeSeries, mergeTagSuggestions, navigationFacet, patchBlockQuery, pivotClauses, previewDecorations, queryBlockPreview, readSessionRpe, removeRuntimePanel, runtimePanelField, saveBlockQuerySource, sectionAtPos, sectionField, sectionGeometry, sessionQueryInsert, sessionQueryWql, setGutterHighlights, setMetricClause, setSuggestionBinding, smartIncrement, sourcePlane, summarizeAggregate, summarizeFind, tagsFromStaticBlocks, toggleWidgetEdit, tooltipTimestamp, urlAtPos, useChartShape, useComposerSlots, useSuggestions, useWqlStageCounts, widgetBlockPreview, wodAutoWrap, wodAutocompletion, wodEditorKeymap, wodLinter, wodOverlayPanel, workoutLintSource, wqlToClauses, wrapInTimeFence } from './chunk-HRROD36H.mjs';
import { jsxs, jsx } from 'react/jsx-runtime';
import { useMemo, useState, useEffect, useCallback } from 'react';
import { Edit3 } from 'lucide-react';
import { defaultTokenValues, substituteTokens, parseQuery, isDashboardWidgetType, unknownWidgetTypeMessage, unknownTokensMessage, isFindQuery, resolveWidgetType } from '@bitcobblers/wod-wiki-wql';

// src/contracts/dedupe.ts
var CODEMIRROR_SINGLETON_DEPS = [
  "@codemirror/autocomplete",
  "@codemirror/commands",
  "@codemirror/lang-markdown",
  "@codemirror/language",
  "@codemirror/lint",
  "@codemirror/search",
  "@codemirror/state",
  "@codemirror/theme-one-dark",
  "@codemirror/view",
  "@lezer/common",
  "@lezer/highlight",
  "@lezer/lr",
  "@lezer/markdown"
];
function WidgetFrame({ title, question, query, span, children }) {
  return /* @__PURE__ */ jsxs(
    "div",
    {
      className: cn(
        "bg-card border border-border rounded-lg p-4 flex flex-col min-h-0",
        span
      ),
      children: [
        /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-3 mb-1", children: [
          /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground min-w-0", children: title }),
          /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground italic text-right max-w-[60%] line-clamp-2 shrink-0", children: question })
        ] }),
        /* @__PURE__ */ jsx("div", { className: "font-mono text-[11px] text-primary/90 bg-background/60 rounded px-2 py-1 mb-3 whitespace-pre-wrap break-all", children: query }),
        /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children })
      ]
    }
  );
}
function DashboardTokenControls({ tokens, values, onChange }) {
  if (tokens.length === 0) return null;
  const readOnly = !onChange;
  return /* @__PURE__ */ jsx(
    "div",
    {
      "data-testid": "dashboard-token-controls",
      className: "flex flex-wrap items-center gap-x-5 gap-y-2 mb-4",
      children: tokens.map((token) => {
        const current = values[token.name] ?? token.values[0];
        return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
          /* @__PURE__ */ jsxs("span", { className: "font-mono text-[11px] text-muted-foreground", children: [
            "$",
            token.name
          ] }),
          token.isList ? /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-1 rounded-lg border border-border bg-card p-1", children: token.values.map((option) => /* @__PURE__ */ jsx(
            "button",
            {
              type: "button",
              disabled: readOnly,
              onClick: () => onChange?.(token.name, option),
              className: cn(
                "text-xs px-2.5 py-1 rounded-md transition-colors",
                option === current ? "bg-primary text-primary-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted",
                readOnly && "cursor-default"
              ),
              children: option
            },
            option
          )) }) : readOnly ? /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground font-mono", children: current }) : /* @__PURE__ */ jsx(
            "input",
            {
              type: "text",
              value: current,
              onChange: (e) => onChange(token.name, e.target.value),
              className: "w-20 rounded-lg border border-border bg-card px-2 py-1 text-xs font-mono text-foreground focus:outline-none focus:border-primary"
            }
          )
        ] }, token.name);
      })
    }
  );
}
function spanClass(widget) {
  const resolved = resolveWidgetType(widget.type);
  if (resolved === "value") return "col-span-1";
  if (resolved === "table") return "col-span-1 md:col-span-2";
  return "col-span-1 md:col-span-2 lg:col-span-4";
}
function DashboardView({
  document,
  executor,
  onEnsureRollupFacts,
  tokenValues,
  onTokenChange,
  onEditQuery,
  rangeStart,
  rangeEnd,
  preferredUnit
}) {
  const values = useMemo(
    () => ({ ...defaultTokenValues(document.tokens), ...tokenValues }),
    [document.tokens, tokenValues]
  );
  const resolved = useMemo(
    () => document.widgets.map((widget) => {
      const { query, missing } = substituteTokens(widget.query, values);
      return { widget, query, missing };
    }),
    [document.widgets, values]
  );
  const [runs, setRuns] = useState({});
  const [loading, setLoading] = useState(true);
  const resolvedKey = useMemo(
    () => resolved.map((r) => `${r.widget.key}:${r.query}`).join("|"),
    [resolved]
  );
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    async function load() {
      const next = {};
      const runnable = [];
      for (const { widget, query, missing } of resolved) {
        if (widget.type !== "" && !isDashboardWidgetType(widget.type)) {
          next[widget.key] = { error: unknownWidgetTypeMessage(widget.type) };
          continue;
        }
        if (missing.length > 0) {
          next[widget.key] = { error: unknownTokensMessage(missing) };
          continue;
        }
        const parsed = parseQuery(query);
        if (parsed.error) {
          next[widget.key] = { error: parsed.error };
          continue;
        }
        if (isFindQuery(parsed)) {
          next[widget.key] = { error: "find: queries render inline in notes, not as dashboard widgets" };
          continue;
        }
        runnable.push({ widget, query });
      }
      if (!executor) {
        if (!cancelled) {
          setRuns(next);
          setLoading(false);
        }
        return;
      }
      const consumesRollups = runnable.some((r) => r.query.includes("calc."));
      if (consumesRollups && onEnsureRollupFacts) {
        await onEnsureRollupFacts().catch(() => void 0);
      }
      await Promise.all(
        runnable.map(async ({ widget, query }) => {
          try {
            next[widget.key] = {
              result: await executor.runQuery(query, { rangeStart, rangeEnd, preferredUnit })
            };
          } catch (err) {
            next[widget.key] = { error: err instanceof Error ? err.message : String(err) };
          }
        })
      );
      if (!cancelled) {
        setRuns(next);
        setLoading(false);
      }
    }
    void load();
    return () => {
      cancelled = true;
    };
  }, [resolvedKey, executor, onEnsureRollupFacts, rangeStart, rangeEnd, preferredUnit]);
  return /* @__PURE__ */ jsxs("div", { "data-testid": "dashboard-view", children: [
    /* @__PURE__ */ jsx(DashboardTokenControls, { tokens: document.tokens, values, onChange: onTokenChange }),
    loading && /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground mb-3", children: "Loading widgets\u2026" }),
    /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4", children: resolved.map(({ widget, query }) => {
      const run = runs[widget.key];
      return /* @__PURE__ */ jsx(
        WidgetFrame,
        {
          title: widget.title ?? "",
          question: widget.question ?? "",
          query,
          span: spanClass(widget),
          children: /* @__PURE__ */ jsxs("div", { className: "relative group/card h-full min-h-[160px]", children: [
            onEditQuery && /* @__PURE__ */ jsx(
              "button",
              {
                type: "button",
                onClick: () => onEditQuery(widget),
                title: "Edit widget query",
                "aria-label": `Edit query for ${widget.title ?? "widget"}`,
                className: "absolute top-0 right-0 p-1 rounded bg-muted/80 text-muted-foreground hover:text-foreground opacity-0 group-hover/card:opacity-100 transition-opacity z-10",
                children: /* @__PURE__ */ jsx(Edit3, { className: "w-3.5 h-3.5" })
              }
            ),
            run?.error ? /* @__PURE__ */ jsx(WidgetProblemBadge, { message: run.error }) : /* @__PURE__ */ jsx(
              WidgetChart,
              {
                type: widget.type,
                result: run?.result,
                label: widget.title,
                unit: preferredUnit,
                params: widget.params
              }
            )
          ] })
        },
        widget.key
      );
    }) })
  ] });
}
function RangeSelector({ weeks: weeksProp, onWeeksChange, className }) {
  const [internalWeeks, setInternalWeeks] = useState(16);
  const currentWeeks = weeksProp ?? internalWeeks;
  const handleSelect = (w) => {
    if (onWeeksChange) {
      onWeeksChange(w);
    } else {
      setInternalWeeks(w);
    }
  };
  return /* @__PURE__ */ jsx("div", { className: cn("inline-flex items-center gap-1 rounded-lg border border-border bg-card p-1", className), children: [4, 8, 16].map((w) => /* @__PURE__ */ jsxs(
    "button",
    {
      type: "button",
      onClick: () => handleSelect(w),
      className: cn(
        "text-xs px-2.5 py-1 rounded-md transition-colors",
        currentWeeks === w ? "bg-primary text-primary-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted"
      ),
      children: [
        w,
        "w"
      ]
    },
    w
  )) });
}
var DAY = 864e5;
var WEEK = 7 * DAY;
function useAnalyticsQueries(queries, weeks, executor, refreshKey = 0, preferredUnit, onEnsureRollupFacts) {
  const [results, setResults] = useState({});
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    let cancelled = false;
    async function load() {
      if (!executor) {
        if (!cancelled) setLoading(false);
        return;
      }
      const now = Date.now();
      const rangeStart = now - weeks * WEEK;
      const consumesRollups = queries.some((q) => q.query.includes("calc."));
      try {
        if (consumesRollups && onEnsureRollupFacts) {
          await onEnsureRollupFacts().catch(() => void 0);
        }
        const settled = await Promise.all(
          queries.map(async (q) => [
            q.key,
            await executor.runQuery(q.query, { rangeStart, rangeEnd: now, preferredUnit })
          ])
        );
        if (!cancelled) setResults(Object.fromEntries(settled));
      } finally {
        if (!cancelled) setLoading(false);
      }
    }
    void load();
    return () => {
      cancelled = true;
    };
  }, [queries, weeks, executor, refreshKey, preferredUnit, onEnsureRollupFacts]);
  return { results, loading };
}
var ANALYTICS_UNIT_STORAGE_KEY = "wod.analytics.unit";
var DEFAULT_ANALYTICS_UNIT = "kg";
var VALID_UNITS = ["kg", "lb"];
var memoryStore = /* @__PURE__ */ new Map();
var inMemoryStorage = {
  getItem: (key) => memoryStore.get(key) ?? null,
  setItem: (key, value) => {
    memoryStore.set(key, value);
  },
  removeItem: (key) => {
    memoryStore.delete(key);
  }
};
var unitListeners = /* @__PURE__ */ new Set();
function notifyUnitChange() {
  for (const l of unitListeners) l();
}
function resolveStorage(custom) {
  return custom ?? inMemoryStorage;
}
function readStoredUnit(storage) {
  const store = resolveStorage(storage);
  const stored = store.getItem(ANALYTICS_UNIT_STORAGE_KEY);
  return VALID_UNITS.includes(stored ?? "") ? stored : DEFAULT_ANALYTICS_UNIT;
}
function useAnalyticsUnitPreference(customStorage) {
  const storage = resolveStorage(customStorage);
  const [unit, setUnitState] = useState(() => readStoredUnit(storage));
  useEffect(() => {
    setUnitState(readStoredUnit(storage));
    const sync = () => setUnitState(readStoredUnit(storage));
    unitListeners.add(sync);
    return () => {
      unitListeners.delete(sync);
    };
  }, [storage]);
  const setUnit = useCallback(
    (next) => {
      storage.setItem(ANALYTICS_UNIT_STORAGE_KEY, next);
      setUnitState(next);
      notifyUnitChange();
    },
    [storage]
  );
  const toggleUnit = useCallback(() => {
    const next = unit === "kg" ? "lb" : "kg";
    setUnit(next);
  }, [unit, setUnit]);
  return { unit, setUnit, toggleUnit };
}
function getEffectiveAnalyticsUnit(query, preferredUnit) {
  const parsed = parseQuery(query);
  const displayUnit = "displayUnit" in parsed ? parsed.displayUnit : void 0;
  if (displayUnit === "lb" || displayUnit === "kg") {
    return { unit: displayUnit, forced: true };
  }
  return { unit: preferredUnit, forced: false };
}
function getDashboardEffectiveUnit(queries, preferredUnit) {
  const units = queries.map((q) => {
    const parsed = parseQuery(q.query);
    return "displayUnit" in parsed ? parsed.displayUnit : void 0;
  }).filter((u) => u === "kg" || u === "lb");
  const unique = Array.from(new Set(units));
  if (unique.length === 1) {
    return { unit: unique[0], forced: true };
  }
  return { unit: preferredUnit, forced: false };
}
function AnalyticsUnitPreference({ className, unit: unitProp, forced = false, storage }) {
  const { unit: storedUnit, setUnit } = useAnalyticsUnitPreference(storage);
  const activeUnit = unitProp ?? storedUnit;
  return /* @__PURE__ */ jsxs(
    "div",
    {
      "data-testid": "analytics-unit-preference",
      className: cn("inline-flex items-center gap-1 text-xs", className),
      children: [
        /* @__PURE__ */ jsxs(
          "div",
          {
            className: cn(
              "inline-flex items-center rounded-lg border border-border bg-card p-1",
              forced && "opacity-70"
            ),
            title: forced ? "Unit forced by query directive in this view" : "Display unit preference",
            children: [
              /* @__PURE__ */ jsx(
                "button",
                {
                  type: "button",
                  disabled: forced,
                  onClick: () => setUnit("kg"),
                  className: cn(
                    "px-2.5 py-1 rounded-md transition-colors text-xs font-mono",
                    activeUnit === "kg" ? "bg-primary text-primary-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted",
                    forced && "cursor-default hover:bg-transparent hover:text-muted-foreground"
                  ),
                  children: "kg"
                }
              ),
              /* @__PURE__ */ jsx(
                "button",
                {
                  type: "button",
                  disabled: forced,
                  onClick: () => setUnit("lb"),
                  className: cn(
                    "px-2.5 py-1 rounded-md transition-colors text-xs font-mono",
                    activeUnit === "lb" ? "bg-primary text-primary-foreground font-medium" : "text-muted-foreground hover:text-foreground hover:bg-muted",
                    forced && "cursor-default hover:bg-transparent hover:text-muted-foreground"
                  ),
                  children: "lb"
                }
              )
            ]
          }
        ),
        forced && /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground italic", children: "forced by note" })
      ]
    }
  );
}

// src/version.ts
var VERSION = "0.5.30";
var GIT_SHA = "09e3127";
var BUILD_TIME = "2026-08-18T21:11:33.793Z";
var SEMVER = {
  version: "0.5.30",
  gitSha: "09e3127",
  buildTime: "2026-08-18T21:11:33.793Z"
};

export { ANALYTICS_UNIT_STORAGE_KEY, AnalyticsUnitPreference, BUILD_TIME, CODEMIRROR_SINGLETON_DEPS, DEFAULT_ANALYTICS_UNIT, DashboardTokenControls, DashboardView, GIT_SHA, RangeSelector, SEMVER, VERSION, WidgetFrame, getDashboardEffectiveUnit, getEffectiveAnalyticsUnit, readStoredUnit, useAnalyticsQueries, useAnalyticsUnitPreference };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map