@xenosystem/blocks 0.13.8 → 0.13.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,8 +8,7 @@ import {
8
8
  } from "../chunk-DJJGZ6U4.js";
9
9
  import {
10
10
  ICONS
11
- } from "../chunk-BNALWAAQ.js";
12
- import "../chunk-GTN5EDY3.js";
11
+ } from "../chunk-W4PTX2Y4.js";
13
12
  import {
14
13
  ChevronGlyph,
15
14
  DownloadGlyph,
@@ -199,8 +198,8 @@ function moveCell(from, delta, bounds) {
199
198
  }
200
199
 
201
200
  // src/data/table/numeric.ts
202
- function parseNumeric(text2) {
203
- const trimmed = String(text2).trim();
201
+ function parseNumeric(text3) {
202
+ const trimmed = String(text3).trim();
204
203
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
205
204
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
206
205
  if (!match) return { ok: false, reason: "nan" };
@@ -554,11 +553,11 @@ var TableController = class {
554
553
  return true;
555
554
  }
556
555
  /** Update the in-flight draft. Emits `editPreview` only when `livePreview` is on. */
557
- updateDraft(text2) {
556
+ updateDraft(text3) {
558
557
  if (!this.editing) return;
559
- this.editing = { ...this.editing, draft: text2 };
558
+ this.editing = { ...this.editing, draft: text3 };
560
559
  if (this.livePreview) {
561
- const parsed = this.parseDraft(this.editing.columnId, text2);
560
+ const parsed = this.parseDraft(this.editing.columnId, text3);
562
561
  if (parsed.ok) {
563
562
  this.host.emit("editPreview", this.buildEdit(this.editing, parsed.value));
564
563
  }
@@ -610,12 +609,12 @@ var TableController = class {
610
609
  };
611
610
  }
612
611
  /** Parse a draft according to the column's declared type. */
613
- parseDraft(columnId, text2) {
612
+ parseDraft(columnId, text3) {
614
613
  const column = this.columnById(columnId);
615
- const trimmed = text2.trim();
614
+ const trimmed = text3.trim();
616
615
  if (column?.type === "number" || column?.type === "integer" || column?.type === "duration") {
617
616
  if (trimmed === "") return { ok: true, value: null };
618
- const parsed = parseNumeric(text2);
617
+ const parsed = parseNumeric(text3);
619
618
  if (!parsed.ok) return { ok: false, reason: parsed.reason };
620
619
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOfColumn(column)) };
621
620
  }
@@ -632,7 +631,7 @@ var TableController = class {
632
631
  value: trimmed === "" ? [] : trimmed.split(",").map((part) => part.trim()).filter(Boolean)
633
632
  };
634
633
  }
635
- return { ok: true, value: trimmed === "" ? null : text2 };
634
+ return { ok: true, value: trimmed === "" ? null : text3 };
636
635
  }
637
636
  rowById(rowId) {
638
637
  return this.result?.rows.find((r) => r.id === rowId);
@@ -2913,138 +2912,185 @@ var metricsManifest = {
2913
2912
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
2914
2913
  import {
2915
2914
  Badge as Badge3,
2915
+ Column,
2916
+ Delta,
2916
2917
  EmptyState as EmptyState3,
2917
2918
  ErrorState as ErrorState3,
2918
2919
  ScrollArea as ScrollArea2,
2919
2920
  SegmentedControl as SegmentedControl3,
2920
2921
  Sparkline,
2922
+ Stack,
2921
2923
  StatTile,
2922
2924
  StatusBar as StatusBar3,
2925
+ Text,
2926
+ TileGrid,
2923
2927
  Toolbar as Toolbar3
2924
2928
  } from "@xenosystem/components/chrome";
2925
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
2926
- function MetricsPanelView({
2927
- controller,
2928
- layout = "grid",
2929
- columns = 4,
2930
- sparkline = true,
2931
- showDelta = true,
2932
- emptyHint,
2933
- partialHint = "Some measures have not finished syncing."
2934
- }) {
2935
- const state = useSyncExternalStore3(controller.subscribe, controller.getState, controller.getState);
2936
- const toolbar = state.ranges.length > 0 ? /* @__PURE__ */ jsx3(
2937
- Toolbar3,
2938
- {
2939
- left: /* @__PURE__ */ jsx3(
2940
- SegmentedControl3,
2941
- {
2942
- size: "sm",
2943
- label: "Range",
2944
- options: state.ranges.map((r) => ({ value: r.id, label: r.label })),
2945
- value: state.rangeId ?? state.ranges[0]?.id ?? "",
2946
- onChange: (id) => controller.setRange(id)
2947
- }
2948
- ),
2949
- right: state.partial ? (
2950
- // The honest state: rows are real, measures are still landing.
2951
- /* @__PURE__ */ jsx3(Badge3, { tone: "warning", title: partialHint, children: "partial" })
2952
- ) : void 0
2953
- }
2954
- ) : null;
2955
- if (state.errorMessage) {
2956
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
2957
- toolbar,
2958
- /* @__PURE__ */ jsx3(ErrorState3, { title: "Could not load metrics", message: state.errorMessage })
2959
- ] });
2960
- }
2961
- if (state.metrics.length === 0) {
2962
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
2963
- toolbar,
2964
- /* @__PURE__ */ jsx3(
2965
- EmptyState3,
2966
- {
2967
- title: "No metrics",
2968
- hint: emptyHint ?? "Configure a metric, or wire a result into this panel."
2969
- }
2970
- )
2971
- ] });
2929
+
2930
+ // src/data/metrics/declaration.ts
2931
+ function metricsViewState(c, options = {}) {
2932
+ const s = c.getState();
2933
+ const sparkline = options.sparkline ?? true;
2934
+ const showDelta = options.showDelta ?? true;
2935
+ const partialHint = options.partialHint ?? "Some measures have not finished syncing.";
2936
+ const errored = !!s.errorMessage;
2937
+ const empty = !errored && s.metrics.length === 0;
2938
+ return {
2939
+ hasRanges: s.ranges.length > 0,
2940
+ ranges: s.ranges.map((r) => ({ value: r.id, label: r.label })),
2941
+ rangeId: s.rangeId ?? s.ranges[0]?.id ?? "",
2942
+ partial: s.partial,
2943
+ partialHint,
2944
+ errorMessage: s.errorMessage ?? void 0,
2945
+ errored,
2946
+ empty,
2947
+ emptyHint: options.emptyHint ?? "Configure a metric, or wire a result into this panel.",
2948
+ ready: !errored && !empty,
2949
+ layout: options.layout ?? "grid",
2950
+ columns: options.columns ?? 4,
2951
+ tiles: s.metrics.map((metric) => {
2952
+ const level = metric.level === "critical" ? "error" : metric.level === "warn" ? "warning" : void 0;
2953
+ const hasDelta = showDelta && metric.delta !== void 0;
2954
+ const hasSpark = sparkline && !!metric.spark && metric.spark.length > 1;
2955
+ return {
2956
+ id: metric.spec.id,
2957
+ label: metric.spec.label,
2958
+ title: metric.spec.description,
2959
+ // A partial result shows an em dash, never a zero — a zero is a lie the user cannot detect.
2960
+ value: metric.partial && metric.value === null ? "\u2014" : metric.display,
2961
+ level: String(metric.level),
2962
+ hasLevel: level !== void 0,
2963
+ tone: level ?? "warning",
2964
+ hasDelta,
2965
+ delta: metric.delta ?? 0,
2966
+ deltaTone: metric.deltaTone,
2967
+ partial: metric.partial === true,
2968
+ hasSpark,
2969
+ spark: hasSpark ? metric.spark : [],
2970
+ sparkLabel: `${metric.spec.label} trend`
2971
+ };
2972
+ }),
2973
+ countLabel: `${s.metrics.length} metric${s.metrics.length === 1 ? "" : "s"}`,
2974
+ partialLabel: s.partial ? partialHint : void 0
2975
+ };
2976
+ }
2977
+ function applyMetricsUi(c, intent) {
2978
+ switch (intent.kind) {
2979
+ case "range":
2980
+ c.setRange(intent.rangeId);
2981
+ break;
2982
+ case "activate":
2983
+ c.activate(intent.metricId);
2984
+ break;
2972
2985
  }
2973
- const gridStyle = layout === "grid" ? { display: "grid", gridTemplateColumns: `repeat(${Math.max(1, columns)}, minmax(0, 1fr))`, gap: 6 } : layout === "row" ? { display: "flex", gap: 6, overflowX: "auto" } : { display: "flex", flexDirection: "column", gap: 6 };
2974
- return /* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
2975
- toolbar,
2976
- /* @__PURE__ */ jsx3(ScrollArea2, { children: /* @__PURE__ */ jsx3("div", { style: { ...gridStyle, padding: 6 }, children: state.metrics.map((metric) => /* @__PURE__ */ jsx3(
2977
- Tile,
2978
- {
2979
- metric,
2980
- sparkline,
2981
- showDelta,
2982
- partialHint,
2983
- onActivate: () => controller.activate(metric.spec.id)
2984
- },
2985
- metric.spec.id
2986
- )) }) }),
2987
- /* @__PURE__ */ jsx3(
2988
- StatusBar3,
2989
- {
2990
- left: `${state.metrics.length} metric${state.metrics.length === 1 ? "" : "s"}`,
2991
- right: state.partial ? partialHint : void 0
2992
- }
2993
- )
2994
- ] });
2995
2986
  }
2996
- function Tile({
2997
- metric,
2998
- sparkline,
2999
- showDelta,
3000
- partialHint,
3001
- onActivate
3002
- }) {
3003
- const tone = metric.level === "critical" ? "error" : metric.level === "warn" ? "warning" : void 0;
3004
- return /* @__PURE__ */ jsx3(
3005
- "div",
3006
- {
3007
- role: "button",
3008
- tabIndex: 0,
3009
- onClick: onActivate,
3010
- onKeyDown: (e) => {
3011
- if (e.key === "Enter" || e.key === " ") {
3012
- e.preventDefault();
3013
- onActivate();
2987
+ var metricsViewManifest = {
2988
+ id: metricsManifest.id,
2989
+ version: "1.0.0",
2990
+ title: "Metrics",
2991
+ category: "data",
2992
+ layout: { slot: "center" },
2993
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The MetricsViewModel the runtime derives from the controller on every draw." }],
2994
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind: range | activate, \u2026 } \u2014 applied by the runtime to the controller." }],
2995
+ commands: []
2996
+ };
2997
+ var EMPTY = { hasRanges: false, ranges: [], rangeId: "", partial: false, partialHint: "", errored: false, empty: true, emptyHint: "Configure a metric, or wire a result into this panel.", ready: false, layout: "grid", columns: 4, tiles: [], countLabel: "0 metrics" };
2998
+ var BEHAVIOURS = {
2999
+ "data/metricsPanelView": (inputs) => inputs.view ?? EMPTY
3000
+ };
3001
+ var METRICS_RUNTIME_UNITS = [];
3002
+ var text = (b) => ({ component: "text", props: { text: b } });
3003
+ var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
3004
+ var METRICS_DECLARATION = {
3005
+ format: "xeno.block@1",
3006
+ manifest: metricsViewManifest,
3007
+ behaviour: "data/metricsPanelView",
3008
+ composition: {
3009
+ component: "chrome/Column",
3010
+ children: [
3011
+ {
3012
+ component: "chrome/Toolbar",
3013
+ when: { input: "view.hasRanges" },
3014
+ props: {
3015
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Range" }, options: { input: "view.ranges" }, value: { input: "view.rangeId" } }, on: { onChange: ui("range", { rangeId: { arg: "" } }) } } },
3016
+ right: { node: { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" }, title: { input: "view.partialHint" } }, children: [text({ value: "partial" })] } }
3014
3017
  }
3015
3018
  },
3016
- style: { cursor: "pointer", minWidth: 0 },
3017
- children: /* @__PURE__ */ jsx3(
3018
- StatTile,
3019
- {
3020
- title: metric.spec.description,
3021
- label: metric.spec.label,
3022
- value: metric.partial && metric.value === null ? "\u2014" : metric.display,
3023
- icon: tone ? /* @__PURE__ */ jsx3(Badge3, { tone, children: metric.level }) : void 0,
3024
- sub: /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
3025
- showDelta && metric.delta !== void 0 ? /* @__PURE__ */ jsxs3(
3026
- "span",
3019
+ { component: "chrome/ErrorState", when: { input: "view.errored" }, props: { title: { value: "Could not load metrics" }, message: { input: "view.errorMessage" } } },
3020
+ { component: "chrome/EmptyState", when: { input: "view.empty" }, props: { title: { value: "No metrics" }, hint: { input: "view.emptyHint" } } },
3021
+ {
3022
+ component: "chrome/ScrollArea",
3023
+ when: { input: "view.ready" },
3024
+ children: [
3025
+ {
3026
+ component: "chrome/TileGrid",
3027
+ props: { layout: { input: "view.layout" }, columns: { input: "view.columns" } },
3028
+ children: [
3027
3029
  {
3028
- style: {
3029
- // Tone comes from deltaDirection, NEVER from the sign: a rising error rate is
3030
- // not good news.
3031
- color: metric.deltaTone === "good" ? "var(--xeno-success)" : metric.deltaTone === "bad" ? "var(--xeno-danger)" : void 0
3030
+ component: "chrome/StatTile",
3031
+ each: { input: "view.tiles", key: "id" },
3032
+ props: {
3033
+ title: { item: "title" },
3034
+ label: { item: "label" },
3035
+ value: { item: "value" },
3036
+ icon: { node: { component: "chrome/Badge", when: { item: "hasLevel" }, props: { tone: { item: "tone" } }, children: [text({ item: "level" })] } },
3037
+ sub: {
3038
+ node: {
3039
+ component: "chrome/Stack",
3040
+ props: { direction: { value: "row" }, pad: { value: "none" } },
3041
+ children: [
3042
+ { component: "chrome/Delta", when: { item: "hasDelta" }, props: { value: { item: "delta" }, tone: { item: "deltaTone" } } },
3043
+ { component: "chrome/Text", when: { item: "partial" }, props: { role: { value: "meta" } }, children: [text({ input: "view.partialHint" })] },
3044
+ { component: "chrome/Sparkline", when: { item: "hasSpark" }, props: { data: { item: "spark" }, label: { item: "sparkLabel" } } }
3045
+ ]
3046
+ }
3047
+ }
3032
3048
  },
3033
- children: [
3034
- metric.delta > 0 ? "\u25B2" : "\u25BC",
3035
- " ",
3036
- Math.abs(metric.delta * 100).toFixed(1),
3037
- "%"
3038
- ]
3049
+ on: { onActivate: ui("activate", { metricId: { item: "id" } }) }
3039
3050
  }
3040
- ) : null,
3041
- metric.partial ? /* @__PURE__ */ jsx3("span", { style: { opacity: 0.6 }, children: partialHint }) : null,
3042
- sparkline && metric.spark && metric.spark.length > 1 ? /* @__PURE__ */ jsx3(Sparkline, { data: metric.spark, label: `${metric.spec.label} trend` }) : null
3043
- ] })
3044
- }
3045
- )
3046
- }
3047
- );
3051
+ ]
3052
+ }
3053
+ ]
3054
+ },
3055
+ { component: "chrome/StatusBar", when: { input: "view.ready" }, props: { left: { input: "view.countLabel" }, right: { input: "view.partialLabel" } } }
3056
+ ]
3057
+ }
3058
+ };
3059
+
3060
+ // src/data/metrics/react/MetricsPanelView.tsx
3061
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3062
+ function MetricsPanelView({ controller, ...options }) {
3063
+ useSyncExternalStore3(controller.subscribe, controller.getState, controller.getState);
3064
+ const view = metricsViewState(controller, options);
3065
+ const ui3 = (intent) => applyMetricsUi(controller, intent);
3066
+ return /* @__PURE__ */ jsxs3(Column, { children: [
3067
+ view.hasRanges ? /* @__PURE__ */ jsx3(
3068
+ Toolbar3,
3069
+ {
3070
+ left: /* @__PURE__ */ jsx3(SegmentedControl3, { size: "sm", label: "Range", options: view.ranges, value: view.rangeId, onChange: (rangeId) => ui3({ kind: "range", rangeId }) }),
3071
+ right: view.partial ? /* @__PURE__ */ jsx3(Badge3, { tone: "warning", title: view.partialHint, children: "partial" }) : null
3072
+ }
3073
+ ) : null,
3074
+ view.errored ? /* @__PURE__ */ jsx3(ErrorState3, { title: "Could not load metrics", message: view.errorMessage }) : null,
3075
+ view.empty ? /* @__PURE__ */ jsx3(EmptyState3, { title: "No metrics", hint: view.emptyHint }) : null,
3076
+ view.ready ? /* @__PURE__ */ jsx3(ScrollArea2, { children: /* @__PURE__ */ jsx3(TileGrid, { layout: view.layout, columns: view.columns, children: view.tiles.map((tile) => /* @__PURE__ */ jsx3(
3077
+ StatTile,
3078
+ {
3079
+ title: tile.title,
3080
+ label: tile.label,
3081
+ value: tile.value,
3082
+ icon: tile.hasLevel ? /* @__PURE__ */ jsx3(Badge3, { tone: tile.tone, children: tile.level }) : null,
3083
+ sub: /* @__PURE__ */ jsxs3(Stack, { direction: "row", pad: "none", children: [
3084
+ tile.hasDelta ? /* @__PURE__ */ jsx3(Delta, { value: tile.delta, tone: tile.deltaTone }) : null,
3085
+ tile.partial ? /* @__PURE__ */ jsx3(Text, { role: "meta", children: view.partialHint }) : null,
3086
+ tile.hasSpark ? /* @__PURE__ */ jsx3(Sparkline, { data: tile.spark, label: tile.sparkLabel }) : null
3087
+ ] }),
3088
+ onActivate: () => ui3({ kind: "activate", metricId: tile.id })
3089
+ },
3090
+ tile.id
3091
+ )) }) }) : null,
3092
+ view.ready ? /* @__PURE__ */ jsx3(StatusBar3, { left: view.countLabel, right: view.partialLabel }) : null
3093
+ ] });
3048
3094
  }
3049
3095
 
3050
3096
  // src/data/metrics/panel.ts
@@ -3241,7 +3287,7 @@ function emptyFilterGroup() {
3241
3287
  function offerableFilterOps(column, sourceOps = []) {
3242
3288
  const common = ["eq", "ne", "isEmpty", "isNotEmpty", "in", "notIn"];
3243
3289
  const ordered = ["gt", "gte", "lt", "lte", "between"];
3244
- const text2 = ["contains", "notContains", "startsWith", "endsWith"];
3290
+ const text3 = ["contains", "notContains", "startsWith", "endsWith"];
3245
3291
  const temporal = ["relative"];
3246
3292
  let ops = [...common];
3247
3293
  switch (column?.type) {
@@ -3259,10 +3305,10 @@ function offerableFilterOps(column, sourceOps = []) {
3259
3305
  case "url":
3260
3306
  case "select":
3261
3307
  case "multiselect":
3262
- ops = [...ops, ...text2];
3308
+ ops = [...ops, ...text3];
3263
3309
  break;
3264
3310
  default:
3265
- ops = [...ops, ...text2, ...ordered];
3311
+ ops = [...ops, ...text3, ...ordered];
3266
3312
  }
3267
3313
  if (sourceOps.length === 0) return ops;
3268
3314
  return ops.filter((op) => sourceOps.includes(op));
@@ -3893,7 +3939,7 @@ var fieldsManifest = {
3893
3939
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
3894
3940
  import {
3895
3941
  Badge as Badge4,
3896
- Column,
3942
+ Column as Column2,
3897
3943
  EmptyState as EmptyState4,
3898
3944
  IconButton as IconButton3,
3899
3945
  Note,
@@ -4028,13 +4074,13 @@ var fieldsViewManifest = {
4028
4074
  outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
4029
4075
  commands: []
4030
4076
  };
4031
- var EMPTY = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4032
- var BEHAVIOURS = {
4033
- "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY
4077
+ var EMPTY2 = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4078
+ var BEHAVIOURS2 = {
4079
+ "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY2
4034
4080
  };
4035
4081
  var FIELDS_RUNTIME_UNITS = [];
4036
- var text = (b) => ({ component: "text", props: { text: b } });
4037
- var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4082
+ var text2 = (b) => ({ component: "text", props: { text: b } });
4083
+ var ui2 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4038
4084
  var removeButton = (label, on) => ({
4039
4085
  component: "chrome/IconButton",
4040
4086
  props: { size: { value: "sm" }, icon: { node: { component: "icons/Close", props: { size: { value: 11 } } } }, label },
@@ -4055,14 +4101,14 @@ var FIELDS_DECLARATION = {
4055
4101
  {
4056
4102
  component: "chrome/Toolbar",
4057
4103
  props: {
4058
- left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text({ value: "sampled schema" })] } },
4104
+ left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text2({ value: "sampled schema" })] } },
4059
4105
  right: {
4060
4106
  node: {
4061
4107
  component: "chrome/ToolbarGroup",
4062
4108
  props: { end: { value: true } },
4063
4109
  children: [
4064
- { component: "chrome/TextButton", on: { onClick: ui("autoAssign") }, children: [text({ value: "Auto-assign" })] },
4065
- { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui("apply") }, children: [text({ value: "Apply" })] }
4110
+ { component: "chrome/TextButton", on: { onClick: ui2("autoAssign") }, children: [text2({ value: "Auto-assign" })] },
4111
+ { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui2("apply") }, children: [text2({ value: "Apply" })] }
4066
4112
  ]
4067
4113
  }
4068
4114
  }
@@ -4082,8 +4128,8 @@ var FIELDS_DECLARATION = {
4082
4128
  {
4083
4129
  component: "chrome/Row",
4084
4130
  each: { input: "view.columns", key: "id" },
4085
- props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text({ item: "stat" })] } } },
4086
- on: { onClick: ui("focus", { columnId: { item: "id" } }) }
4131
+ props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text2({ item: "stat" })] } } },
4132
+ on: { onClick: ui2("focus", { columnId: { item: "id" } }) }
4087
4133
  }
4088
4134
  ]
4089
4135
  }
@@ -4092,9 +4138,9 @@ var FIELDS_DECLARATION = {
4092
4138
  {
4093
4139
  component: "chrome/Section",
4094
4140
  each: { input: "view.shelves", key: "id" },
4095
- props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text({ item: "count" })] } } },
4141
+ props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text2({ item: "count" })] } } },
4096
4142
  children: [
4097
- { component: "chrome/Note", when: { item: "empty" }, children: [text({ item: "hint" })] },
4143
+ { component: "chrome/Note", when: { item: "empty" }, children: [text2({ item: "hint" })] },
4098
4144
  {
4099
4145
  component: "chrome/RowList",
4100
4146
  when: { item: "hasEntries" },
@@ -4109,13 +4155,13 @@ var FIELDS_DECLARATION = {
4109
4155
  node: {
4110
4156
  component: "fragment",
4111
4157
  children: [
4112
- { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4113
- { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text({ item: "direction" })] },
4114
- { component: "chrome/Badge", when: { item: "granularity" }, children: [text({ item: "granularity" })] }
4158
+ { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui2("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4159
+ { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui2("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text2({ item: "direction" })] },
4160
+ { component: "chrome/Badge", when: { item: "granularity" }, children: [text2({ item: "granularity" })] }
4115
4161
  ]
4116
4162
  }
4117
4163
  },
4118
- trailing: { node: removeButton({ item: "removeLabel" }, ui("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4164
+ trailing: { node: removeButton({ item: "removeLabel" }, ui2("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4119
4165
  }
4120
4166
  }
4121
4167
  ]
@@ -4127,11 +4173,11 @@ var FIELDS_DECLARATION = {
4127
4173
  props: {
4128
4174
  title: { value: "Filters" },
4129
4175
  flush: { value: true },
4130
- badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text({ input: "view.filterCount" })] } },
4131
- actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui("addFilter") } } }
4176
+ badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text2({ input: "view.filterCount" })] } },
4177
+ actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui2("addFilter") } } }
4132
4178
  },
4133
4179
  children: [
4134
- { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text({ value: "No filters. Everything passes." })] },
4180
+ { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text2({ value: "No filters. Everything passes." })] },
4135
4181
  {
4136
4182
  component: "chrome/RowList",
4137
4183
  when: { input: "view.hasFilters" },
@@ -4141,8 +4187,8 @@ var FIELDS_DECLARATION = {
4141
4187
  each: { input: "view.filters", key: "key" },
4142
4188
  props: {
4143
4189
  label: { item: "label" },
4144
- meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4145
- trailing: { node: removeButton({ item: "removeLabel" }, ui("removeFilter", { index: { item: "index" } })) }
4190
+ meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui2("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4191
+ trailing: { node: removeButton({ item: "removeLabel" }, ui2("removeFilter", { index: { item: "index" } })) }
4146
4192
  }
4147
4193
  }
4148
4194
  ]
@@ -4151,8 +4197,8 @@ var FIELDS_DECLARATION = {
4151
4197
  component: "chrome/Toolbar",
4152
4198
  props: {
4153
4199
  divided: { value: false },
4154
- left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui("combinator", { combinator: { arg: "" } }) } } },
4155
- right: { node: { component: "chrome/TextButton", on: { onClick: ui("addGroup") }, children: [text({ value: "+ group" })] } }
4200
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui2("combinator", { combinator: { arg: "" } }) } } },
4201
+ right: { node: { component: "chrome/TextButton", on: { onClick: ui2("addGroup") }, children: [text2({ value: "+ group" })] } }
4156
4202
  }
4157
4203
  }
4158
4204
  ]
@@ -4174,18 +4220,18 @@ var COMBINATORS2 = [{ value: "and", label: "All" }, { value: "or", label: "Any"
4174
4220
  function FieldsPanelView({ controller, showStats, manualApply }) {
4175
4221
  useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4176
4222
  const view = fieldsViewState(controller, { showStats, manualApply });
4177
- const ui2 = (intent) => applyFieldsUi(controller, intent);
4223
+ const ui3 = (intent) => applyFieldsUi(controller, intent);
4178
4224
  if (view.noColumns) {
4179
4225
  return /* @__PURE__ */ jsx4(EmptyState4, { title: "No fields", hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." });
4180
4226
  }
4181
- return /* @__PURE__ */ jsxs4(Column, { children: [
4227
+ return /* @__PURE__ */ jsxs4(Column2, { children: [
4182
4228
  /* @__PURE__ */ jsx4(
4183
4229
  Toolbar4,
4184
4230
  {
4185
4231
  left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
4186
4232
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
4187
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "autoAssign" }), children: "Auto-assign" }),
4188
- view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui2({ kind: "apply" }), children: "Apply" }) : null
4233
+ /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui3({ kind: "autoAssign" }), children: "Auto-assign" }),
4234
+ view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui3({ kind: "apply" }), children: "Apply" }) : null
4189
4235
  ] })
4190
4236
  }
4191
4237
  ),
@@ -4197,7 +4243,7 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4197
4243
  meta: column.role,
4198
4244
  selected: column.focused,
4199
4245
  trailing: column.stat ? /* @__PURE__ */ jsx4(Badge4, { title: column.statTitle, children: column.stat }) : null,
4200
- onClick: () => ui2({ kind: "focus", columnId: column.id })
4246
+ onClick: () => ui3({ kind: "focus", columnId: column.id })
4201
4247
  },
4202
4248
  column.id
4203
4249
  )) }) }),
@@ -4209,11 +4255,11 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4209
4255
  label: entry.label,
4210
4256
  dimmed: entry.suggested,
4211
4257
  meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4212
- entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui2({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4213
- entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4258
+ entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui3({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4259
+ entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui3({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4214
4260
  entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4215
4261
  ] }),
4216
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui2({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
4262
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui3({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
4217
4263
  },
4218
4264
  entry.columnId
4219
4265
  )) }) : null
@@ -4224,15 +4270,15 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4224
4270
  title: "Filters",
4225
4271
  flush: true,
4226
4272
  badge: view.hasFilters ? /* @__PURE__ */ jsx4(Badge4, { children: view.filterCount }) : null,
4227
- actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui2({ kind: "addFilter" }) }),
4273
+ actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui3({ kind: "addFilter" }) }),
4228
4274
  children: [
4229
4275
  view.noFilters ? /* @__PURE__ */ jsx4(Note, { children: "No filters. Everything passes." }) : null,
4230
4276
  view.hasFilters ? /* @__PURE__ */ jsx4(RowList, { children: view.filters.map((filter) => /* @__PURE__ */ jsx4(
4231
4277
  Row,
4232
4278
  {
4233
4279
  label: filter.label,
4234
- meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui2({ kind: "filterOp", index: filter.index, op }) }) : null,
4235
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui2({ kind: "removeFilter", index: filter.index }) })
4280
+ meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui3({ kind: "filterOp", index: filter.index, op }) }) : null,
4281
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui3({ kind: "removeFilter", index: filter.index }) })
4236
4282
  },
4237
4283
  filter.key
4238
4284
  )) }) : null,
@@ -4240,8 +4286,8 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4240
4286
  Toolbar4,
4241
4287
  {
4242
4288
  divided: false,
4243
- left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui2({ kind: "combinator", combinator }) }),
4244
- right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "addGroup" }), children: "+ group" })
4289
+ left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui3({ kind: "combinator", combinator }) }),
4290
+ right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui3({ kind: "addGroup" }), children: "+ group" })
4245
4291
  }
4246
4292
  )
4247
4293
  ]
@@ -5377,10 +5423,10 @@ function parseJsonPath(path) {
5377
5423
  }
5378
5424
  return steps;
5379
5425
  }
5380
- function findBracketEnd(text2, start) {
5426
+ function findBracketEnd(text3, start) {
5381
5427
  let quote = null;
5382
- for (let i = start + 1; i < text2.length; i += 1) {
5383
- const char = text2[i];
5428
+ for (let i = start + 1; i < text3.length; i += 1) {
5429
+ const char = text3[i];
5384
5430
  if (quote) {
5385
5431
  if (char === "\\") i += 1;
5386
5432
  else if (char === quote) quote = null;
@@ -5804,9 +5850,9 @@ function firstGraphqlError(errors) {
5804
5850
  }
5805
5851
 
5806
5852
  // src/data/connectors/executors/csv.ts
5807
- function sniffDelimiter(text2) {
5853
+ function sniffDelimiter(text3) {
5808
5854
  const candidates = [",", ";", " ", "|"];
5809
- const line = firstLogicalLine(text2);
5855
+ const line = firstLogicalLine(text3);
5810
5856
  let best = ",";
5811
5857
  let bestCount = 0;
5812
5858
  for (const candidate of candidates) {
@@ -5828,21 +5874,21 @@ function sniffDelimiter(text2) {
5828
5874
  }
5829
5875
  return best;
5830
5876
  }
5831
- function firstLogicalLine(text2) {
5877
+ function firstLogicalLine(text3) {
5832
5878
  let inQuotes = false;
5833
- for (let i = 0; i < text2.length; i += 1) {
5834
- const char = text2[i];
5879
+ for (let i = 0; i < text3.length; i += 1) {
5880
+ const char = text3[i];
5835
5881
  if (char === '"') {
5836
- if (inQuotes && text2[i + 1] === '"') i += 1;
5882
+ if (inQuotes && text3[i + 1] === '"') i += 1;
5837
5883
  else inQuotes = !inQuotes;
5838
5884
  } else if ((char === "\n" || char === "\r") && !inQuotes) {
5839
- return text2.slice(0, i);
5885
+ return text3.slice(0, i);
5840
5886
  }
5841
5887
  }
5842
- return text2;
5888
+ return text3;
5843
5889
  }
5844
- function parseCsv(text2, options = {}) {
5845
- const source = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
5890
+ function parseCsv(text3, options = {}) {
5891
+ const source = text3.charCodeAt(0) === 65279 ? text3.slice(1) : text3;
5846
5892
  const delimiter = options.delimiter && options.delimiter.length > 0 ? options.delimiter : sniffDelimiter(source);
5847
5893
  const quote = options.quote && options.quote.length > 0 ? options.quote : '"';
5848
5894
  const hasHeader = options.hasHeader ?? true;
@@ -5877,17 +5923,17 @@ function dedupe(names) {
5877
5923
  return count === 0 ? name : `${name}_${count + 1}`;
5878
5924
  });
5879
5925
  }
5880
- function scanRecords(text2, delimiter, quote) {
5926
+ function scanRecords(text3, delimiter, quote) {
5881
5927
  const records = [];
5882
5928
  let fields = [];
5883
5929
  let field = "";
5884
5930
  let inQuotes = false;
5885
5931
  let i = 0;
5886
- while (i < text2.length) {
5887
- const char = text2[i];
5932
+ while (i < text3.length) {
5933
+ const char = text3[i];
5888
5934
  if (inQuotes) {
5889
5935
  if (char === quote) {
5890
- if (text2[i + 1] === quote) {
5936
+ if (text3[i + 1] === quote) {
5891
5937
  field += quote;
5892
5938
  i += 2;
5893
5939
  continue;
@@ -5905,7 +5951,7 @@ function scanRecords(text2, delimiter, quote) {
5905
5951
  i += 1;
5906
5952
  continue;
5907
5953
  }
5908
- if (text2.startsWith(delimiter, i)) {
5954
+ if (text3.startsWith(delimiter, i)) {
5909
5955
  fields.push(field);
5910
5956
  field = "";
5911
5957
  i += delimiter.length;
@@ -5916,7 +5962,7 @@ function scanRecords(text2, delimiter, quote) {
5916
5962
  records.push(fields);
5917
5963
  fields = [];
5918
5964
  field = "";
5919
- i += char === "\r" && text2[i + 1] === "\n" ? 2 : 1;
5965
+ i += char === "\r" && text3[i + 1] === "\n" ? 2 : 1;
5920
5966
  continue;
5921
5967
  }
5922
5968
  field += char;
@@ -7067,14 +7113,17 @@ export {
7067
7113
  DEFAULT_RANGES,
7068
7114
  EMPTY_LAYOUT,
7069
7115
  EXPIRY_SKEW_MS,
7070
- BEHAVIOURS as FIELDS_BEHAVIOURS,
7116
+ BEHAVIOURS2 as FIELDS_BEHAVIOURS,
7071
7117
  FIELDS_DECLARATION,
7072
7118
  FIELDS_PANEL_ID,
7073
7119
  FIELDS_RUNTIME_UNITS,
7074
7120
  FieldsController,
7075
7121
  FieldsPanelView,
7076
7122
  IMPLEMENTED_VIEW_MODES,
7123
+ BEHAVIOURS as METRICS_BEHAVIOURS,
7124
+ METRICS_DECLARATION,
7077
7125
  METRICS_PANEL_ID,
7126
+ METRICS_RUNTIME_UNITS,
7078
7127
  MIN_COLUMN_WIDTH,
7079
7128
  MONO_FILL_PALETTE,
7080
7129
  MONO_PALETTE,
@@ -7086,6 +7135,7 @@ export {
7086
7135
  TableController,
7087
7136
  TablePanelView,
7088
7137
  applyFieldsUi,
7138
+ applyMetricsUi,
7089
7139
  applyPrecision,
7090
7140
  base64Url,
7091
7141
  breachSideOf,
@@ -7151,6 +7201,8 @@ export {
7151
7201
  mergeRefreshResponse,
7152
7202
  metricsManifest,
7153
7203
  metricsPanel,
7204
+ metricsViewManifest,
7205
+ metricsViewState,
7154
7206
  moveCell,
7155
7207
  moveColumn,
7156
7208
  needsRefresh,