@xenosystem/blocks 0.13.12 → 0.13.14

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.
package/dist/ops/index.js CHANGED
@@ -7,10 +7,8 @@ import {
7
7
  terminalThemeVar
8
8
  } from "../chunk-WE6A2DTY.js";
9
9
  import {
10
- CancelGlyph,
11
- ICONS,
12
- RetryGlyph
13
- } from "../chunk-5BVZMAD2.js";
10
+ ICONS
11
+ } from "../chunk-PI5WOATA.js";
14
12
  import "../chunk-2KG3PWR4.js";
15
13
 
16
14
  // src/ops/console/panel.ts
@@ -62,10 +60,10 @@ function spliceRedactedAppend(existing, addition, redact, carry = REDACTION_CARR
62
60
  const tail = existing.slice(cut);
63
61
  return head + redact(tail + addition);
64
62
  }
65
- function truncateLogMessage(text2, max) {
66
- if (max <= 0 || text2.length <= max) return text2;
67
- if (max <= MESSAGE_ELISION.length) return text2.slice(text2.length - max);
68
- return MESSAGE_ELISION + text2.slice(text2.length - (max - MESSAGE_ELISION.length));
63
+ function truncateLogMessage(text3, max) {
64
+ if (max <= 0 || text3.length <= max) return text3;
65
+ if (max <= MESSAGE_ELISION.length) return text3.slice(text3.length - max);
66
+ return MESSAGE_ELISION + text3.slice(text3.length - (max - MESSAGE_ELISION.length));
69
67
  }
70
68
  function formatRecord(record) {
71
69
  const time = new Date(record.ts).toISOString();
@@ -242,7 +240,7 @@ var ConsoleController = class {
242
240
  */
243
241
  merge(existing, patch) {
244
242
  const secrets = [...this.host.getSecrets?.() ?? []];
245
- const redact = (text2) => redactForLog(text2, secrets);
243
+ const redact = (text3) => redactForLog(text3, secrets);
246
244
  let message = existing.message;
247
245
  if (typeof patch.message === "string") message = redact(patch.message);
248
246
  if (typeof patch.appendMessage === "string" && patch.appendMessage.length > 0) {
@@ -872,24 +870,24 @@ function consoleRuntimeComponents(controller, rowHeight) {
872
870
  function ConsolePanelView({ controller, ...options }) {
873
871
  useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
874
872
  const view = consoleViewState(controller, options);
875
- const ui2 = (intent) => applyConsoleUi(controller, intent);
873
+ const ui3 = (intent) => applyConsoleUi(controller, intent);
876
874
  return /* @__PURE__ */ jsxs(Column, { children: [
877
875
  /* @__PURE__ */ jsx(
878
876
  Toolbar,
879
877
  {
880
878
  left: /* @__PURE__ */ jsxs(Fragment, { children: [
881
- /* @__PURE__ */ jsx(SearchField, { value: view.search, placeholder: "Filter\u2026", onChange: (value) => ui2({ kind: "search", value }) }),
882
- /* @__PURE__ */ jsx(SegmentedControl, { size: "sm", label: "Minimum level", options: view.levels, value: view.level, onChange: (level) => ui2({ kind: "level", level }) })
879
+ /* @__PURE__ */ jsx(SearchField, { value: view.search, placeholder: "Filter\u2026", onChange: (value) => ui3({ kind: "search", value }) }),
880
+ /* @__PURE__ */ jsx(SegmentedControl, { size: "sm", label: "Minimum level", options: view.levels, value: view.level, onChange: (level) => ui3({ kind: "level", level }) })
883
881
  ] }),
884
882
  right: /* @__PURE__ */ jsxs(ToolbarGroup, { end: true, children: [
885
883
  view.errors ? /* @__PURE__ */ jsx(Badge, { tone: "error", children: view.errors }) : null,
886
884
  view.warns ? /* @__PURE__ */ jsx(Badge, { tone: "warning", children: view.warns }) : null,
887
- /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(CopyGlyph, {}), label: "Copy visible", onClick: () => ui2({ kind: "copyVisible" }) }),
888
- /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(ClearGlyph, {}), label: "Clear", onClick: () => ui2({ kind: "clear" }) })
885
+ /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(CopyGlyph, {}), label: "Copy visible", onClick: () => ui3({ kind: "copyVisible" }) }),
886
+ /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(ClearGlyph, {}), label: "Clear", onClick: () => ui3({ kind: "clear" }) })
889
887
  ] })
890
888
  }
891
889
  ),
892
- view.multiSource ? /* @__PURE__ */ jsx(Stack, { direction: "row", gap: 4, pad: "inset", children: view.sources.map((source) => /* @__PURE__ */ jsx(FilterChip, { on: source.on, title: source.title, onChange: (on) => ui2({ kind: "source", id: source.id, on }), children: source.label }, source.id)) }) : null,
890
+ view.multiSource ? /* @__PURE__ */ jsx(Stack, { direction: "row", gap: 4, pad: "inset", children: view.sources.map((source) => /* @__PURE__ */ jsx(FilterChip, { on: source.on, title: source.title, onChange: (on) => ui3({ kind: "source", id: source.id, on }), children: source.label }, source.id)) }) : null,
893
891
  view.empty ? /* @__PURE__ */ jsx(EmptyState, { title: view.emptyTitle, hint: view.emptyHint }) : null,
894
892
  view.listed ? /* @__PURE__ */ jsx(ConsoleScroller, { controller, rowHeight: view.rowHeight, children: /* @__PURE__ */ jsx(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: /* @__PURE__ */ jsx(RowList, { role: "log", children: view.rows.map((row) => /* @__PURE__ */ jsxs(Fragment, { children: [
895
893
  /* @__PURE__ */ jsx(
@@ -904,8 +902,8 @@ function ConsolePanelView({ controller, ...options }) {
904
902
  /* @__PURE__ */ jsx(Text, { children: row.message })
905
903
  ] }),
906
904
  trailing: row.repeat ? /* @__PURE__ */ jsx(Badge, { title: "Repeated consecutively", children: row.repeat }) : null,
907
- onClick: () => ui2({ kind: "row", id: row.id }),
908
- onContextMenuAt: (x, y) => ui2({ kind: "contextMenu", id: row.id, x, y })
905
+ onClick: () => ui3({ kind: "row", id: row.id }),
906
+ onContextMenuAt: (x, y) => ui3({ kind: "contextMenu", id: row.id, x, y })
909
907
  },
910
908
  row.id
911
909
  ),
@@ -1816,19 +1814,25 @@ var runsManifest = {
1816
1814
  import { useSyncExternalStore as useSyncExternalStore2 } from "react";
1817
1815
  import {
1818
1816
  Badge as Badge2,
1817
+ Column as Column2,
1819
1818
  EmptyState as EmptyState2,
1819
+ FilterChip as FilterChip2,
1820
1820
  IconButton as IconButton2,
1821
- ProportionBar,
1821
+ Note,
1822
+ Progress,
1822
1823
  Row as Row2,
1823
1824
  RowList as RowList2,
1824
1825
  ScrollArea as ScrollArea2,
1825
1826
  SearchField as SearchField2,
1827
+ Stack as Stack2,
1826
1828
  StatusBar as StatusBar2,
1829
+ Text as Text2,
1827
1830
  TextButton,
1828
1831
  Toolbar as Toolbar2,
1829
1832
  ToolbarGroup as ToolbarGroup2
1830
1833
  } from "@xenosystem/components/chrome";
1831
- import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1834
+
1835
+ // src/ops/runs/declaration.ts
1832
1836
  var TONE2 = {
1833
1837
  requested: "neutral",
1834
1838
  queued: "neutral",
@@ -1841,205 +1845,296 @@ var TONE2 = {
1841
1845
  failed: "error",
1842
1846
  cancelled: "neutral"
1843
1847
  };
1844
- function toneOf(status) {
1845
- return TONE2[status] ?? "neutral";
1846
- }
1847
- function RunsPanelView({
1848
- controller,
1849
- showStats = true,
1850
- showProgress = true,
1851
- emptyHint,
1852
- now = () => Date.now()
1853
- }) {
1854
- const state = useSyncExternalStore2(controller.subscribe, controller.getState, controller.getState);
1855
- const active = new Set(state.filter.statuses ?? []);
1856
- return /* @__PURE__ */ jsxs2("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
1857
- /* @__PURE__ */ jsx2(
1858
- Toolbar2,
1859
- {
1860
- left: /* @__PURE__ */ jsx2(
1861
- SearchField2,
1862
- {
1863
- value: state.filter.search ?? "",
1864
- onChange: (term) => controller.setSearch(term),
1865
- placeholder: "Filter runs\u2026"
1866
- }
1867
- ),
1868
- right: /* @__PURE__ */ jsx2(ToolbarGroup2, { end: true, children: /* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.clearTerminal(), children: "Clear finished" }) })
1869
- }
1870
- ),
1871
- /* @__PURE__ */ jsxs2("div", { style: { display: "flex", gap: 4, padding: "2px 6px", flexWrap: "wrap" }, children: [
1872
- ALL_STATUSES.filter((s) => state.counts[s] > 0).map((status) => /* @__PURE__ */ jsx2(
1873
- "span",
1874
- {
1875
- onClick: () => controller.setStatuses(
1876
- active.has(status) ? [...active].filter((s) => s !== status) : [...active, status]
1877
- ),
1878
- style: { cursor: "pointer", opacity: active.size === 0 || active.has(status) ? 1 : 0.4 },
1879
- children: /* @__PURE__ */ jsx2(Badge2, { tone: TONE2[status], children: `${status} ${state.counts[status]}` })
1880
- },
1881
- status
1882
- )),
1883
- state.sources.length > 1 ? state.sources.map((source) => /* @__PURE__ */ jsx2(
1884
- "span",
1885
- {
1886
- onClick: () => controller.toggleSource(source.id),
1887
- style: { cursor: "pointer", opacity: source.enabled ? 1 : 0.4 },
1888
- children: /* @__PURE__ */ jsx2(Badge2, { title: `${source.count} runs`, children: source.label })
1889
- },
1890
- source.id
1891
- )) : null
1892
- ] }),
1893
- state.rows.length === 0 ? /* @__PURE__ */ jsx2(
1894
- EmptyState2,
1895
- {
1896
- title: state.total > 0 ? "No matching runs" : "No runs yet",
1897
- hint: state.total > 0 ? "Nothing matches the current filter." : emptyHint ?? "Run something to see it here."
1898
- }
1899
- ) : /* @__PURE__ */ jsx2(ScrollArea2, { children: /* @__PURE__ */ jsx2(RowList2, { role: "list", children: state.rows.map(
1900
- (row) => row.kind === "run" ? /* @__PURE__ */ jsx2(RunRow, { row, controller, showProgress, now }, row.key) : /* @__PURE__ */ jsx2(StepRow, { row, controller, showProgress }, row.key)
1901
- ) }) }),
1902
- /* @__PURE__ */ jsx2(
1903
- StatusBar2,
1904
- {
1905
- left: `${state.runs.length} of ${state.total}${state.dropped > 0 ? ` \xB7 ${state.dropped} dropped` : ""}`,
1906
- right: showStats && state.stats.terminal > 0 ? `${Math.round((state.stats.successRate ?? 0) * 100)}% ok${state.stats.meanDurationMs !== null ? ` \xB7 ~${formatDuration(state.stats.meanDurationMs)}` : ""}` : (
1907
- // `null` success rate is NOT 0% — nothing has finished, which is a different fact.
1908
- state.stats.active > 0 ? `${state.stats.active} active` : void 0
1909
- )
1848
+ var toneOf = (status) => TONE2[status] ?? "neutral";
1849
+ var truncationHint = (reason) => reason === "cycle" ? "This step is nested inside itself \u2014 the panel stopped here rather than repeat it." : "Nesting is deeper than this panel will render.";
1850
+ function runsViewState(c, options = {}) {
1851
+ const s = c.getState();
1852
+ const showStats = options.showStats ?? true;
1853
+ const showProgress = options.showProgress ?? true;
1854
+ const now = options.now ?? (() => Date.now());
1855
+ const active = new Set(s.filter.statuses ?? []);
1856
+ const statuses = ALL_STATUSES.filter((st) => s.counts[st] > 0).map((st) => ({ id: st, label: `${st} ${s.counts[st]}`, on: active.size === 0 || active.has(st), tone: toneOf(st) }));
1857
+ const sources = s.sources.length > 1 ? s.sources.map((src) => ({ id: src.id, label: src.label, on: src.enabled, tone: "neutral", title: `${src.count} runs` })) : [];
1858
+ const stats = showStats && s.stats.terminal > 0 ? `${Math.round((s.stats.successRate ?? 0) * 100)}% ok${s.stats.meanDurationMs !== null ? ` \xB7 ~${formatDuration(s.stats.meanDurationMs)}` : ""}` : s.stats.active > 0 ? `${s.stats.active} active` : void 0;
1859
+ return {
1860
+ search: s.filter.search ?? "",
1861
+ statuses,
1862
+ multiSource: s.sources.length > 1,
1863
+ sources,
1864
+ hasChips: statuses.length > 0 || sources.length > 0,
1865
+ empty: s.rows.length === 0,
1866
+ emptyTitle: s.total > 0 ? "No matching runs" : "No runs yet",
1867
+ emptyHint: s.total > 0 ? "Nothing matches the current filter." : options.emptyHint ?? "Run something to see it here.",
1868
+ listed: s.rows.length > 0,
1869
+ rows: s.rows.map((row) => {
1870
+ const run = row.run;
1871
+ if (row.kind === "run") {
1872
+ const duration = durationOf(run, now());
1873
+ const hasProgress2 = showProgress && run.progress !== void 0;
1874
+ return {
1875
+ key: row.key,
1876
+ runId: run.id,
1877
+ stepPath: [],
1878
+ depth: 0,
1879
+ step: false,
1880
+ status: run.status,
1881
+ tone: toneOf(run.status),
1882
+ label: run.label,
1883
+ trigger: run.trigger,
1884
+ retries: (run.retryCount ?? 0) > 0 ? `retry x${run.retryCount}` : void 0,
1885
+ truncated: row.truncated ? row.truncated === "cycle" ? "cycle" : "deeper\u2026" : void 0,
1886
+ truncatedHint: row.truncated ? truncationHint(row.truncated) : void 0,
1887
+ hasProgress: hasProgress2,
1888
+ progress: run.progress ?? 0,
1889
+ progressLabel: `${Math.round((run.progress ?? 0) * 100)}%`,
1890
+ duration: !hasProgress2 && duration !== null ? formatDuration(duration) : void 0,
1891
+ canCancel: canCancel(run),
1892
+ cancelLabel: `Cancel ${run.label}`,
1893
+ canRetry: canRetry(run),
1894
+ retryLabel: `Retry ${run.label}`,
1895
+ hasError: !!run.error,
1896
+ errorCode: run.error?.code,
1897
+ errorMessage: run.error?.message
1898
+ };
1910
1899
  }
1911
- )
1912
- ] });
1900
+ const step = row.step;
1901
+ const elapsed = step.startedAt !== void 0 && step.endedAt !== void 0 ? step.endedAt - step.startedAt : null;
1902
+ const hasProgress = showProgress && step.progress !== void 0;
1903
+ return {
1904
+ key: row.key,
1905
+ runId: row.runId,
1906
+ stepPath: row.stepPath,
1907
+ depth: row.depth,
1908
+ step: true,
1909
+ status: step.status,
1910
+ tone: toneOf(step.status),
1911
+ label: step.label,
1912
+ truncated: row.truncated ? row.truncated === "cycle" ? "cycle" : "deeper\u2026" : void 0,
1913
+ truncatedHint: row.truncated ? truncationHint(row.truncated) : void 0,
1914
+ hasProgress,
1915
+ progress: step.progress ?? 0,
1916
+ progressLabel: `${Math.round((step.progress ?? 0) * 100)}%`,
1917
+ duration: !hasProgress && elapsed !== null ? formatDuration(elapsed) : void 0,
1918
+ canCancel: false,
1919
+ cancelLabel: "",
1920
+ canRetry: false,
1921
+ retryLabel: "",
1922
+ hasError: false
1923
+ };
1924
+ }),
1925
+ countLabel: `${s.runs.length} of ${s.total}${s.dropped > 0 ? ` \xB7 ${s.dropped} dropped` : ""}`,
1926
+ statsLabel: stats
1927
+ };
1913
1928
  }
1914
- function TruncationBadge({ reason }) {
1915
- return /* @__PURE__ */ jsx2(
1916
- Badge2,
1917
- {
1918
- tone: "warning",
1919
- title: reason === "cycle" ? "This step is nested inside itself \u2014 the panel stopped here rather than repeat it." : "Nesting is deeper than this panel will render.",
1920
- children: reason === "cycle" ? "cycle" : "deeper\u2026"
1929
+ function applyRunsUi(c, intent) {
1930
+ switch (intent.kind) {
1931
+ case "search":
1932
+ c.setSearch(intent.value);
1933
+ break;
1934
+ case "clearTerminal":
1935
+ c.clearTerminal();
1936
+ break;
1937
+ case "status": {
1938
+ const active = new Set(c.getState().filter.statuses ?? []);
1939
+ const st = intent.status;
1940
+ if (active.has(st)) active.delete(st);
1941
+ else active.add(st);
1942
+ c.setStatuses([...active]);
1943
+ break;
1921
1944
  }
1922
- );
1923
- }
1924
- function RowLabel({
1925
- status,
1926
- text: text2,
1927
- trailing
1928
- }) {
1929
- return /* @__PURE__ */ jsxs2("span", { style: { display: "flex", gap: 6, alignItems: "center", minWidth: 0 }, children: [
1930
- /* @__PURE__ */ jsx2(Badge2, { tone: toneOf(status), children: status }),
1931
- /* @__PURE__ */ jsx2("span", { style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: text2 }),
1932
- trailing
1933
- ] });
1945
+ case "source":
1946
+ c.toggleSource(intent.id, intent.on);
1947
+ break;
1948
+ case "row": {
1949
+ const row = c.getState().rows.find((r) => r.runId === intent.runId && r.stepPath.join("/") === intent.stepPath.join("/"));
1950
+ if (row?.hasChildren) c.toggleExpanded(intent.runId, intent.stepPath.length ? intent.stepPath : void 0);
1951
+ c.activate(intent.runId, intent.stepPath.length ? intent.stepPath : void 0);
1952
+ break;
1953
+ }
1954
+ case "contextMenu":
1955
+ c.requestContextMenu(intent.runId, intent.x, intent.y);
1956
+ break;
1957
+ case "cancel":
1958
+ c.cancel(intent.runId);
1959
+ break;
1960
+ case "retry":
1961
+ c.retry(intent.runId);
1962
+ break;
1963
+ }
1934
1964
  }
1935
- function RunRow({
1936
- row,
1937
- controller,
1938
- showProgress,
1939
- now
1940
- }) {
1941
- const run = row.run;
1942
- const duration = durationOf(run, now());
1943
- return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1944
- /* @__PURE__ */ jsx2(
1945
- Row2,
1965
+ var runsViewManifest = {
1966
+ id: runsManifest.id,
1967
+ version: "1.0.0",
1968
+ title: "Runs",
1969
+ category: "ops",
1970
+ layout: { slot: "side" },
1971
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The RunsViewModel the runtime derives from the controller on every draw." }],
1972
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
1973
+ commands: []
1974
+ };
1975
+ var EMPTY2 = { search: "", statuses: [], multiSource: false, sources: [], hasChips: false, empty: true, emptyTitle: "No runs yet", emptyHint: "Run something to see it here.", listed: false, rows: [], countLabel: "0 of 0" };
1976
+ var BEHAVIOURS2 = {
1977
+ "ops/runsPanelView": (inputs) => inputs.view ?? EMPTY2
1978
+ };
1979
+ var RUNS_RUNTIME_UNITS = [];
1980
+ var text2 = (b) => ({ component: "text", props: { text: b } });
1981
+ var ui2 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
1982
+ var chip = (intent) => ({
1983
+ component: "chrome/FilterChip",
1984
+ props: { on: { item: "on" }, title: { item: "title" } },
1985
+ on: { onChange: intent },
1986
+ children: [{ component: "chrome/Badge", props: { tone: { item: "tone" } }, children: [text2({ item: "label" })] }]
1987
+ });
1988
+ var RUNS_DECLARATION = {
1989
+ format: "xeno.block@1",
1990
+ manifest: runsViewManifest,
1991
+ behaviour: "ops/runsPanelView",
1992
+ composition: {
1993
+ component: "chrome/Column",
1994
+ children: [
1946
1995
  {
1947
- noIcon: true,
1948
- onClick: () => {
1949
- if (row.hasChildren) controller.toggleExpanded(run.id);
1950
- controller.activate(run.id);
1951
- },
1952
- onContextMenu: (e) => {
1953
- e.preventDefault();
1954
- controller.requestContextMenu(run.id, e.clientX, e.clientY);
1955
- },
1956
- label: /* @__PURE__ */ jsx2(
1957
- RowLabel,
1958
- {
1959
- status: run.status,
1960
- text: run.label,
1961
- trailing: /* @__PURE__ */ jsxs2(Fragment2, { children: [
1962
- run.trigger ? /* @__PURE__ */ jsx2("span", { style: { opacity: 0.5, fontSize: 9 }, children: run.trigger }) : null,
1963
- (run.retryCount ?? 0) > 0 ? /* @__PURE__ */ jsx2(Badge2, { title: "Retries so far", children: `retry x${run.retryCount}` }) : null,
1964
- row.truncated ? /* @__PURE__ */ jsx2(TruncationBadge, { reason: row.truncated }) : null
1965
- ] })
1966
- }
1967
- ),
1968
- meta: showProgress && run.progress !== void 0 ? /* @__PURE__ */ jsx2("span", { style: { width: 48, display: "inline-flex" }, children: /* @__PURE__ */ jsx2(
1969
- ProportionBar,
1996
+ component: "chrome/Toolbar",
1997
+ props: {
1998
+ left: { node: { component: "chrome/SearchField", props: { value: { input: "view.search" }, placeholder: { value: "Filter runs\u2026" } }, on: { onChange: ui2("search", { value: { arg: "" } }) } } },
1999
+ right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/TextButton", on: { onClick: ui2("clearTerminal") }, children: [text2({ value: "Clear finished" })] }] } }
2000
+ }
2001
+ },
2002
+ {
2003
+ component: "chrome/Stack",
2004
+ when: { input: "view.hasChips" },
2005
+ props: { direction: { value: "row" }, gap: { value: 4 }, pad: { value: "inset" } },
2006
+ children: [
2007
+ { ...chip(ui2("status", { status: { item: "id" }, on: { arg: "" } })), each: { input: "view.statuses", key: "id" } },
2008
+ { ...chip(ui2("source", { id: { item: "id" }, on: { arg: "" } })), each: { input: "view.sources", key: "id" } }
2009
+ ]
2010
+ },
2011
+ { component: "chrome/EmptyState", when: { input: "view.empty" }, props: { title: { input: "view.emptyTitle" }, hint: { input: "view.emptyHint" } } },
2012
+ {
2013
+ component: "chrome/ScrollArea",
2014
+ when: { input: "view.listed" },
2015
+ children: [
1970
2016
  {
1971
- label: `${Math.round(run.progress * 100)}%`,
1972
- segments: [
1973
- { value: run.progress, opacity: 0.45 },
1974
- { value: Math.max(0, 1 - run.progress), opacity: 0.06 }
2017
+ component: "chrome/RowList",
2018
+ children: [
2019
+ {
2020
+ component: "fragment",
2021
+ each: { input: "view.rows", key: "key" },
2022
+ children: [
2023
+ {
2024
+ component: "chrome/Row",
2025
+ props: {
2026
+ noIcon: { value: true },
2027
+ depth: { item: "depth" },
2028
+ dimmed: { item: "step" },
2029
+ label: {
2030
+ node: {
2031
+ component: "chrome/Stack",
2032
+ props: { direction: { value: "row" }, pad: { value: "none" } },
2033
+ children: [
2034
+ { component: "chrome/Badge", props: { tone: { item: "tone" } }, children: [text2({ item: "status" })] },
2035
+ { component: "chrome/Text", children: [text2({ item: "label" })] },
2036
+ { component: "chrome/Text", when: { item: "trigger" }, props: { role: { value: "caption" } }, children: [text2({ item: "trigger" })] },
2037
+ { component: "chrome/Badge", when: { item: "retries" }, props: { title: { value: "Retries so far" } }, children: [text2({ item: "retries" })] },
2038
+ { component: "chrome/Badge", when: { item: "truncated" }, props: { tone: { value: "warning" }, title: { item: "truncatedHint" } }, children: [text2({ item: "truncated" })] }
2039
+ ]
2040
+ }
2041
+ },
2042
+ meta: {
2043
+ node: {
2044
+ component: "fragment",
2045
+ children: [
2046
+ { component: "chrome/Progress", when: { item: "hasProgress" }, props: { value: { item: "progress" }, label: { item: "progressLabel" } } },
2047
+ { component: "chrome/Text", when: { item: "duration" }, props: { role: { value: "number" } }, children: [text2({ item: "duration" })] }
2048
+ ]
2049
+ }
2050
+ },
2051
+ trailing: {
2052
+ node: {
2053
+ component: "fragment",
2054
+ children: [
2055
+ { component: "chrome/IconButton", when: { item: "canCancel" }, props: { size: { value: "sm" }, icon: { node: { component: "icons/Cancel" } }, label: { item: "cancelLabel" } }, on: { onClick: ui2("cancel", { runId: { item: "runId" } }) } },
2056
+ { component: "chrome/IconButton", when: { item: "canRetry" }, props: { size: { value: "sm" }, icon: { node: { component: "icons/Retry" } }, label: { item: "retryLabel" } }, on: { onClick: ui2("retry", { runId: { item: "runId" } }) } }
2057
+ ]
2058
+ }
2059
+ }
2060
+ },
2061
+ on: { onClick: ui2("row", { runId: { item: "runId" }, stepPath: { item: "stepPath" } }), onContextMenuAt: ui2("contextMenu", { runId: { item: "runId" }, x: { arg: "", index: 0 }, y: { arg: "", index: 1 } }) }
2062
+ },
2063
+ {
2064
+ component: "chrome/Note",
2065
+ when: { item: "hasError" },
2066
+ props: { variant: { value: "inset" } },
2067
+ // The host's sentence, verbatim. The panel never composes error prose.
2068
+ children: [{ component: "chrome/Badge", props: { tone: { value: "error" } }, children: [text2({ item: "errorCode" })] }, text2({ value: " " }), text2({ item: "errorMessage" })]
2069
+ }
2070
+ ]
2071
+ }
1975
2072
  ]
1976
2073
  }
1977
- ) }) : duration !== null ? /* @__PURE__ */ jsx2("span", { style: { fontVariantNumeric: "tabular-nums", opacity: 0.6 }, children: formatDuration(duration) }) : null,
1978
- trailing: /* @__PURE__ */ jsxs2(Fragment2, { children: [
1979
- canCancel(run) ? /* @__PURE__ */ jsx2(
1980
- IconButton2,
1981
- {
1982
- size: "sm",
1983
- icon: /* @__PURE__ */ jsx2(CancelGlyph, {}),
1984
- label: `Cancel ${run.label}`,
1985
- onClick: () => controller.cancel(run.id)
1986
- }
1987
- ) : null,
1988
- canRetry(run) ? /* @__PURE__ */ jsx2(
1989
- IconButton2,
1990
- {
1991
- size: "sm",
1992
- icon: /* @__PURE__ */ jsx2(RetryGlyph, {}),
1993
- label: `Retry ${run.label}`,
1994
- onClick: () => controller.retry(run.id)
1995
- }
1996
- ) : null
1997
- ] })
2074
+ ]
2075
+ },
2076
+ { component: "chrome/StatusBar", props: { left: { input: "view.countLabel" }, right: { input: "view.statsLabel" } } }
2077
+ ]
2078
+ }
2079
+ };
2080
+
2081
+ // src/ops/runs/react/RunsPanelView.tsx
2082
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2083
+ var CancelGlyph = ICONS["icons/Cancel"];
2084
+ var RetryGlyph = ICONS["icons/Retry"];
2085
+ function RunsPanelView({ controller, ...options }) {
2086
+ useSyncExternalStore2(controller.subscribe, controller.getState, controller.getState);
2087
+ const view = runsViewState(controller, options);
2088
+ const ui3 = (intent) => applyRunsUi(controller, intent);
2089
+ const chip2 = (c, onChange) => /* @__PURE__ */ jsx2(FilterChip2, { on: c.on, title: c.title, onChange, children: /* @__PURE__ */ jsx2(Badge2, { tone: c.tone, children: c.label }) }, c.id);
2090
+ return /* @__PURE__ */ jsxs2(Column2, { children: [
2091
+ /* @__PURE__ */ jsx2(
2092
+ Toolbar2,
2093
+ {
2094
+ left: /* @__PURE__ */ jsx2(SearchField2, { value: view.search, placeholder: "Filter runs\u2026", onChange: (value) => ui3({ kind: "search", value }) }),
2095
+ right: /* @__PURE__ */ jsx2(ToolbarGroup2, { end: true, children: /* @__PURE__ */ jsx2(TextButton, { onClick: () => ui3({ kind: "clearTerminal" }), children: "Clear finished" }) })
1998
2096
  }
1999
2097
  ),
2000
- run.error ? /* @__PURE__ */ jsxs2("div", { style: { padding: "0 10px 3px 24px", fontSize: 10, opacity: 0.75 }, children: [
2001
- /* @__PURE__ */ jsx2(Badge2, { tone: "error", children: run.error.code }),
2002
- " ",
2003
- run.error.message
2004
- ] }) : null
2005
- ] });
2006
- }
2007
- function StepRow({
2008
- row,
2009
- controller,
2010
- showProgress
2011
- }) {
2012
- const step = row.step;
2013
- const elapsed = step.startedAt !== void 0 && step.endedAt !== void 0 ? step.endedAt - step.startedAt : null;
2014
- return /* @__PURE__ */ jsx2(
2015
- Row2,
2016
- {
2017
- noIcon: true,
2018
- style: { paddingLeft: 12 + row.depth * 12, opacity: 0.85 },
2019
- onClick: () => {
2020
- if (row.hasChildren) controller.toggleExpanded(row.runId, row.stepPath);
2021
- controller.activate(row.runId, row.stepPath);
2022
- },
2023
- label: /* @__PURE__ */ jsx2(
2024
- RowLabel,
2098
+ view.hasChips ? /* @__PURE__ */ jsxs2(Stack2, { direction: "row", gap: 4, pad: "inset", children: [
2099
+ view.statuses.map((c) => chip2(c, (on) => ui3({ kind: "status", status: c.id, on }))),
2100
+ view.sources.map((c) => chip2(c, (on) => ui3({ kind: "source", id: c.id, on })))
2101
+ ] }) : null,
2102
+ view.empty ? /* @__PURE__ */ jsx2(EmptyState2, { title: view.emptyTitle, hint: view.emptyHint }) : null,
2103
+ view.listed ? /* @__PURE__ */ jsx2(ScrollArea2, { children: /* @__PURE__ */ jsx2(RowList2, { children: view.rows.map((row) => /* @__PURE__ */ jsxs2(Fragment2, { children: [
2104
+ /* @__PURE__ */ jsx2(
2105
+ Row2,
2025
2106
  {
2026
- status: step.status,
2027
- text: step.label,
2028
- trailing: row.truncated ? /* @__PURE__ */ jsx2(TruncationBadge, { reason: row.truncated }) : null
2029
- }
2107
+ noIcon: true,
2108
+ depth: row.depth,
2109
+ dimmed: row.step,
2110
+ label: /* @__PURE__ */ jsxs2(Stack2, { direction: "row", pad: "none", children: [
2111
+ /* @__PURE__ */ jsx2(Badge2, { tone: row.tone, children: row.status }),
2112
+ /* @__PURE__ */ jsx2(Text2, { children: row.label }),
2113
+ row.trigger ? /* @__PURE__ */ jsx2(Text2, { role: "caption", children: row.trigger }) : null,
2114
+ row.retries ? /* @__PURE__ */ jsx2(Badge2, { title: "Retries so far", children: row.retries }) : null,
2115
+ row.truncated ? /* @__PURE__ */ jsx2(Badge2, { tone: "warning", title: row.truncatedHint, children: row.truncated }) : null
2116
+ ] }),
2117
+ meta: /* @__PURE__ */ jsxs2(Fragment2, { children: [
2118
+ row.hasProgress ? /* @__PURE__ */ jsx2(Progress, { value: row.progress, label: row.progressLabel }) : null,
2119
+ row.duration ? /* @__PURE__ */ jsx2(Text2, { role: "number", children: row.duration }) : null
2120
+ ] }),
2121
+ trailing: /* @__PURE__ */ jsxs2(Fragment2, { children: [
2122
+ row.canCancel ? /* @__PURE__ */ jsx2(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx2(CancelGlyph, {}), label: row.cancelLabel, onClick: () => ui3({ kind: "cancel", runId: row.runId }) }) : null,
2123
+ row.canRetry ? /* @__PURE__ */ jsx2(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx2(RetryGlyph, {}), label: row.retryLabel, onClick: () => ui3({ kind: "retry", runId: row.runId }) }) : null
2124
+ ] }),
2125
+ onClick: () => ui3({ kind: "row", runId: row.runId, stepPath: row.stepPath }),
2126
+ onContextMenuAt: (x, y) => ui3({ kind: "contextMenu", runId: row.runId, x, y })
2127
+ },
2128
+ row.key
2030
2129
  ),
2031
- meta: showProgress && step.progress !== void 0 ? /* @__PURE__ */ jsx2("span", { style: { width: 48, display: "inline-flex" }, children: /* @__PURE__ */ jsx2(
2032
- ProportionBar,
2033
- {
2034
- label: `${Math.round(step.progress * 100)}%`,
2035
- segments: [
2036
- { value: step.progress, opacity: 0.45 },
2037
- { value: Math.max(0, 1 - step.progress), opacity: 0.06 }
2038
- ]
2039
- }
2040
- ) }) : elapsed !== null ? /* @__PURE__ */ jsx2("span", { style: { fontVariantNumeric: "tabular-nums", opacity: 0.6 }, children: formatDuration(elapsed) }) : null
2041
- }
2042
- );
2130
+ row.hasError ? /* @__PURE__ */ jsxs2(Note, { variant: "inset", children: [
2131
+ /* @__PURE__ */ jsx2(Badge2, { tone: "error", children: row.errorCode }),
2132
+ " ",
2133
+ row.errorMessage
2134
+ ] }, `${row.key}:error`) : null
2135
+ ] })) }) }) : null,
2136
+ /* @__PURE__ */ jsx2(StatusBar2, { left: view.countLabel, right: view.statsLabel })
2137
+ ] });
2043
2138
  }
2044
2139
 
2045
2140
  // src/ops/runs/panel.ts
@@ -2930,11 +3025,11 @@ function omissionLabel(omission) {
2930
3025
  }
2931
3026
 
2932
3027
  // src/ops/diff/controller.ts
2933
- var EMPTY2 = { files: [], status: "idle" };
3028
+ var EMPTY3 = { files: [], status: "idle" };
2934
3029
  var DiffController = class {
2935
3030
  host;
2936
3031
  reviewEnabled;
2937
- model = EMPTY2;
3032
+ model = EMPTY3;
2938
3033
  rev = -1;
2939
3034
  decisions = /* @__PURE__ */ new Map();
2940
3035
  view;
@@ -3197,7 +3292,7 @@ var DiffController = class {
3197
3292
  /** Tear down. */
3198
3293
  dispose() {
3199
3294
  this.listeners.clear();
3200
- this.model = EMPTY2;
3295
+ this.model = EMPTY3;
3201
3296
  this.decisions.clear();
3202
3297
  }
3203
3298
  };
@@ -3353,8 +3448,8 @@ var MARK = {
3353
3448
  context: {}
3354
3449
  };
3355
3450
  var SIGIL = { added: "+", removed: "-", context: " " };
3356
- function visible(text2, show) {
3357
- return show ? text2.replace(/\t/g, "\u2192 ").replace(/ /g, "\xB7") : text2;
3451
+ function visible(text3, show) {
3452
+ return show ? text3.replace(/\t/g, "\u2192 ").replace(/ /g, "\xB7") : text3;
3358
3453
  }
3359
3454
  function LineText({
3360
3455
  line,
@@ -3751,7 +3846,10 @@ export {
3751
3846
  LEVEL_ORDER,
3752
3847
  MESSAGE_ELISION,
3753
3848
  REDACTION_CARRY,
3849
+ BEHAVIOURS2 as RUNS_BEHAVIOURS,
3850
+ RUNS_DECLARATION,
3754
3851
  RUNS_PANEL_ID,
3852
+ RUNS_RUNTIME_UNITS,
3755
3853
  RUN_PATH_SEPARATOR,
3756
3854
  RUN_STATUSES,
3757
3855
  RunsController,
@@ -3763,6 +3861,7 @@ export {
3763
3861
  XENO_TERMINAL_THEME_KEYS,
3764
3862
  appendStepsAtPath,
3765
3863
  applyConsoleUi,
3864
+ applyRunsUi,
3766
3865
  canCancel,
3767
3866
  canRetry,
3768
3867
  collapseAdjacent,
@@ -3804,6 +3903,8 @@ export {
3804
3903
  resolveStepPath,
3805
3904
  runsManifest,
3806
3905
  runsPanel,
3906
+ runsViewManifest,
3907
+ runsViewState,
3807
3908
  safeJson,
3808
3909
  spliceRedactedAppend,
3809
3910
  splitRows,
@@ -3811,5 +3912,6 @@ export {
3811
3912
  terminalManifest,
3812
3913
  terminalPanel,
3813
3914
  terminalThemeVar,
3915
+ toneOf,
3814
3916
  truncateLogMessage
3815
3917
  };