@kky42/pievo-server 2.1.0 → 2.3.0
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/.output/nitro.json +1 -1
- package/.output/public/assets/LoopView-DjVHUyrR.js +59 -0
- package/.output/public/assets/{Modal-B3xpEtcf.js → Modal-CCRvx959.js} +1 -1
- package/.output/public/assets/{SignIn-9vsaKv_p.js → SignIn-Qxw5zDFq.js} +1 -1
- package/.output/public/assets/{actionUi-DDDfD0WX.js → actionUi-CXxhzYe2.js} +1 -1
- package/.output/public/assets/app-D1cij_NJ.css +2 -0
- package/.output/public/assets/{artifactView-BV-yQVrj.js → artifactView-Cd5wWPil.js} +1 -1
- package/.output/public/assets/index-BRN_CQ8U.js +12 -0
- package/.output/public/assets/{invite._token-5zhVSA26.js → invite._token-BIxr8DlP.js} +1 -1
- package/.output/public/assets/{invite._token-C3t4JfAY.js → invite._token-j2BTCeG1.js} +1 -1
- package/.output/public/assets/lifecycleUi-Qy_8beQK.js +1 -0
- package/.output/public/assets/{loopApi-C_Dtw-nL.js → loopApi-CT1mMhlb.js} +1 -1
- package/.output/public/assets/loops._loopId-BRBQzioy.js +4 -0
- package/.output/public/assets/loops._loopId_.runs._runId-CMnOJkem.js +3 -0
- package/.output/public/assets/{notifyFns-CF12Yboc.js → notifyFns-DYD4EKaG.js} +1 -1
- package/.output/public/assets/{preload-helper--VCqDT5m.js → preload-helper-DFkkpP6W.js} +1 -1
- package/.output/public/assets/{routes-DsdcURfk.js → routes-3nuEJGdo.js} +1 -1
- package/.output/public/assets/{routes-WVPuW976.js → routes-DORLim-C.js} +1 -1
- package/.output/public/assets/{t._teamId-BdVLnwih.js → t._teamId-F5f4K8kd.js} +1 -1
- package/.output/public/assets/{t._teamId-C9uEyAh_.js → t._teamId-XfE_S1Fo.js} +1 -1
- package/.output/public/assets/ui-Dhsx6SW0.js +1 -0
- package/.output/server/_libs/d3-shape.mjs +130 -1
- package/.output/server/_libs/react-is.mjs +48 -0
- package/.output/server/_libs/recharts+victory-vendor.mjs +2210 -782
- package/.output/server/_ssr/{DashboardView-DJMpoucm.mjs → DashboardView-BF7PciP7.mjs} +11 -8
- package/.output/server/_ssr/{LoopPlaybook-WFNer22Y.mjs → LoopPlaybook-DC7QVvPY.mjs} +2 -2
- package/.output/server/_ssr/{LoopView-Dsc6oik9.mjs → LoopView-DwSrm9Ja.mjs} +418 -224
- package/.output/server/_ssr/{Modal-Bc_5TYGr.mjs → Modal-MaCG52Pv.mjs} +2 -2
- package/.output/server/_ssr/{SignIn-2qr-vtR5.mjs → SignIn-RmpWm9uy.mjs} +3 -3
- package/.output/server/_ssr/{actionUi-BKF6xI3d.mjs → actionUi-BN55TNYE.mjs} +2 -2
- package/.output/server/_ssr/{adapters-Bwuh6uek.mjs → adapters-BoLkOf1T.mjs} +12 -6
- package/.output/server/_ssr/{artifactFiles-Cp1IBHne.mjs → artifactFiles-BCaIIrrT.mjs} +3 -3
- package/.output/server/_ssr/{artifactView-DX4P3Krh.mjs → artifactView-iuPsp5aa.mjs} +2 -2
- package/.output/server/_ssr/artifacts-jQ-bfdhI.mjs +2 -0
- package/.output/server/_ssr/{auth-uQ19WsD1.mjs → auth-30Z4lV1w.mjs} +9 -9
- package/.output/server/_ssr/{boot-DPoPmSLh.mjs → boot-3BNKujyp.mjs} +392 -193
- package/.output/server/_ssr/boot-CsEFT53K.mjs +2 -0
- package/.output/server/_ssr/{bun-sqlite-dialect-BW9W1_Ps-CqcLCApi.mjs → bun-sqlite-dialect-BW9W1_Ps-_7tfuNRi.mjs} +2 -2
- package/.output/server/_ssr/{d1-sqlite-dialect-BLC8LXE6-AuWb61y8.mjs → d1-sqlite-dialect-BLC8LXE6-ucDUbeBk.mjs} +2 -2
- package/.output/server/_ssr/db-CXownd1H.mjs +2 -0
- package/.output/server/_ssr/{dist-CtYTvPCD.mjs → dist-Cd6nlLlO.mjs} +2 -2
- package/.output/server/_ssr/{factory-BtqxMS8a.mjs → factory-CWWwV7XZ.mjs} +2 -2
- package/.output/server/_ssr/{invite._token-DtAfQyfP.mjs → invite._token-BKlz0R2f.mjs} +5 -5
- package/.output/server/_ssr/{invite._token-BlbVjz-o.mjs → invite._token-CWl6kipD.mjs} +2 -2
- package/.output/server/_ssr/{invite._token-DBttmaDb.mjs → invite._token-SdWXemIc.mjs} +6 -6
- package/.output/server/_ssr/{kysely-adapter-bSpTlW7Z.mjs → kysely-adapter-C8iS51DB.mjs} +1 -1
- package/.output/server/_ssr/{lifecycleUi-Djb44Aj3.mjs → lifecycleUi-DgBQAYzp.mjs} +2 -1
- package/.output/server/_ssr/logger-Gy-47z3k.mjs +2 -0
- package/.output/server/_ssr/{loopApi-7MTSs6hy.mjs → loopApi-8_KgBapr.mjs} +6 -2
- package/.output/server/_ssr/{loopApi-BXRBFGWh.mjs → loopApi-ABi9SgWS.mjs} +23 -13
- package/.output/server/_ssr/{loops._loopId-6Q0DMXU0.mjs → loops._loopId-SON_t43g.mjs} +4 -4
- package/.output/server/_ssr/{loops._loopId-C0x0SE8F.mjs → loops._loopId-p1KWsvMM.mjs} +13 -18
- package/.output/server/_ssr/{loops._loopId_.runs._runId-CkM0-ckn.mjs → loops._loopId_.runs._runId-PUGBrD5q.mjs} +22 -16
- package/.output/server/_ssr/{loops._loopId_.runs._runId-BOJBJu_a.mjs → loops._loopId_.runs._runId-Ymy4lwee.mjs} +4 -4
- package/.output/server/_ssr/{machineFns-CQuvvdXx.mjs → machineFns-BZ9puxDt.mjs} +3 -3
- package/.output/server/_ssr/{node-sqlite-dialect-ZOyD6D7F.mjs → node-sqlite-dialect-D_3JmkMo.mjs} +2 -2
- package/.output/server/_ssr/{notifyFns-BOeYZDRp.mjs → notifyFns-BUCePuF3.mjs} +3 -3
- package/.output/server/_ssr/{notifyFns-UFZ2j7Fv.mjs → notifyFns-DuPR1zd7.mjs} +6 -6
- package/.output/server/_ssr/{router-jnPelPNh.mjs → router-Czxvra6l.mjs} +44 -37
- package/.output/server/_ssr/{routes-CXnbFjB_.mjs → routes-ClZEdsnB.mjs} +6 -6
- package/.output/server/_ssr/{routes-Bx0bRxt9.mjs → routes-ComqHvCL.mjs} +2 -2
- package/.output/server/_ssr/{routes-CMAUcUsZ.mjs → routes-D5jJJ-30.mjs} +5 -5
- package/.output/server/_ssr/runDiff--qLtU4FI.mjs +2 -0
- package/.output/server/_ssr/ssr.mjs +52 -48
- package/.output/server/_ssr/store-40eVzW1S.mjs +2 -0
- package/.output/server/_ssr/{t._teamId-Bsm4WrJl.mjs → t._teamId-BgHV_KFP.mjs} +6 -6
- package/.output/server/_ssr/{t._teamId-D9nCEa2h.mjs → t._teamId-DDERNgfn.mjs} +2 -2
- package/.output/server/_ssr/{t._teamId-36z-mmyw.mjs → t._teamId-DRJvKHZn.mjs} +5 -5
- package/.output/server/_ssr/{teamFns-DAx4a1z4.mjs → teamFns-Bp81UO-x.mjs} +3 -3
- package/.output/server/_ssr/{teamFns-BBIbatdZ.mjs → teamFns-bm82twd5.mjs} +2 -2
- package/.output/server/_ssr/tokens-CbfoRC_s.mjs +2 -0
- package/.output/server/_ssr/{ui-B6mE5-5v.mjs → ui-Ww2tTM2u.mjs} +9 -2
- package/.output/server/{_tanstack-start-manifest_v-5iQOchGB.mjs → _tanstack-start-manifest_v-DwKENYCN.mjs} +28 -28
- package/.output/server/index.mjs +114 -114
- package/package.json +1 -1
- package/.output/public/assets/LoopView-Pbp2YtUu.js +0 -55
- package/.output/public/assets/app-CStTBRt2.css +0 -2
- package/.output/public/assets/index-DT_reRxT.js +0 -12
- package/.output/public/assets/lifecycleUi-CMFw5Nlj.js +0 -1
- package/.output/public/assets/loops._loopId-6JlPOCsY.js +0 -4
- package/.output/public/assets/loops._loopId_.runs._runId-wFUMHN9w.js +0 -3
- package/.output/public/assets/ui-Gb1413-F.js +0 -1
- package/.output/server/_ssr/artifacts-9G_Ai7vO.mjs +0 -2
- package/.output/server/_ssr/boot-Zz5AJgfy.mjs +0 -2
- package/.output/server/_ssr/db-BMANvbZu.mjs +0 -2
- package/.output/server/_ssr/logger-CR1a0flR.mjs +0 -2
- package/.output/server/_ssr/runDiff-CDPvl_cY.mjs +0 -2
- package/.output/server/_ssr/store-DDOk2Ty_.mjs +0 -2
- package/.output/server/_ssr/tokens-7KEKpLnu.mjs +0 -2
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import { o as __toESM } from "../_runtime.mjs";
|
|
2
2
|
import { g as require_jsx_runtime, h as require_react } from "../_libs/@base-ui/react+[...].mjs";
|
|
3
|
-
import {
|
|
4
|
-
import { A as tsShort, v as fmt, w as md, y as fnum } from "./ui-
|
|
5
|
-
import { s as getArtifacts } from "./loopApi-
|
|
3
|
+
import { Bt as parseChartSpec, _t as isTaskPath, c as CHART_ATTRS } from "./boot-3BNKujyp.mjs";
|
|
4
|
+
import { A as tsShort, v as fmt, w as md, y as fnum } from "./ui-Ww2tTM2u.mjs";
|
|
5
|
+
import { l as getChartRuns, s as getArtifacts } from "./loopApi-8_KgBapr.mjs";
|
|
6
6
|
import { t as artifactKind } from "./artifactKind-BW0isV8L.mjs";
|
|
7
7
|
import { t as purify } from "../_libs/dompurify.mjs";
|
|
8
|
-
import { i as isMarkdown, r as ViewerHead, t as ArtifactBody } from "./artifactView-
|
|
9
|
-
import { n as ModalHead, t as Modal } from "./Modal-
|
|
8
|
+
import { i as isMarkdown, r as ViewerHead, t as ArtifactBody } from "./artifactView-iuPsp5aa.mjs";
|
|
9
|
+
import { n as ModalHead, t as Modal } from "./Modal-MaCG52Pv.mjs";
|
|
10
10
|
import { n as import_lib, t as esm_default } from "../_libs/html-react-parser+[...].mjs";
|
|
11
|
-
import { a as
|
|
12
|
-
//#region node_modules/.nitro/vite/services/ssr/assets/LoopView-
|
|
11
|
+
import { a as Scatter, c as CartesianGrid, d as ResponsiveContainer, i as XAxis, l as Tooltip, n as ScatterChart, o as Area, r as YAxis, s as Line, t as ComposedChart, u as Legend } from "../_libs/recharts+victory-vendor.mjs";
|
|
12
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/LoopView-DwSrm9Ja.js
|
|
13
13
|
var import_react = /* @__PURE__ */ __toESM(require_react());
|
|
14
14
|
var import_jsx_runtime = require_jsx_runtime();
|
|
15
|
-
/** Parse a `<loop-chart series="key:label:unit, ...">` attribute into MetricField[]. */
|
|
16
|
-
function parseSeries(attr) {
|
|
17
|
-
if (!attr) return [];
|
|
18
|
-
return attr.split(",").map((s) => {
|
|
19
|
-
const [key, label, unit] = s.split(":").map((x) => x.trim());
|
|
20
|
-
return {
|
|
21
|
-
key: key ?? "",
|
|
22
|
-
...label ? { label } : {},
|
|
23
|
-
...unit ? { unit } : {}
|
|
24
|
-
};
|
|
25
|
-
}).filter((f) => f.key);
|
|
26
|
-
}
|
|
27
15
|
function buildBindingContext(runs) {
|
|
28
16
|
const latest = {};
|
|
29
17
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -53,49 +41,72 @@ function lookup(ctx, path) {
|
|
|
53
41
|
const parts = path.split(".");
|
|
54
42
|
if (parts[0] === "latest" && parts[1]) return ctx.latest[parts[1]];
|
|
55
43
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
44
|
+
var finiteMetric = (run, key) => {
|
|
45
|
+
const value = run.metrics?.[key];
|
|
46
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
47
|
+
};
|
|
48
|
+
var chronological = (runs) => runs.slice().sort((a, b) => a.runIndex - b.runIndex);
|
|
49
|
+
function seriesChartRows(runs, keys, x) {
|
|
50
|
+
return chronological(runs).flatMap((run) => {
|
|
51
|
+
const values = keys.map((key) => [key, finiteMetric(run, key)]);
|
|
52
|
+
if (!values.some(([, value]) => value != null)) return [];
|
|
53
|
+
const row = {
|
|
54
|
+
__x: x === "time" ? run.ts : run.runIndex,
|
|
55
|
+
__runIndex: run.runIndex,
|
|
56
|
+
__status: run.status
|
|
57
|
+
};
|
|
58
|
+
for (const [key, value] of values) if (value != null) row[key] = value;
|
|
59
|
+
return [row];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function scatterChartRows(runs, xKey, yKey) {
|
|
63
|
+
return chronological(runs).flatMap((run) => {
|
|
64
|
+
const x = finiteMetric(run, xKey);
|
|
65
|
+
const y = finiteMetric(run, yKey);
|
|
66
|
+
return x == null || y == null ? [] : [{
|
|
67
|
+
x,
|
|
68
|
+
y,
|
|
69
|
+
runIndex: run.runIndex,
|
|
70
|
+
status: run.status,
|
|
71
|
+
ts: run.ts
|
|
72
|
+
}];
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function progressChartRows(runs, yKey) {
|
|
76
|
+
return chronological(runs).flatMap((run) => {
|
|
77
|
+
const y = finiteMetric(run, yKey);
|
|
78
|
+
return y == null ? [] : [{
|
|
79
|
+
x: run.runIndex,
|
|
80
|
+
y,
|
|
81
|
+
runIndex: run.runIndex,
|
|
82
|
+
status: run.status,
|
|
83
|
+
ts: run.ts
|
|
84
|
+
}];
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** Running best follows persisted kept decisions only. There is no renderer-side keep threshold. */
|
|
88
|
+
function runningBest(rows, direction) {
|
|
89
|
+
let best = null;
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const row of rows) {
|
|
92
|
+
if (row.status === "kept") best = best == null ? row.y : direction === "min" ? Math.min(best, row.y) : Math.max(best, row.y);
|
|
93
|
+
if (best != null) out.push({
|
|
94
|
+
x: row.x,
|
|
95
|
+
y: best
|
|
96
|
+
});
|
|
69
97
|
}
|
|
70
|
-
return
|
|
98
|
+
return out;
|
|
71
99
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
const sorted = [...ts].sort();
|
|
82
|
-
const idx = new Map(sorted.map((t, i) => [t, i]));
|
|
83
|
-
const rows = sorted.map((t) => ({ __t: t }));
|
|
84
|
-
for (const k of keys) for (const p of data[k] ?? []) rows[idx.get(p.t)][k] = p.v;
|
|
85
|
-
return rows;
|
|
100
|
+
function chartDomain(values, requested) {
|
|
101
|
+
if (Array.isArray(requested)) return requested;
|
|
102
|
+
if (!values.length) return ["auto", "auto"];
|
|
103
|
+
const min = Math.min(...values);
|
|
104
|
+
const max = Math.max(...values);
|
|
105
|
+
if (requested === "zero") return [Math.min(0, min), Math.max(0, max)];
|
|
106
|
+
const span = max - min;
|
|
107
|
+
const pad = span > 0 ? span * .1 : Math.max(Math.abs(max) * .05, 1);
|
|
108
|
+
return [min - pad, max + pad];
|
|
86
109
|
}
|
|
87
|
-
/**
|
|
88
|
-
* `<loop-chart series="mrr:MRR:$, paid:Paid">` - the loop's run-history trend,
|
|
89
|
-
* rendered by Recharts in the shadcn/ui-charts grammar, themed entirely by the
|
|
90
|
-
* theme tokens (`--color-chart-1..5` ramp, hairline grid, mono ticks,
|
|
91
|
-
* flat tooltip - no shadows, no decorative color). One series renders as a
|
|
92
|
-
* gradient area; multiple series render as plain lines (overlapping translucent
|
|
93
|
-
* fills go muddy in a monochrome palette). A single-point series renders a dot
|
|
94
|
-
* instead of nothing, so a young loop's dashboard is never blank.
|
|
95
|
-
*
|
|
96
|
-
* `data` is the shared `numericSeries(runs)` map computed once by LoopView.
|
|
97
|
-
*/
|
|
98
|
-
/** Distinct strokes for multiple series - display ink first, then the signal colors. */
|
|
99
110
|
var STROKES = [
|
|
100
111
|
"var(--color-chart-1)",
|
|
101
112
|
"var(--color-chart-2)",
|
|
@@ -103,13 +114,6 @@ var STROKES = [
|
|
|
103
114
|
"var(--color-chart-4)",
|
|
104
115
|
"var(--color-chart-5)"
|
|
105
116
|
];
|
|
106
|
-
/**
|
|
107
|
-
* Fixed pixel height: ResponsiveContainer tracks the CONTAINER width while the
|
|
108
|
-
* height stays constant - the fix for the old stretched-viewBox renderer, which
|
|
109
|
-
* scaled like an image (≈4px-fat strokes and a ~340px-tall chart at the loop
|
|
110
|
-
* page's full dashboard width). Also the initial render size before the
|
|
111
|
-
* ResizeObserver measures (and the only size in a non-browser render).
|
|
112
|
-
*/
|
|
113
117
|
var HEIGHT = 190;
|
|
114
118
|
var INITIAL = {
|
|
115
119
|
width: 640,
|
|
@@ -129,163 +133,327 @@ var TOOLTIP_STYLE = {
|
|
|
129
133
|
fontFamily: "var(--font-mono)",
|
|
130
134
|
fontSize: 11
|
|
131
135
|
};
|
|
132
|
-
|
|
133
|
-
var
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const rows = seriesRows(data, plotted.map((s) => s.field.key));
|
|
143
|
-
const single = plotted.length === 1;
|
|
144
|
-
const unitOf = new Map(plotted.map((s) => [s.field.key, s.field.unit ?? ""]));
|
|
145
|
-
const xAxis = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
|
|
146
|
-
dataKey: "__t",
|
|
147
|
-
tickLine: false,
|
|
148
|
-
axisLine: false,
|
|
149
|
-
tickMargin: 8,
|
|
150
|
-
minTickGap: 28,
|
|
151
|
-
interval: "preserveStartEnd",
|
|
152
|
-
tick: TICK,
|
|
153
|
-
tickFormatter: (t) => md(t)
|
|
154
|
-
});
|
|
155
|
-
const yAxis = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
|
|
156
|
-
width: 34,
|
|
157
|
-
tickCount: 4,
|
|
158
|
-
tickLine: false,
|
|
159
|
-
axisLine: false,
|
|
160
|
-
tick: TICK,
|
|
161
|
-
tickFormatter: (v) => fnum(v)
|
|
162
|
-
});
|
|
163
|
-
const grid = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, {
|
|
164
|
-
vertical: false,
|
|
165
|
-
stroke: "var(--color-hairline)"
|
|
166
|
-
});
|
|
167
|
-
const tooltip = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip, {
|
|
168
|
-
isAnimationActive: false,
|
|
169
|
-
cursor: {
|
|
170
|
-
stroke: "var(--color-wire)",
|
|
171
|
-
strokeDasharray: "3 3"
|
|
172
|
-
},
|
|
173
|
-
contentStyle: TOOLTIP_STYLE,
|
|
174
|
-
labelStyle: {
|
|
175
|
-
color: "var(--color-secondary)",
|
|
176
|
-
marginBottom: 2
|
|
177
|
-
},
|
|
178
|
-
itemStyle: {
|
|
179
|
-
color: "var(--color-primary)",
|
|
180
|
-
padding: "1px 0"
|
|
181
|
-
},
|
|
182
|
-
labelFormatter: (t) => tsShort(String(t)),
|
|
183
|
-
formatter: (v, name, item) => [withUnit(Number(v), unitOf.get(String(item?.dataKey)) ?? ""), String(name)]
|
|
184
|
-
});
|
|
185
|
-
const seriesProps = (s, idx) => {
|
|
186
|
-
const color = STROKES[idx % STROKES.length];
|
|
187
|
-
return {
|
|
188
|
-
dataKey: s.field.key,
|
|
189
|
-
name: s.field.label ?? s.field.key,
|
|
190
|
-
type: "monotone",
|
|
191
|
-
stroke: color,
|
|
192
|
-
strokeWidth: 1.5,
|
|
193
|
-
dot: s.pts.length === 1 ? {
|
|
194
|
-
r: 3.5,
|
|
195
|
-
strokeWidth: 0,
|
|
196
|
-
fill: color
|
|
197
|
-
} : false,
|
|
198
|
-
activeDot: {
|
|
199
|
-
r: 3,
|
|
200
|
-
strokeWidth: 0,
|
|
201
|
-
fill: color
|
|
202
|
-
},
|
|
203
|
-
connectNulls: true,
|
|
204
|
-
isAnimationActive: false
|
|
205
|
-
};
|
|
206
|
-
};
|
|
207
|
-
const margin = {
|
|
208
|
-
top: 6,
|
|
209
|
-
right: 12,
|
|
210
|
-
left: 0,
|
|
211
|
-
bottom: 0
|
|
212
|
-
};
|
|
136
|
+
var withUnit = (value, unit = "") => unit === "$" ? `$${fnum(value)}` : `${fnum(value)}${unit}`;
|
|
137
|
+
var metricValues = (runs, keys) => runs.flatMap((run) => keys.map((key) => finiteMetric(run, key)).filter((value) => value != null));
|
|
138
|
+
var axisDomain = (runs, keys, spec) => chartDomain(metricValues(runs, keys), spec.yDomain);
|
|
139
|
+
var axisValue = (value, domain) => {
|
|
140
|
+
if (typeof domain[0] !== "number" || typeof domain[1] !== "number") return fnum(value);
|
|
141
|
+
const span = Math.abs(domain[1] - domain[0]);
|
|
142
|
+
const digits = span < .001 ? 6 : span < .01 ? 5 : span < .1 ? 4 : span < 1 ? 3 : span < 10 ? 2 : span < 100 ? 1 : 0;
|
|
143
|
+
return value.toFixed(digits);
|
|
144
|
+
};
|
|
145
|
+
function ChartFrame({ children, caption }) {
|
|
213
146
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("figure", {
|
|
214
147
|
className: "my-2 min-w-0",
|
|
215
148
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
216
149
|
className: "min-w-0",
|
|
217
150
|
style: { height: HEIGHT },
|
|
218
|
-
children
|
|
219
|
-
|
|
220
|
-
height: "100%",
|
|
221
|
-
initialDimension: INITIAL,
|
|
222
|
-
children: single ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AreaChart, {
|
|
223
|
-
data: rows,
|
|
224
|
-
margin,
|
|
225
|
-
children: [
|
|
226
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", {
|
|
227
|
-
id: gradientId,
|
|
228
|
-
x1: "0",
|
|
229
|
-
y1: "0",
|
|
230
|
-
x2: "0",
|
|
231
|
-
y2: "1",
|
|
232
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", {
|
|
233
|
-
offset: "5%",
|
|
234
|
-
stopColor: STROKES[0],
|
|
235
|
-
stopOpacity: .28
|
|
236
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", {
|
|
237
|
-
offset: "95%",
|
|
238
|
-
stopColor: STROKES[0],
|
|
239
|
-
stopOpacity: .02
|
|
240
|
-
})]
|
|
241
|
-
}) }),
|
|
242
|
-
grid,
|
|
243
|
-
xAxis,
|
|
244
|
-
yAxis,
|
|
245
|
-
tooltip,
|
|
246
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Area, {
|
|
247
|
-
...seriesProps(plotted[0], 0),
|
|
248
|
-
fill: `url(#${gradientId})`
|
|
249
|
-
})
|
|
250
|
-
]
|
|
251
|
-
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(LineChart, {
|
|
252
|
-
data: rows,
|
|
253
|
-
margin,
|
|
254
|
-
children: [
|
|
255
|
-
grid,
|
|
256
|
-
xAxis,
|
|
257
|
-
yAxis,
|
|
258
|
-
tooltip,
|
|
259
|
-
plotted.map((s, idx) => /* @__PURE__ */ (0, import_react.createElement)(Line, {
|
|
260
|
-
...seriesProps(s, idx),
|
|
261
|
-
key: s.field.key
|
|
262
|
-
}))
|
|
263
|
-
]
|
|
264
|
-
})
|
|
265
|
-
})
|
|
266
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("figcaption", {
|
|
151
|
+
children
|
|
152
|
+
}), caption ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("figcaption", {
|
|
267
153
|
className: "mt-1 flex flex-wrap gap-x-4 gap-y-1 text-label text-[var(--color-secondary)]",
|
|
268
|
-
children:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
154
|
+
children: caption
|
|
155
|
+
}) : null]
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function LoopChart({ runs, spec }) {
|
|
159
|
+
if (spec.type === "scatter") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScatterPlot, {
|
|
160
|
+
runs,
|
|
161
|
+
spec
|
|
162
|
+
});
|
|
163
|
+
if (spec.type === "progress") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProgressPlot, {
|
|
164
|
+
runs,
|
|
165
|
+
spec
|
|
166
|
+
});
|
|
167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SeriesPlot, {
|
|
168
|
+
runs,
|
|
169
|
+
spec
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function SeriesPlot({ runs, spec }) {
|
|
173
|
+
const gradientId = (0, import_react.useId)();
|
|
174
|
+
const keys = spec.series.map((field) => field.key);
|
|
175
|
+
const rows = seriesChartRows(runs, keys, spec.x);
|
|
176
|
+
if (!rows.length) return null;
|
|
177
|
+
const units = new Map(spec.series.map((field) => [field.key, field.unit ?? ""]));
|
|
178
|
+
const domain = axisDomain(runs, keys, spec);
|
|
179
|
+
const latest = /* @__PURE__ */ new Map();
|
|
180
|
+
for (const row of rows) for (const key of keys) if (typeof row[key] === "number") latest.set(key, row[key]);
|
|
181
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartFrame, {
|
|
182
|
+
caption: spec.series.map((field, index) => {
|
|
183
|
+
const value = latest.get(field.key);
|
|
184
|
+
return value == null ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
185
|
+
className: "inline-flex items-center gap-1.5",
|
|
186
|
+
children: [
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
188
|
+
className: "inline-block h-[2px] w-3",
|
|
189
|
+
style: { background: STROKES[index % STROKES.length] }
|
|
190
|
+
}),
|
|
191
|
+
field.label ?? field.key,
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
193
|
+
className: "text-[var(--color-display)]",
|
|
194
|
+
children: withUnit(value, field.unit)
|
|
195
|
+
})
|
|
196
|
+
]
|
|
197
|
+
}, field.key);
|
|
198
|
+
}),
|
|
199
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResponsiveContainer, {
|
|
200
|
+
width: "100%",
|
|
201
|
+
height: "100%",
|
|
202
|
+
initialDimension: INITIAL,
|
|
203
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComposedChart, {
|
|
204
|
+
data: rows,
|
|
205
|
+
margin: {
|
|
206
|
+
top: 6,
|
|
207
|
+
right: 12,
|
|
208
|
+
left: 0,
|
|
209
|
+
bottom: 0
|
|
210
|
+
},
|
|
211
|
+
children: [
|
|
212
|
+
spec.type === "area" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("linearGradient", {
|
|
213
|
+
id: gradientId,
|
|
214
|
+
x1: "0",
|
|
215
|
+
y1: "0",
|
|
216
|
+
x2: "0",
|
|
217
|
+
y2: "1",
|
|
218
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", {
|
|
219
|
+
offset: "5%",
|
|
220
|
+
stopColor: STROKES[0],
|
|
221
|
+
stopOpacity: .28
|
|
222
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("stop", {
|
|
223
|
+
offset: "95%",
|
|
224
|
+
stopColor: STROKES[0],
|
|
225
|
+
stopOpacity: .02
|
|
226
|
+
})]
|
|
227
|
+
}) }) : null,
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, {
|
|
229
|
+
vertical: false,
|
|
230
|
+
stroke: "var(--color-hairline)"
|
|
231
|
+
}),
|
|
232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
|
|
233
|
+
dataKey: "__x",
|
|
234
|
+
type: spec.x === "runIndex" ? "number" : "category",
|
|
235
|
+
domain: spec.x === "runIndex" ? ["dataMin", "dataMax"] : void 0,
|
|
236
|
+
tick: TICK,
|
|
237
|
+
tickLine: false,
|
|
238
|
+
axisLine: false,
|
|
239
|
+
tickMargin: 8,
|
|
240
|
+
minTickGap: 28,
|
|
241
|
+
tickFormatter: (value) => spec.x === "time" ? md(String(value)) : String(value)
|
|
242
|
+
}),
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
|
|
244
|
+
width: 60,
|
|
245
|
+
domain,
|
|
246
|
+
allowDataOverflow: Array.isArray(spec.yDomain),
|
|
247
|
+
tick: TICK,
|
|
248
|
+
tickLine: false,
|
|
249
|
+
axisLine: false,
|
|
250
|
+
tickFormatter: (value) => axisValue(value, domain)
|
|
251
|
+
}),
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip, {
|
|
253
|
+
isAnimationActive: false,
|
|
254
|
+
contentStyle: TOOLTIP_STYLE,
|
|
255
|
+
labelFormatter: (value) => spec.x === "time" ? tsShort(String(value)) : `run #${value}`,
|
|
256
|
+
formatter: (value, name, item) => [withUnit(Number(value), units.get(String(item?.dataKey))), String(name)]
|
|
257
|
+
}),
|
|
258
|
+
spec.type === "area" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Area, {
|
|
259
|
+
...seriesProps(spec.series[0], 0),
|
|
260
|
+
connectNulls: true,
|
|
261
|
+
dot: rows.length === 1 ? {
|
|
262
|
+
r: 3.5,
|
|
263
|
+
strokeWidth: 0
|
|
264
|
+
} : false,
|
|
265
|
+
fill: `url(#${gradientId})`
|
|
266
|
+
}) : spec.series.map((field, index) => /* @__PURE__ */ (0, import_react.createElement)(Line, {
|
|
267
|
+
...seriesProps(field, index),
|
|
268
|
+
key: field.key,
|
|
269
|
+
connectNulls: true,
|
|
270
|
+
dot: spec.colorBy === "status" ? statusDot(field.key) : rows.length === 1 ? {
|
|
271
|
+
r: 3.5,
|
|
272
|
+
strokeWidth: 0
|
|
273
|
+
} : false
|
|
274
|
+
}))
|
|
275
|
+
]
|
|
285
276
|
})
|
|
286
|
-
})
|
|
277
|
+
})
|
|
287
278
|
});
|
|
288
279
|
}
|
|
280
|
+
function ScatterPlot({ runs, spec }) {
|
|
281
|
+
const rows = scatterChartRows(runs, spec.xKey, spec.yKey);
|
|
282
|
+
if (!rows.length) return null;
|
|
283
|
+
const domain = chartDomain(rows.map((row) => row.y), spec.yDomain);
|
|
284
|
+
const xDomain = chartDomain(rows.map((row) => row.x), "auto");
|
|
285
|
+
const kept = rows.filter((row) => row.status === "kept");
|
|
286
|
+
const other = rows.filter((row) => row.status !== "kept");
|
|
287
|
+
const groups = spec.colorBy === "status" ? [{
|
|
288
|
+
name: "Other",
|
|
289
|
+
rows: other,
|
|
290
|
+
fill: "var(--color-disabled)"
|
|
291
|
+
}, {
|
|
292
|
+
name: "Kept",
|
|
293
|
+
rows: kept,
|
|
294
|
+
fill: "var(--color-success)"
|
|
295
|
+
}] : [{
|
|
296
|
+
name: spec.yLabel ?? spec.yKey,
|
|
297
|
+
rows,
|
|
298
|
+
fill: STROKES[0]
|
|
299
|
+
}];
|
|
300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartFrame, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResponsiveContainer, {
|
|
301
|
+
width: "100%",
|
|
302
|
+
height: "100%",
|
|
303
|
+
initialDimension: INITIAL,
|
|
304
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ScatterChart, {
|
|
305
|
+
margin: {
|
|
306
|
+
top: 8,
|
|
307
|
+
right: 12,
|
|
308
|
+
left: 0,
|
|
309
|
+
bottom: 8
|
|
310
|
+
},
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, { stroke: "var(--color-hairline)" }),
|
|
313
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
|
|
314
|
+
type: "number",
|
|
315
|
+
dataKey: "x",
|
|
316
|
+
name: spec.xLabel ?? spec.xKey,
|
|
317
|
+
domain: xDomain,
|
|
318
|
+
tickFormatter: (value) => axisValue(value, xDomain),
|
|
319
|
+
tick: TICK,
|
|
320
|
+
tickLine: false,
|
|
321
|
+
axisLine: false
|
|
322
|
+
}),
|
|
323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
|
|
324
|
+
type: "number",
|
|
325
|
+
dataKey: "y",
|
|
326
|
+
name: spec.yLabel ?? spec.yKey,
|
|
327
|
+
domain,
|
|
328
|
+
allowDataOverflow: Array.isArray(spec.yDomain),
|
|
329
|
+
width: 60,
|
|
330
|
+
tickFormatter: (value) => axisValue(value, domain),
|
|
331
|
+
tick: TICK,
|
|
332
|
+
tickLine: false,
|
|
333
|
+
axisLine: false
|
|
334
|
+
}),
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip, {
|
|
336
|
+
isAnimationActive: false,
|
|
337
|
+
contentStyle: TOOLTIP_STYLE,
|
|
338
|
+
cursor: {
|
|
339
|
+
stroke: "var(--color-wire)",
|
|
340
|
+
strokeDasharray: "3 3"
|
|
341
|
+
},
|
|
342
|
+
labelFormatter: (_, payload) => payload?.[0]?.payload ? `run #${payload[0].payload.runIndex}` : "",
|
|
343
|
+
formatter: (value, name, item) => [withUnit(Number(value), String(item?.dataKey) === "x" ? spec.xUnit : spec.yUnit), String(name)]
|
|
344
|
+
}),
|
|
345
|
+
groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scatter, {
|
|
346
|
+
name: group.name,
|
|
347
|
+
data: group.rows,
|
|
348
|
+
fill: group.fill,
|
|
349
|
+
isAnimationActive: false
|
|
350
|
+
}, group.name)),
|
|
351
|
+
spec.colorBy ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Legend, {
|
|
352
|
+
verticalAlign: "top",
|
|
353
|
+
align: "right"
|
|
354
|
+
}) : null
|
|
355
|
+
]
|
|
356
|
+
})
|
|
357
|
+
}) });
|
|
358
|
+
}
|
|
359
|
+
function ProgressPlot({ runs, spec }) {
|
|
360
|
+
const rows = progressChartRows(runs, spec.yKey);
|
|
361
|
+
if (!rows.length) return null;
|
|
362
|
+
const kept = rows.filter((row) => row.status === "kept");
|
|
363
|
+
const other = rows.filter((row) => row.status !== "kept");
|
|
364
|
+
const best = runningBest(rows, spec.direction);
|
|
365
|
+
const domain = chartDomain(rows.map((row) => row.y), spec.yDomain);
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChartFrame, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResponsiveContainer, {
|
|
367
|
+
width: "100%",
|
|
368
|
+
height: "100%",
|
|
369
|
+
initialDimension: INITIAL,
|
|
370
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ComposedChart, {
|
|
371
|
+
margin: {
|
|
372
|
+
top: 8,
|
|
373
|
+
right: 12,
|
|
374
|
+
left: 0,
|
|
375
|
+
bottom: 8
|
|
376
|
+
},
|
|
377
|
+
children: [
|
|
378
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CartesianGrid, {
|
|
379
|
+
vertical: false,
|
|
380
|
+
stroke: "var(--color-hairline)"
|
|
381
|
+
}),
|
|
382
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(XAxis, {
|
|
383
|
+
type: "number",
|
|
384
|
+
dataKey: "x",
|
|
385
|
+
domain: ["dataMin", "dataMax"],
|
|
386
|
+
tick: TICK,
|
|
387
|
+
tickLine: false,
|
|
388
|
+
axisLine: false
|
|
389
|
+
}),
|
|
390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(YAxis, {
|
|
391
|
+
type: "number",
|
|
392
|
+
dataKey: "y",
|
|
393
|
+
domain,
|
|
394
|
+
allowDataOverflow: Array.isArray(spec.yDomain),
|
|
395
|
+
width: 60,
|
|
396
|
+
tickFormatter: (value) => axisValue(value, domain),
|
|
397
|
+
tick: TICK,
|
|
398
|
+
tickLine: false,
|
|
399
|
+
axisLine: false
|
|
400
|
+
}),
|
|
401
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip, {
|
|
402
|
+
isAnimationActive: false,
|
|
403
|
+
contentStyle: TOOLTIP_STYLE,
|
|
404
|
+
labelFormatter: (_, payload) => payload?.[0]?.payload ? `run #${payload[0].payload.runIndex ?? payload[0].payload.x}` : ""
|
|
405
|
+
}),
|
|
406
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scatter, {
|
|
407
|
+
name: "Other",
|
|
408
|
+
data: other,
|
|
409
|
+
fill: "var(--color-disabled)",
|
|
410
|
+
isAnimationActive: false
|
|
411
|
+
}),
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scatter, {
|
|
413
|
+
name: "Kept",
|
|
414
|
+
data: kept,
|
|
415
|
+
fill: "var(--color-success)",
|
|
416
|
+
isAnimationActive: false
|
|
417
|
+
}),
|
|
418
|
+
best.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Line, {
|
|
419
|
+
name: "Running best",
|
|
420
|
+
data: best,
|
|
421
|
+
dataKey: "y",
|
|
422
|
+
type: "stepAfter",
|
|
423
|
+
stroke: "var(--color-success)",
|
|
424
|
+
strokeWidth: 2,
|
|
425
|
+
dot: false,
|
|
426
|
+
isAnimationActive: false
|
|
427
|
+
}) : null,
|
|
428
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Legend, {
|
|
429
|
+
verticalAlign: "top",
|
|
430
|
+
align: "right"
|
|
431
|
+
})
|
|
432
|
+
]
|
|
433
|
+
})
|
|
434
|
+
}) });
|
|
435
|
+
}
|
|
436
|
+
var seriesProps = (field, index) => ({
|
|
437
|
+
dataKey: field.key,
|
|
438
|
+
name: field.label ?? field.key,
|
|
439
|
+
type: "linear",
|
|
440
|
+
stroke: STROKES[index % STROKES.length],
|
|
441
|
+
strokeWidth: 1.5,
|
|
442
|
+
activeDot: {
|
|
443
|
+
r: 3,
|
|
444
|
+
strokeWidth: 0
|
|
445
|
+
},
|
|
446
|
+
isAnimationActive: false
|
|
447
|
+
});
|
|
448
|
+
var statusDot = (key) => (props) => {
|
|
449
|
+
if (typeof props?.payload?.[key] !== "number") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", {});
|
|
450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", {
|
|
451
|
+
cx: props.cx,
|
|
452
|
+
cy: props.cy,
|
|
453
|
+
r: 2.8,
|
|
454
|
+
fill: props.payload.__status === "kept" ? "var(--color-success)" : "var(--color-disabled)"
|
|
455
|
+
});
|
|
456
|
+
};
|
|
289
457
|
var basename$2 = (path) => path.split("/").pop() || path;
|
|
290
458
|
var pad = (n) => String(n).padStart(2, "0");
|
|
291
459
|
/** A real calendar date within a sane range (no 2026-13-40, no year 10521). */
|
|
@@ -949,14 +1117,14 @@ function KanbanCard({ card, onOpen }) {
|
|
|
949
1117
|
* else (A/B panels, stat tiles, layout, text) is the agent's own HTML — there are
|
|
950
1118
|
* NO opinionated panel components.
|
|
951
1119
|
*
|
|
952
|
-
* <loop-chart series="mrr:MRR
|
|
1120
|
+
* <loop-chart type="line" x="runIndex" series="mrr:MRR:$"></loop-chart>
|
|
953
1121
|
* <loop-embed match="reports/digest-*.md"></loop-embed> newest matching artifact, embedded
|
|
954
1122
|
* <loop-calendar match="reports/*.md"></loop-calendar> month calendar of produced files
|
|
955
1123
|
* <loop-kanban columns="a,b,c" match="notes/*.md"> typed products as a board, columns = type
|
|
956
1124
|
*
|
|
957
1125
|
* Registering a new primitive means moving three things together: LOOP_TAGS +
|
|
958
1126
|
* the sanitizer config below, the parser swap in `options`, and the skill's
|
|
959
|
-
* authoring docs (references/
|
|
1127
|
+
* authoring docs (references/dashboard.md plus concise run-prompt examples) - the sanitizer
|
|
960
1128
|
* allowlist and the skill prose must never drift apart.
|
|
961
1129
|
*/
|
|
962
1130
|
var LOOP_TAGS = [
|
|
@@ -967,7 +1135,7 @@ var LOOP_TAGS = [
|
|
|
967
1135
|
];
|
|
968
1136
|
/** Data-bearing attributes on the loop-* primitives (all parsed by us, never markup). */
|
|
969
1137
|
var LOOP_ATTRS = [
|
|
970
|
-
|
|
1138
|
+
...CHART_ATTRS,
|
|
971
1139
|
"file",
|
|
972
1140
|
"match",
|
|
973
1141
|
"full",
|
|
@@ -1039,10 +1207,29 @@ function LoopView({ html, runs, loopId, taskFile }) {
|
|
|
1039
1207
|
const ctx = buildBindingContext(runs);
|
|
1040
1208
|
return purify.sanitize(resolveBindings(html, ctx), SANITIZE_CONFIG);
|
|
1041
1209
|
}, [html, runs]);
|
|
1042
|
-
const
|
|
1210
|
+
const wantsCharts = /<loop-chart\b/.test(clean);
|
|
1211
|
+
const [chartData, setChartData] = (0, import_react.useState)(null);
|
|
1212
|
+
const chartRuns = chartData?.loopId === loopId ? chartData.runs : null;
|
|
1043
1213
|
const wantsArtifacts = /<loop-(embed|calendar|kanban)\b/.test(clean);
|
|
1044
1214
|
const [artifacts, setArtifacts] = (0, import_react.useState)(null);
|
|
1045
1215
|
const newestRunId = runs[0]?.id;
|
|
1216
|
+
const newestRunLive = runs[0]?.running === true;
|
|
1217
|
+
(0, import_react.useEffect)(() => {
|
|
1218
|
+
if (!wantsCharts) return;
|
|
1219
|
+
let alive = true;
|
|
1220
|
+
getChartRuns({ data: { loopId } }).then((list) => alive && setChartData({
|
|
1221
|
+
loopId,
|
|
1222
|
+
runs: list
|
|
1223
|
+
})).catch(() => {});
|
|
1224
|
+
return () => {
|
|
1225
|
+
alive = false;
|
|
1226
|
+
};
|
|
1227
|
+
}, [
|
|
1228
|
+
wantsCharts,
|
|
1229
|
+
loopId,
|
|
1230
|
+
newestRunId,
|
|
1231
|
+
newestRunLive
|
|
1232
|
+
]);
|
|
1046
1233
|
(0, import_react.useEffect)(() => {
|
|
1047
1234
|
if (!wantsArtifacts) return;
|
|
1048
1235
|
let alive = true;
|
|
@@ -1062,17 +1249,24 @@ function LoopView({ html, runs, loopId, taskFile }) {
|
|
|
1062
1249
|
wantsArtifacts,
|
|
1063
1250
|
loopId,
|
|
1064
1251
|
newestRunId,
|
|
1065
|
-
|
|
1252
|
+
newestRunLive
|
|
1066
1253
|
]);
|
|
1067
1254
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
1068
1255
|
className: "loopview text-[14px] leading-relaxed",
|
|
1069
1256
|
children: esm_default(clean, (0, import_react.useMemo)(() => ({ replace: (node) => {
|
|
1070
1257
|
if (!(node instanceof import_lib.Element)) return void 0;
|
|
1071
1258
|
const a = node.attribs ?? {};
|
|
1072
|
-
if (node.name === "loop-chart")
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1259
|
+
if (node.name === "loop-chart") {
|
|
1260
|
+
const parsed = parseChartSpec(a);
|
|
1261
|
+
if (!parsed.ok) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
1262
|
+
className: "rounded-control border border-[var(--color-wire)] bg-[var(--color-raised)] p-3 text-meta text-[var(--color-secondary)]",
|
|
1263
|
+
children: "This chart uses an outdated or invalid configuration."
|
|
1264
|
+
});
|
|
1265
|
+
return chartRuns ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopChart, {
|
|
1266
|
+
runs: chartRuns,
|
|
1267
|
+
spec: parsed.value
|
|
1268
|
+
}) : null;
|
|
1269
|
+
}
|
|
1076
1270
|
if (node.name === "loop-embed") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoopEmbed, {
|
|
1077
1271
|
loopId,
|
|
1078
1272
|
artifacts,
|
|
@@ -1095,7 +1289,7 @@ function LoopView({ html, runs, loopId, taskFile }) {
|
|
|
1095
1289
|
taskFile
|
|
1096
1290
|
});
|
|
1097
1291
|
} }), [
|
|
1098
|
-
|
|
1292
|
+
chartRuns,
|
|
1099
1293
|
loopId,
|
|
1100
1294
|
taskFile,
|
|
1101
1295
|
artifacts
|