@burdenoff/microfe-cosmicintersection 2026.704.1 → 2026.704.3

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.
Files changed (59) hide show
  1. package/dist/cosmicintersection/CosmicIntersectionRoutes.js +45 -25
  2. package/dist/cosmicintersection/CosmicIntersectionRoutes.js.map +1 -1
  3. package/dist/cosmicintersection/api/cosmicApi.js +137 -66
  4. package/dist/cosmicintersection/api/cosmicApi.js.map +1 -1
  5. package/dist/cosmicintersection/components/ActivityFeedPanel.js +68 -0
  6. package/dist/cosmicintersection/components/ActivityFeedPanel.js.map +1 -0
  7. package/dist/cosmicintersection/components/DiscussionPanel.js +57 -0
  8. package/dist/cosmicintersection/components/DiscussionPanel.js.map +1 -0
  9. package/dist/cosmicintersection/components/FacetPanel.js +66 -0
  10. package/dist/cosmicintersection/components/FacetPanel.js.map +1 -0
  11. package/dist/cosmicintersection/components/LineageGraph.js +388 -0
  12. package/dist/cosmicintersection/components/LineageGraph.js.map +1 -0
  13. package/dist/cosmicintersection/components/ResultsChart.js +329 -0
  14. package/dist/cosmicintersection/components/ResultsChart.js.map +1 -0
  15. package/dist/cosmicintersection/components/StepDAG.js +104 -0
  16. package/dist/cosmicintersection/components/StepDAG.js.map +1 -0
  17. package/dist/cosmicintersection/components/index.js +5 -3
  18. package/dist/cosmicintersection/pages/access/AccessPage.js +220 -161
  19. package/dist/cosmicintersection/pages/access/AccessPage.js.map +1 -1
  20. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js +332 -172
  21. package/dist/cosmicintersection/pages/analyses/AnalysesPage.js.map +1 -1
  22. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js +167 -109
  23. package/dist/cosmicintersection/pages/collections/CollectionDetailPage.js.map +1 -1
  24. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js +87 -61
  25. package/dist/cosmicintersection/pages/collections/CollectionFormPage.js.map +1 -1
  26. package/dist/cosmicintersection/pages/collections/CollectionsPage.js +51 -42
  27. package/dist/cosmicintersection/pages/collections/CollectionsPage.js.map +1 -1
  28. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js +31 -18
  29. package/dist/cosmicintersection/pages/dashboard/DashboardPage.js.map +1 -1
  30. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js +330 -184
  31. package/dist/cosmicintersection/pages/datasets/DatasetDetailPage.js.map +1 -1
  32. package/dist/cosmicintersection/pages/datasets/DatasetFormPage.js +7 -7
  33. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js +321 -163
  34. package/dist/cosmicintersection/pages/datasets/DatasetsPage.js.map +1 -1
  35. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js +358 -0
  36. package/dist/cosmicintersection/pages/discovery/DiscoveryPage.js.map +1 -0
  37. package/dist/cosmicintersection/pages/lineage/LineagePage.js +171 -0
  38. package/dist/cosmicintersection/pages/lineage/LineagePage.js.map +1 -0
  39. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js +339 -260
  40. package/dist/cosmicintersection/pages/notebooks/NotebookDetailPage.js.map +1 -1
  41. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js +143 -82
  42. package/dist/cosmicintersection/pages/notebooks/NotebooksPage.js.map +1 -1
  43. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js +286 -142
  44. package/dist/cosmicintersection/pages/pipelines/PipelineDetailPage.js.map +1 -1
  45. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js +305 -103
  46. package/dist/cosmicintersection/pages/pipelines/PipelineFormPage.js.map +1 -1
  47. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js +183 -79
  48. package/dist/cosmicintersection/pages/pipelines/PipelinesPage.js.map +1 -1
  49. package/dist/cosmicintersection/pages/releases/ReleasesPage.js +346 -204
  50. package/dist/cosmicintersection/pages/releases/ReleasesPage.js.map +1 -1
  51. package/dist/cosmicintersection/pages/runs/RunDetailPage.js +184 -136
  52. package/dist/cosmicintersection/pages/runs/RunDetailPage.js.map +1 -1
  53. package/dist/cosmicintersection/pages/runs/RunsPage.js +182 -120
  54. package/dist/cosmicintersection/pages/runs/RunsPage.js.map +1 -1
  55. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js +234 -0
  56. package/dist/cosmicintersection/pages/sources/SourceDetailPage.js.map +1 -0
  57. package/dist/cosmicintersection/pages/sources/SourcesPage.js +250 -133
  58. package/dist/cosmicintersection/pages/sources/SourcesPage.js.map +1 -1
  59. package/package.json +1 -1
@@ -0,0 +1,329 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useMemo as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region src/cosmicintersection/components/ResultsChart.tsx
5
+ var i = [
6
+ 1,
7
+ 2,
8
+ 3,
9
+ 4,
10
+ 5,
11
+ 6
12
+ ].map((e) => `var(--color-chart-${e})`), a = 600;
13
+ function o(e) {
14
+ return typeof e == "number" && Number.isFinite(e);
15
+ }
16
+ function s(e) {
17
+ let t = e.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2").trim();
18
+ return t ? t.charAt(0).toUpperCase() + t.slice(1) : e;
19
+ }
20
+ function c(e, t) {
21
+ if (o(e)) return {
22
+ x: t,
23
+ y: e
24
+ };
25
+ if (Array.isArray(e)) {
26
+ let [n, r] = e;
27
+ return o(n) && o(r) ? {
28
+ x: n,
29
+ y: r
30
+ } : o(n) && r === void 0 ? {
31
+ x: t,
32
+ y: n
33
+ } : null;
34
+ }
35
+ if (e && typeof e == "object") {
36
+ let n = e, r = n.y ?? n.value ?? n.val ?? n.count;
37
+ if (!o(r)) return null;
38
+ let i = n.x ?? n.t ?? n.time ?? n.index ?? n.bin;
39
+ return {
40
+ x: o(i) ? i : t,
41
+ y: r
42
+ };
43
+ }
44
+ return null;
45
+ }
46
+ function l(e) {
47
+ let t = [];
48
+ for (let n = 0; n < e.length; n += 1) {
49
+ let r = c(e[n], n);
50
+ r && t.push(r);
51
+ }
52
+ return t;
53
+ }
54
+ function u(e) {
55
+ if (e.length <= a) return e;
56
+ let t = e.length / a, n = [];
57
+ for (let r = 0; r < a; r += 1) n.push(e[Math.floor(r * t)]);
58
+ return n;
59
+ }
60
+ function d(e) {
61
+ if (/scatter|point|dot|observ|raw/i.test(e)) return "scatter";
62
+ if (/line|fit|curve|model|trend|series/i.test(e)) return "line";
63
+ }
64
+ function f(e) {
65
+ let t = [], n = 0, r = () => {
66
+ let e = i[n % i.length];
67
+ return n += 1, e;
68
+ }, a = (e, n, i) => {
69
+ let a = u(l(e));
70
+ a.length >= 2 && t.push({
71
+ name: n,
72
+ kind: i,
73
+ points: a,
74
+ colorVar: r()
75
+ });
76
+ }, o = e.series;
77
+ if (Array.isArray(o) && o.forEach((e, t) => {
78
+ if (Array.isArray(e)) {
79
+ a(e, `Series ${t + 1}`, "line");
80
+ return;
81
+ }
82
+ if (e && typeof e == "object") {
83
+ let n = e, r = n.points ?? n.data ?? n.values;
84
+ if (!Array.isArray(r)) return;
85
+ a(r, typeof n.name == "string" && n.name.trim() ? n.name : typeof n.label == "string" && n.label.trim() ? n.label : `Series ${t + 1}`, d([
86
+ n.kind,
87
+ n.type,
88
+ n.name,
89
+ n.label
90
+ ].filter((e) => typeof e == "string").join(" ")) ?? "line");
91
+ }
92
+ }), t.length === 0 && (Array.isArray(e.points) ? a(e.points, "Observations", "scatter") : Array.isArray(e.data) ? a(e.data, "Data", "line") : Array.isArray(e.values) && a(e.values, "Values", "line"), Array.isArray(e.fit) && a(e.fit, "Fit", "line"), Array.isArray(e.model) && a(e.model, "Model", "line")), t.length === 0) {
93
+ for (let [n, r] of Object.entries(e)) if (Array.isArray(r) && a(r, s(n), "line"), t.length >= i.length) break;
94
+ }
95
+ return t;
96
+ }
97
+ function p(e, t, n, r) {
98
+ for (let n of t) {
99
+ let t = e[n];
100
+ if (typeof t == "string" && t.trim()) return t;
101
+ }
102
+ let i = e.axes ?? e.axis;
103
+ if (i && typeof i == "object") {
104
+ let e = i[n];
105
+ if (typeof e == "string" && e.trim()) return e;
106
+ }
107
+ return r;
108
+ }
109
+ function m(e, t, n) {
110
+ if (!Number.isFinite(e) || !Number.isFinite(t) || e === t) return [e];
111
+ let r = (t - e) / Math.max(1, n), i = 10 ** Math.floor(Math.log10(r)), a = r / i, o = (a < 1.5 ? 1 : a < 3 ? 2 : a < 7 ? 5 : 10) * i, s = Math.floor(e / o) * o, c = Math.ceil(t / o) * o, l = [];
112
+ for (let e = s; e <= c + o * 1e-6; e += o) l.push(Number(e.toFixed(12)));
113
+ return l;
114
+ }
115
+ function h(e) {
116
+ if (e === 0) return "0";
117
+ let t = Math.abs(e);
118
+ return t < .001 || t >= 1e6 ? e.toExponential(1) : Number(e.toPrecision(4)).toLocaleString(void 0, { maximumFractionDigits: 4 });
119
+ }
120
+ var g = 640, _ = 340, v = {
121
+ top: 16,
122
+ right: 20,
123
+ bottom: 48,
124
+ left: 62
125
+ }, y = g - v.left - v.right, b = _ - v.top - v.bottom;
126
+ function x(e) {
127
+ let t = f(e);
128
+ if (t.length === 0) return null;
129
+ let n = [], r = [];
130
+ for (let e of t) for (let t of e.points) n.push(t.x), r.push(t.y);
131
+ let i = Math.min(...n), a = Math.max(...n), o = Math.min(...r), s = Math.max(...r);
132
+ if (i === a) {
133
+ let e = Math.abs(i) || 1;
134
+ i -= e, a += e;
135
+ }
136
+ if (o === s) {
137
+ let e = Math.abs(o) || 1;
138
+ o -= e, s += e;
139
+ }
140
+ let c = (s - o) * .08;
141
+ o -= c, s += c;
142
+ let l = m(i, a, 5), u = m(o, s, 5), d = [Math.min(i, l[0]), Math.max(a, l[l.length - 1])], h = [Math.min(o, u[0]), Math.max(s, u[u.length - 1])];
143
+ return {
144
+ series: t,
145
+ xTicks: l.filter((e) => e >= d[0] && e <= d[1]),
146
+ yTicks: u.filter((e) => e >= h[0] && e <= h[1]),
147
+ domainX: d,
148
+ domainY: h,
149
+ xLabel: p(e, [
150
+ "xLabel",
151
+ "xlabel",
152
+ "xAxis",
153
+ "xaxis",
154
+ "xTitle",
155
+ "xUnit"
156
+ ], "x", "x"),
157
+ yLabel: p(e, [
158
+ "yLabel",
159
+ "ylabel",
160
+ "yAxis",
161
+ "yaxis",
162
+ "yTitle",
163
+ "yUnit"
164
+ ], "y", "y")
165
+ };
166
+ }
167
+ function S(e) {
168
+ if (e == null) return "—";
169
+ if (typeof e == "number") return e.toLocaleString(void 0, { maximumFractionDigits: 6 });
170
+ if (typeof e == "boolean") return e ? "Yes" : "No";
171
+ if (typeof e == "string") return e;
172
+ if (Array.isArray(e)) return `${e.length} item${e.length === 1 ? "" : "s"}`;
173
+ try {
174
+ return JSON.stringify(e);
175
+ } catch {
176
+ return String(e);
177
+ }
178
+ }
179
+ function C({ results: e }) {
180
+ let t = Object.entries(e);
181
+ return t.length === 0 ? /* @__PURE__ */ n("p", {
182
+ className: "rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary",
183
+ children: "No structured results were recorded for this analysis."
184
+ }) : /* @__PURE__ */ n("dl", {
185
+ className: "grid grid-cols-1 gap-px overflow-hidden rounded-card border border-border-default bg-border-subtle sm:grid-cols-2",
186
+ children: t.map(([e, t]) => {
187
+ let i = S(t);
188
+ return /* @__PURE__ */ r("div", {
189
+ className: "flex items-baseline justify-between gap-3 bg-bg-elevated px-3 py-2",
190
+ children: [/* @__PURE__ */ n("dt", {
191
+ className: "min-w-0 truncate text-xs font-medium uppercase tracking-wide text-text-secondary",
192
+ children: s(e)
193
+ }), /* @__PURE__ */ n("dd", {
194
+ className: "max-w-[60%] truncate text-right text-sm tabular-nums text-text-primary",
195
+ title: i,
196
+ children: i
197
+ })]
198
+ }, e);
199
+ })
200
+ });
201
+ }
202
+ function w({ results: i, className: a }) {
203
+ let o = t(() => x(i), [i]);
204
+ if (!o) return /* @__PURE__ */ n("div", {
205
+ className: a,
206
+ children: /* @__PURE__ */ n(C, { results: i })
207
+ });
208
+ let { series: s, xTicks: c, yTicks: l, domainX: u, domainY: d, xLabel: f, yLabel: p } = o, m = (e) => v.left + (e - u[0]) / (u[1] - u[0]) * y, S = (e) => v.top + b - (e - d[0]) / (d[1] - d[0]) * b, w = s.length > 1, T = `Chart of ${s.map((e) => e.name).join(", ")}; x-axis ${f}, y-axis ${p}`;
209
+ return /* @__PURE__ */ r("div", {
210
+ className: e("space-y-3", a),
211
+ children: [/* @__PURE__ */ n("div", {
212
+ className: "rounded-card border border-border-default bg-bg-sunken p-3",
213
+ children: /* @__PURE__ */ r("svg", {
214
+ viewBox: `0 0 ${g} ${_}`,
215
+ className: "h-auto w-full",
216
+ preserveAspectRatio: "xMidYMid meet",
217
+ role: "img",
218
+ "aria-label": T,
219
+ children: [
220
+ l.map((e) => {
221
+ let t = S(e);
222
+ return /* @__PURE__ */ r("g", { children: [/* @__PURE__ */ n("line", {
223
+ x1: v.left,
224
+ x2: v.left + y,
225
+ y1: t,
226
+ y2: t,
227
+ stroke: "var(--color-chart-grid)",
228
+ strokeWidth: 1
229
+ }), /* @__PURE__ */ n("text", {
230
+ x: v.left - 8,
231
+ y: t,
232
+ textAnchor: "end",
233
+ dominantBaseline: "central",
234
+ fontSize: 11,
235
+ fill: "var(--color-text-tertiary)",
236
+ children: h(e)
237
+ })] }, `y-${e}`);
238
+ }),
239
+ c.map((e) => {
240
+ let t = m(e);
241
+ return /* @__PURE__ */ r("g", { children: [/* @__PURE__ */ n("line", {
242
+ x1: t,
243
+ x2: t,
244
+ y1: v.top,
245
+ y2: v.top + b,
246
+ stroke: "var(--color-chart-grid)",
247
+ strokeWidth: 1,
248
+ strokeDasharray: "2 4"
249
+ }), /* @__PURE__ */ n("text", {
250
+ x: t,
251
+ y: v.top + b + 18,
252
+ textAnchor: "middle",
253
+ fontSize: 11,
254
+ fill: "var(--color-text-tertiary)",
255
+ children: h(e)
256
+ })] }, `x-${e}`);
257
+ }),
258
+ /* @__PURE__ */ n("rect", {
259
+ x: v.left,
260
+ y: v.top,
261
+ width: y,
262
+ height: b,
263
+ fill: "none",
264
+ stroke: "var(--color-chart-axis)",
265
+ strokeWidth: 1
266
+ }),
267
+ s.map((e) => {
268
+ let t = [...e.points].sort((e, t) => e.x - t.x).map((e, t) => `${t === 0 ? "M" : "L"} ${m(e.x).toFixed(2)} ${S(e.y).toFixed(2)}`).join(" "), i = e.kind === "scatter" || e.points.length <= 40;
269
+ return /* @__PURE__ */ r("g", { children: [e.kind === "line" ? /* @__PURE__ */ n("path", {
270
+ d: t,
271
+ fill: "none",
272
+ stroke: e.colorVar,
273
+ strokeWidth: 2,
274
+ strokeLinejoin: "round",
275
+ strokeLinecap: "round"
276
+ }) : null, i ? e.points.map((t, r) => /* @__PURE__ */ n("circle", {
277
+ cx: m(t.x),
278
+ cy: S(t.y),
279
+ r: e.kind === "scatter" ? 2.4 : 2.8,
280
+ fill: e.colorVar,
281
+ fillOpacity: e.kind === "scatter" ? .85 : 1
282
+ }, `${e.name}-${r}`)) : null] }, e.name);
283
+ }),
284
+ /* @__PURE__ */ n("text", {
285
+ x: v.left + y / 2,
286
+ y: _ - 6,
287
+ textAnchor: "middle",
288
+ fontSize: 12,
289
+ fill: "var(--color-text-secondary)",
290
+ children: f
291
+ }),
292
+ /* @__PURE__ */ n("text", {
293
+ x: 14,
294
+ y: v.top + b / 2,
295
+ textAnchor: "middle",
296
+ fontSize: 12,
297
+ fill: "var(--color-text-secondary)",
298
+ transform: `rotate(-90 14 ${v.top + b / 2})`,
299
+ children: p
300
+ })
301
+ ]
302
+ })
303
+ }), w ? /* @__PURE__ */ n("ul", {
304
+ className: "flex flex-wrap gap-x-4 gap-y-1.5",
305
+ children: s.map((e) => /* @__PURE__ */ r("li", {
306
+ className: "flex items-center gap-1.5 text-xs text-text-secondary",
307
+ children: [/* @__PURE__ */ n("span", {
308
+ "aria-hidden": "true",
309
+ className: "inline-block size-2.5 rounded-full",
310
+ style: { backgroundColor: e.colorVar }
311
+ }), /* @__PURE__ */ r("span", {
312
+ className: "truncate",
313
+ children: [e.name, /* @__PURE__ */ r("span", {
314
+ className: "ml-1 text-text-tertiary",
315
+ children: [
316
+ "(",
317
+ e.kind === "scatter" ? "points" : "line",
318
+ ")"
319
+ ]
320
+ })]
321
+ })]
322
+ }, e.name))
323
+ }) : null]
324
+ });
325
+ }
326
+ //#endregion
327
+ export { w as default };
328
+
329
+ //# sourceMappingURL=ResultsChart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResultsChart.js","names":[],"sources":["../../../src/cosmicintersection/components/ResultsChart.tsx"],"sourcesContent":["/**\n * ResultsChart — inline visualization for a completed analysis' `results` JSON.\n *\n * There is NO chart library here: axes, gridlines, scatter dots and line series\n * are hand-drawn as SVG using the design system's dataviz tokens\n * (`var(--color-chart-*)`, `var(--color-chart-axis)`, `var(--color-chart-grid)`)\n * plus the standard text tokens — never a raw palette colour. The SVG uses a\n * `viewBox` and `width:100%`, so it scales fluidly on phones (no horizontal\n * scroll).\n *\n * The `results` payload is free-form JSON (whatever `cosCompleteAnalysis` stored),\n * so the component sniffs it for numeric series. Accepted shapes:\n * - `results.series: Array<{ name?, kind?: 'line'|'scatter', points }>`\n * - `results.points` (scatter) and/or `results.fit` / `results.model` (line)\n * - any top-level numeric arrays (rendered as line series)\n * where a `points` array may hold `{x,y}` objects, `[x,y]` pairs, `{value}` /\n * `{y}` objects, or bare numbers (index used as x).\n *\n * When nothing chartable is found, a compact, responsive key/value metrics list\n * is rendered instead. Everything comes from the real payload — nothing is\n * fabricated; an empty payload renders an honest \"no results\" message.\n */\nimport { useMemo } from 'react';\n\nimport { cn } from '../utils/cn';\n\ntype Json = Record<string, unknown>;\n\ninterface Point {\n x: number;\n y: number;\n}\n\ntype SeriesKind = 'line' | 'scatter';\n\ninterface Series {\n name: string;\n kind: SeriesKind;\n points: Point[];\n colorVar: string;\n}\n\nconst SERIES_PALETTE = [1, 2, 3, 4, 5, 6].map((n) => `var(--color-chart-${n})`);\n\nconst MAX_POINTS_PER_SERIES = 600;\n\n// ---------------------------------------------------------------------------\n// Payload parsing\n// ---------------------------------------------------------------------------\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value);\n}\n\nfunction humanize(key: string): string {\n const spaced = key\n .replace(/[_-]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .trim();\n if (!spaced) {\n return key;\n }\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n}\n\n/** Coerce a single raw element into a {x,y} point (index is the fallback x). */\nfunction toPoint(element: unknown, index: number): Point | null {\n if (isFiniteNumber(element)) {\n return { x: index, y: element };\n }\n if (Array.isArray(element)) {\n const [a, b] = element;\n if (isFiniteNumber(a) && isFiniteNumber(b)) {\n return { x: a, y: b };\n }\n if (isFiniteNumber(a) && b === undefined) {\n return { x: index, y: a };\n }\n return null;\n }\n if (element && typeof element === 'object') {\n const obj = element as Record<string, unknown>;\n const yRaw = obj.y ?? obj.value ?? obj.val ?? obj.count;\n if (!isFiniteNumber(yRaw)) {\n return null;\n }\n const xRaw = obj.x ?? obj.t ?? obj.time ?? obj.index ?? obj.bin;\n return { x: isFiniteNumber(xRaw) ? xRaw : index, y: yRaw };\n }\n return null;\n}\n\n/** Map an array to points, dropping unparseable elements. */\nfunction pointsFrom(raw: unknown[]): Point[] {\n const points: Point[] = [];\n for (let i = 0; i < raw.length; i += 1) {\n const point = toPoint(raw[i], i);\n if (point) {\n points.push(point);\n }\n }\n return points;\n}\n\n/** Evenly downsample a long series so the SVG stays light on huge payloads. */\nfunction sample(points: Point[]): Point[] {\n if (points.length <= MAX_POINTS_PER_SERIES) {\n return points;\n }\n const stride = points.length / MAX_POINTS_PER_SERIES;\n const out: Point[] = [];\n for (let i = 0; i < MAX_POINTS_PER_SERIES; i += 1) {\n out.push(points[Math.floor(i * stride)]);\n }\n return out;\n}\n\nfunction kindFrom(raw: string): SeriesKind | undefined {\n if (/scatter|point|dot|observ|raw/i.test(raw)) {\n return 'scatter';\n }\n if (/line|fit|curve|model|trend|series/i.test(raw)) {\n return 'line';\n }\n return undefined;\n}\n\n/** Sniff the payload for one or more numeric series (empty = not chartable). */\nfunction extractSeries(results: Json): Series[] {\n const out: Series[] = [];\n let colorIndex = 0;\n const nextColor = (): string => {\n const color = SERIES_PALETTE[colorIndex % SERIES_PALETTE.length];\n colorIndex += 1;\n return color;\n };\n const push = (raw: unknown[], name: string, kind: SeriesKind): void => {\n const points = sample(pointsFrom(raw));\n if (points.length >= 2) {\n out.push({ name, kind, points, colorVar: nextColor() });\n }\n };\n\n // 1. Explicit `series` array — the richest shape.\n const series = results.series;\n if (Array.isArray(series)) {\n series.forEach((entry, i) => {\n if (Array.isArray(entry)) {\n push(entry, `Series ${i + 1}`, 'line');\n return;\n }\n if (entry && typeof entry === 'object') {\n const so = entry as Record<string, unknown>;\n const data = so.points ?? so.data ?? so.values;\n if (!Array.isArray(data)) {\n return;\n }\n const name =\n typeof so.name === 'string' && so.name.trim()\n ? so.name\n : typeof so.label === 'string' && so.label.trim()\n ? so.label\n : `Series ${i + 1}`;\n const kindRaw = [so.kind, so.type, so.name, so.label]\n .filter((v): v is string => typeof v === 'string')\n .join(' ');\n push(data, name, kindFrom(kindRaw) ?? 'line');\n }\n });\n }\n\n // 2. `points` (scatter) plus optional `fit` / `model` overlay line.\n if (out.length === 0) {\n if (Array.isArray(results.points)) {\n push(results.points, 'Observations', 'scatter');\n } else if (Array.isArray(results.data)) {\n push(results.data, 'Data', 'line');\n } else if (Array.isArray(results.values)) {\n push(results.values, 'Values', 'line');\n }\n if (Array.isArray(results.fit)) {\n push(results.fit, 'Fit', 'line');\n }\n if (Array.isArray(results.model)) {\n push(results.model, 'Model', 'line');\n }\n }\n\n // 3. Scan for the first few bare numeric arrays (e.g. { loss, accuracy }).\n if (out.length === 0) {\n for (const [key, value] of Object.entries(results)) {\n if (Array.isArray(value)) {\n push(value, humanize(key), 'line');\n }\n if (out.length >= SERIES_PALETTE.length) {\n break;\n }\n }\n }\n\n return out;\n}\n\nfunction readLabel(results: Json, keys: string[], axisKey: 'x' | 'y', fallback: string): string {\n for (const key of keys) {\n const value = results[key];\n if (typeof value === 'string' && value.trim()) {\n return value;\n }\n }\n const axes = results.axes ?? results.axis;\n if (axes && typeof axes === 'object') {\n const value = (axes as Record<string, unknown>)[axisKey];\n if (typeof value === 'string' && value.trim()) {\n return value;\n }\n }\n return fallback;\n}\n\n// ---------------------------------------------------------------------------\n// Axis math\n// ---------------------------------------------------------------------------\n\n/** Rounded, human-friendly tick values across [min, max]. */\nfunction niceTicks(min: number, max: number, count: number): number[] {\n if (!Number.isFinite(min) || !Number.isFinite(max) || min === max) {\n return [min];\n }\n const span = max - min;\n const rawStep = span / Math.max(1, count);\n const mag = Math.pow(10, Math.floor(Math.log10(rawStep)));\n const norm = rawStep / mag;\n const niceNorm = norm < 1.5 ? 1 : norm < 3 ? 2 : norm < 7 ? 5 : 10;\n const step = niceNorm * mag;\n const start = Math.floor(min / step) * step;\n const end = Math.ceil(max / step) * step;\n const ticks: number[] = [];\n for (let v = start; v <= end + step * 1e-6; v += step) {\n ticks.push(Number(v.toFixed(12)));\n }\n return ticks;\n}\n\nfunction formatTick(value: number): string {\n if (value === 0) {\n return '0';\n }\n const abs = Math.abs(value);\n if (abs < 1e-3 || abs >= 1e6) {\n return value.toExponential(1);\n }\n const rounded = Number(value.toPrecision(4));\n return rounded.toLocaleString(undefined, { maximumFractionDigits: 4 });\n}\n\n// ---------------------------------------------------------------------------\n// Chart geometry\n// ---------------------------------------------------------------------------\n\nconst VIEW_W = 640;\nconst VIEW_H = 340;\nconst MARGIN = { top: 16, right: 20, bottom: 48, left: 62 };\nconst INNER_W = VIEW_W - MARGIN.left - MARGIN.right;\nconst INNER_H = VIEW_H - MARGIN.top - MARGIN.bottom;\n\ninterface ChartModel {\n series: Series[];\n xTicks: number[];\n yTicks: number[];\n domainX: [number, number];\n domainY: [number, number];\n xLabel: string;\n yLabel: string;\n}\n\nfunction buildModel(results: Json): ChartModel | null {\n const series = extractSeries(results);\n if (series.length === 0) {\n return null;\n }\n\n const xs: number[] = [];\n const ys: number[] = [];\n for (const s of series) {\n for (const p of s.points) {\n xs.push(p.x);\n ys.push(p.y);\n }\n }\n\n let minX = Math.min(...xs);\n let maxX = Math.max(...xs);\n let minY = Math.min(...ys);\n let maxY = Math.max(...ys);\n\n if (minX === maxX) {\n const pad = Math.abs(minX) || 1;\n minX -= pad;\n maxX += pad;\n }\n if (minY === maxY) {\n const pad = Math.abs(minY) || 1;\n minY -= pad;\n maxY += pad;\n }\n // Breathing room above/below the data so markers are not clipped.\n const yPad = (maxY - minY) * 0.08;\n minY -= yPad;\n maxY += yPad;\n\n const xTicks = niceTicks(minX, maxX, 5);\n const yTicks = niceTicks(minY, maxY, 5);\n const domainX: [number, number] = [\n Math.min(minX, xTicks[0]),\n Math.max(maxX, xTicks[xTicks.length - 1]),\n ];\n const domainY: [number, number] = [\n Math.min(minY, yTicks[0]),\n Math.max(maxY, yTicks[yTicks.length - 1]),\n ];\n\n return {\n series,\n xTicks: xTicks.filter((t) => t >= domainX[0] && t <= domainX[1]),\n yTicks: yTicks.filter((t) => t >= domainY[0] && t <= domainY[1]),\n domainX,\n domainY,\n xLabel: readLabel(results, ['xLabel', 'xlabel', 'xAxis', 'xaxis', 'xTitle', 'xUnit'], 'x', 'x'),\n yLabel: readLabel(results, ['yLabel', 'ylabel', 'yAxis', 'yaxis', 'yTitle', 'yUnit'], 'y', 'y'),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Fallback key/value table\n// ---------------------------------------------------------------------------\n\nfunction displayValue(value: unknown): string {\n if (value === null || value === undefined) {\n return '—';\n }\n if (typeof value === 'number') {\n return value.toLocaleString(undefined, { maximumFractionDigits: 6 });\n }\n if (typeof value === 'boolean') {\n return value ? 'Yes' : 'No';\n }\n if (typeof value === 'string') {\n return value;\n }\n if (Array.isArray(value)) {\n return `${value.length} item${value.length === 1 ? '' : 's'}`;\n }\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction MetricsTable({ results }: { results: Json }) {\n const entries = Object.entries(results);\n if (entries.length === 0) {\n return (\n <p className=\"rounded-card border border-border-default bg-bg-sunken px-4 py-6 text-center text-sm text-text-secondary\">\n No structured results were recorded for this analysis.\n </p>\n );\n }\n return (\n <dl className=\"grid grid-cols-1 gap-px overflow-hidden rounded-card border border-border-default bg-border-subtle sm:grid-cols-2\">\n {entries.map(([key, value]) => {\n const text = displayValue(value);\n return (\n <div\n key={key}\n className=\"flex items-baseline justify-between gap-3 bg-bg-elevated px-3 py-2\"\n >\n <dt className=\"min-w-0 truncate text-xs font-medium uppercase tracking-wide text-text-secondary\">\n {humanize(key)}\n </dt>\n <dd\n className=\"max-w-[60%] truncate text-right text-sm tabular-nums text-text-primary\"\n title={text}\n >\n {text}\n </dd>\n </div>\n );\n })}\n </dl>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport interface ResultsChartProps {\n results: Record<string, unknown>;\n className?: string;\n}\n\nexport function ResultsChart({ results, className }: ResultsChartProps) {\n const model = useMemo(() => buildModel(results), [results]);\n\n if (!model) {\n return (\n <div className={className}>\n <MetricsTable results={results} />\n </div>\n );\n }\n\n const { series, xTicks, yTicks, domainX, domainY, xLabel, yLabel } = model;\n\n const sx = (x: number): number =>\n MARGIN.left + ((x - domainX[0]) / (domainX[1] - domainX[0])) * INNER_W;\n const sy = (y: number): number =>\n MARGIN.top + INNER_H - ((y - domainY[0]) / (domainY[1] - domainY[0])) * INNER_H;\n\n const showLegend = series.length > 1;\n const ariaLabel = `Chart of ${series\n .map((s) => s.name)\n .join(', ')}; x-axis ${xLabel}, y-axis ${yLabel}`;\n\n return (\n <div className={cn('space-y-3', className)}>\n <div className=\"rounded-card border border-border-default bg-bg-sunken p-3\">\n <svg\n viewBox={`0 0 ${VIEW_W} ${VIEW_H}`}\n className=\"h-auto w-full\"\n preserveAspectRatio=\"xMidYMid meet\"\n role=\"img\"\n aria-label={ariaLabel}\n >\n {/* horizontal gridlines + y tick labels */}\n {yTicks.map((tick) => {\n const y = sy(tick);\n return (\n <g key={`y-${tick}`}>\n <line\n x1={MARGIN.left}\n x2={MARGIN.left + INNER_W}\n y1={y}\n y2={y}\n stroke=\"var(--color-chart-grid)\"\n strokeWidth={1}\n />\n <text\n x={MARGIN.left - 8}\n y={y}\n textAnchor=\"end\"\n dominantBaseline=\"central\"\n fontSize={11}\n fill=\"var(--color-text-tertiary)\"\n >\n {formatTick(tick)}\n </text>\n </g>\n );\n })}\n\n {/* vertical gridlines + x tick labels */}\n {xTicks.map((tick) => {\n const x = sx(tick);\n return (\n <g key={`x-${tick}`}>\n <line\n x1={x}\n x2={x}\n y1={MARGIN.top}\n y2={MARGIN.top + INNER_H}\n stroke=\"var(--color-chart-grid)\"\n strokeWidth={1}\n strokeDasharray=\"2 4\"\n />\n <text\n x={x}\n y={MARGIN.top + INNER_H + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--color-text-tertiary)\"\n >\n {formatTick(tick)}\n </text>\n </g>\n );\n })}\n\n {/* plot frame */}\n <rect\n x={MARGIN.left}\n y={MARGIN.top}\n width={INNER_W}\n height={INNER_H}\n fill=\"none\"\n stroke=\"var(--color-chart-axis)\"\n strokeWidth={1}\n />\n\n {/* series */}\n {series.map((s) => {\n const ordered = [...s.points].sort((a, b) => a.x - b.x);\n const path = ordered\n .map((p, i) => `${i === 0 ? 'M' : 'L'} ${sx(p.x).toFixed(2)} ${sy(p.y).toFixed(2)}`)\n .join(' ');\n const showDots = s.kind === 'scatter' || s.points.length <= 40;\n return (\n <g key={s.name}>\n {s.kind === 'line' ? (\n <path\n d={path}\n fill=\"none\"\n stroke={s.colorVar}\n strokeWidth={2}\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n ) : null}\n {showDots\n ? s.points.map((p, i) => (\n <circle\n key={`${s.name}-${i}`}\n cx={sx(p.x)}\n cy={sy(p.y)}\n r={s.kind === 'scatter' ? 2.4 : 2.8}\n fill={s.colorVar}\n fillOpacity={s.kind === 'scatter' ? 0.85 : 1}\n />\n ))\n : null}\n </g>\n );\n })}\n\n {/* axis titles */}\n <text\n x={MARGIN.left + INNER_W / 2}\n y={VIEW_H - 6}\n textAnchor=\"middle\"\n fontSize={12}\n fill=\"var(--color-text-secondary)\"\n >\n {xLabel}\n </text>\n <text\n x={14}\n y={MARGIN.top + INNER_H / 2}\n textAnchor=\"middle\"\n fontSize={12}\n fill=\"var(--color-text-secondary)\"\n transform={`rotate(-90 14 ${MARGIN.top + INNER_H / 2})`}\n >\n {yLabel}\n </text>\n </svg>\n </div>\n\n {showLegend ? (\n <ul className=\"flex flex-wrap gap-x-4 gap-y-1.5\">\n {series.map((s) => (\n <li key={s.name} className=\"flex items-center gap-1.5 text-xs text-text-secondary\">\n <span\n aria-hidden=\"true\"\n className=\"inline-block size-2.5 rounded-full\"\n style={{ backgroundColor: s.colorVar }}\n />\n <span className=\"truncate\">\n {s.name}\n <span className=\"ml-1 text-text-tertiary\">\n ({s.kind === 'scatter' ? 'points' : 'line'})\n </span>\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n </div>\n );\n}\n\nexport default ResultsChart;\n"],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { formatEnumLabel as e } from "./StatusBadge.js";
2
+ import { Fragment as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ import { Bell as i, Database as a, Gauge as o, Shuffle as s, SlidersHorizontal as c, Upload as l } from "lucide-react";
5
+ //#region src/cosmicintersection/components/StepDAG.tsx
6
+ var u = {
7
+ INGEST: { icon: a },
8
+ CALIBRATE: { icon: c },
9
+ TRANSFORM: { icon: s },
10
+ ANALYZE: { icon: o },
11
+ EXPORT: { icon: l },
12
+ NOTIFY: { icon: i }
13
+ }, d = {
14
+ border: "border-border-default",
15
+ icon: "bg-bg-sunken text-text-secondary",
16
+ ring: ""
17
+ }, f = {
18
+ SUCCEEDED: {
19
+ border: "border-status-success-border",
20
+ icon: "bg-status-success-bg-subtle text-status-success-text",
21
+ ring: ""
22
+ },
23
+ RUNNING: {
24
+ border: "border-status-warning-border",
25
+ icon: "bg-status-warning-bg-subtle text-status-warning-text",
26
+ ring: "ring-2 ring-status-warning-border"
27
+ },
28
+ FAILED: {
29
+ border: "border-status-error-border",
30
+ icon: "bg-status-error-bg-subtle text-status-error-text",
31
+ ring: ""
32
+ },
33
+ QUEUED: {
34
+ border: "border-status-info-border",
35
+ icon: "bg-status-info-bg-subtle text-status-info-text",
36
+ ring: ""
37
+ },
38
+ CANCELLED: d
39
+ };
40
+ function p() {
41
+ return /* @__PURE__ */ n("span", {
42
+ "aria-hidden": "true",
43
+ className: "flex shrink-0 items-center justify-center text-border-strong",
44
+ children: /* @__PURE__ */ r("svg", {
45
+ viewBox: "0 0 40 12",
46
+ className: "h-4 w-6 rotate-90 md:h-3 md:w-9 md:rotate-0",
47
+ fill: "none",
48
+ stroke: "currentColor",
49
+ strokeWidth: 1.75,
50
+ strokeLinecap: "round",
51
+ strokeLinejoin: "round",
52
+ children: [/* @__PURE__ */ n("line", {
53
+ x1: "2",
54
+ y1: "6",
55
+ x2: "33",
56
+ y2: "6"
57
+ }), /* @__PURE__ */ n("path", { d: "M28 2 L34 6 L28 10" })]
58
+ })
59
+ });
60
+ }
61
+ function m({ step: t, index: i }) {
62
+ let a = u[t.kind].icon, o = t.status && f[t.status] || d, s = e(t.kind), c = !!(t.name && t.name.trim() && t.name.trim().toLowerCase() !== s.toLowerCase());
63
+ return /* @__PURE__ */ r("div", {
64
+ role: "listitem",
65
+ className: `flex w-full items-center gap-3 rounded-card border bg-bg-elevated px-3.5 py-3 md:w-auto md:min-w-[8.5rem] ${o.border} ${o.ring}`,
66
+ children: [/* @__PURE__ */ n("span", {
67
+ className: `flex size-9 shrink-0 items-center justify-center rounded-lg ${o.icon}`,
68
+ children: /* @__PURE__ */ n(a, {
69
+ className: "size-4",
70
+ "aria-hidden": "true"
71
+ })
72
+ }), /* @__PURE__ */ r("span", {
73
+ className: "flex min-w-0 flex-col",
74
+ children: [/* @__PURE__ */ r("span", {
75
+ className: "flex items-baseline gap-1.5",
76
+ children: [/* @__PURE__ */ n("span", {
77
+ className: "text-[0.65rem] font-semibold tabular-nums text-text-secondary",
78
+ children: i + 1
79
+ }), /* @__PURE__ */ n("span", {
80
+ className: "truncate text-sm font-medium text-text-primary",
81
+ children: s
82
+ })]
83
+ }), c ? /* @__PURE__ */ n("span", {
84
+ className: "truncate text-xs text-text-secondary",
85
+ children: t.name
86
+ }) : null]
87
+ })]
88
+ });
89
+ }
90
+ function h({ steps: e, className: i }) {
91
+ return e.length === 0 ? null : /* @__PURE__ */ n("div", {
92
+ role: "list",
93
+ "aria-label": "Pipeline step flow",
94
+ className: `flex flex-col items-stretch gap-2 md:flex-row md:flex-wrap md:items-center ${i ?? ""}`,
95
+ children: e.map((i, a) => /* @__PURE__ */ r(t, { children: [/* @__PURE__ */ n(m, {
96
+ step: i,
97
+ index: a
98
+ }), a < e.length - 1 ? /* @__PURE__ */ n(p, {}) : null] }, `${i.kind}-${a}`))
99
+ });
100
+ }
101
+ //#endregion
102
+ export { h as default };
103
+
104
+ //# sourceMappingURL=StepDAG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StepDAG.js","names":[],"sources":["../../../src/cosmicintersection/components/StepDAG.tsx"],"sourcesContent":["/**\n * StepDAG — a lightweight, dependency-free step-flow visualization.\n *\n * Renders an ordered list of pipeline steps as connected rounded nodes: each\n * node shows a per-kind icon + label (and an optional custom step name), and an\n * optional run status tint. Nodes are joined by custom inline-SVG arrow\n * connectors — horizontal on md+ screens, vertical (stacked) on small screens\n * so it never forces a horizontal-scroll.\n *\n * Colour comes only from the semantic token families (bg-bg-*, text-text-*,\n * border-border-*, status-*) — never a raw palette colour — matching mockups\n * 11 & 14.\n */\nimport { Fragment } from 'react';\nimport type { LucideIcon } from 'lucide-react';\nimport { Bell, Database, Gauge, Shuffle, SlidersHorizontal, Upload } from 'lucide-react';\n\nimport type { CosRunStatus, CosStepKind } from '../api/cosmicApi';\nimport { formatEnumLabel } from './StatusBadge';\n\nexport interface StepDAGStep {\n /** Optional human-authored step name; shown under the kind label when it differs. */\n name?: string | null;\n kind: CosStepKind;\n /** Optional run status — tints the node when present. */\n status?: CosRunStatus | null;\n}\n\nexport interface StepDAGProps {\n steps: StepDAGStep[];\n className?: string;\n}\n\nconst KIND_META: Record<CosStepKind, { icon: LucideIcon }> = {\n INGEST: { icon: Database },\n CALIBRATE: { icon: SlidersHorizontal },\n TRANSFORM: { icon: Shuffle },\n ANALYZE: { icon: Gauge },\n EXPORT: { icon: Upload },\n NOTIFY: { icon: Bell },\n};\n\ninterface Accent {\n /** Node border. */\n border: string;\n /** Icon chip background + text. */\n icon: string;\n /** Optional focus ring (used to flag an in-flight node). */\n ring: string;\n}\n\nconst DEFAULT_ACCENT: Accent = {\n border: 'border-border-default',\n icon: 'bg-bg-sunken text-text-secondary',\n ring: '',\n};\n\nconst STATUS_ACCENT: Partial<Record<CosRunStatus, Accent>> = {\n SUCCEEDED: {\n border: 'border-status-success-border',\n icon: 'bg-status-success-bg-subtle text-status-success-text',\n ring: '',\n },\n RUNNING: {\n border: 'border-status-warning-border',\n icon: 'bg-status-warning-bg-subtle text-status-warning-text',\n ring: 'ring-2 ring-status-warning-border',\n },\n FAILED: {\n border: 'border-status-error-border',\n icon: 'bg-status-error-bg-subtle text-status-error-text',\n ring: '',\n },\n QUEUED: {\n border: 'border-status-info-border',\n icon: 'bg-status-info-bg-subtle text-status-info-text',\n ring: '',\n },\n CANCELLED: DEFAULT_ACCENT,\n};\n\n/** Custom inline-SVG connector: a line + arrowhead. Points right on md+, down below. */\nfunction StepConnector() {\n return (\n <span\n aria-hidden=\"true\"\n className=\"flex shrink-0 items-center justify-center text-border-strong\"\n >\n <svg\n viewBox=\"0 0 40 12\"\n className=\"h-4 w-6 rotate-90 md:h-3 md:w-9 md:rotate-0\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <line x1=\"2\" y1=\"6\" x2=\"33\" y2=\"6\" />\n <path d=\"M28 2 L34 6 L28 10\" />\n </svg>\n </span>\n );\n}\n\nfunction StepNode({ step, index }: { step: StepDAGStep; index: number }) {\n const meta = KIND_META[step.kind];\n const Icon = meta.icon;\n const accent = (step.status && STATUS_ACCENT[step.status]) || DEFAULT_ACCENT;\n const label = formatEnumLabel(step.kind);\n const showName = Boolean(\n step.name && step.name.trim() && step.name.trim().toLowerCase() !== label.toLowerCase()\n );\n\n return (\n <div\n role=\"listitem\"\n className={`flex w-full items-center gap-3 rounded-card border bg-bg-elevated px-3.5 py-3 md:w-auto md:min-w-[8.5rem] ${accent.border} ${accent.ring}`}\n >\n <span\n className={`flex size-9 shrink-0 items-center justify-center rounded-lg ${accent.icon}`}\n >\n <Icon className=\"size-4\" aria-hidden=\"true\" />\n </span>\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"flex items-baseline gap-1.5\">\n <span className=\"text-[0.65rem] font-semibold tabular-nums text-text-secondary\">\n {index + 1}\n </span>\n <span className=\"truncate text-sm font-medium text-text-primary\">{label}</span>\n </span>\n {showName ? (\n <span className=\"truncate text-xs text-text-secondary\">{step.name}</span>\n ) : null}\n </span>\n </div>\n );\n}\n\n/**\n * Horizontal (md+) / vertical (mobile) step-flow diagram. Presentational only —\n * callers decide where the step list comes from (pipeline definition or a run).\n */\nexport function StepDAG({ steps, className }: StepDAGProps) {\n if (steps.length === 0) {\n return null;\n }\n return (\n <div\n role=\"list\"\n aria-label=\"Pipeline step flow\"\n className={`flex flex-col items-stretch gap-2 md:flex-row md:flex-wrap md:items-center ${className ?? ''}`}\n >\n {steps.map((step, index) => (\n <Fragment key={`${step.kind}-${index}`}>\n <StepNode step={step} index={index} />\n {index < steps.length - 1 ? <StepConnector /> : null}\n </Fragment>\n ))}\n </div>\n );\n}\n\nexport default StepDAG;\n"],"mappings":";;;;;AAiCA,IAAM,IAAuD;CAC3D,QAAQ,EAAE,MAAM,GAAU;CAC1B,WAAW,EAAE,MAAM,GAAmB;CACtC,WAAW,EAAE,MAAM,GAAS;CAC5B,SAAS,EAAE,MAAM,GAAO;CACxB,QAAQ,EAAE,MAAM,GAAQ;CACxB,QAAQ,EAAE,MAAM,GAAM;CACvB,EAWK,IAAyB;CAC7B,QAAQ;CACR,MAAM;CACN,MAAM;CACP,EAEK,IAAuD;CAC3D,WAAW;EACT,QAAQ;EACR,MAAM;EACN,MAAM;EACP;CACD,SAAS;EACP,QAAQ;EACR,MAAM;EACN,MAAM;EACP;CACD,QAAQ;EACN,QAAQ;EACR,MAAM;EACN,MAAM;EACP;CACD,QAAQ;EACN,QAAQ;EACR,MAAM;EACN,MAAM;EACP;CACD,WAAW;CACZ;AAGD,SAAS,IAAgB;AACvB,QACE,kBAAC,QAAD;EACE,eAAY;EACZ,WAAU;YAEV,kBAAC,OAAD;GACE,SAAQ;GACR,WAAU;GACV,MAAK;GACL,QAAO;GACP,aAAa;GACb,eAAc;GACd,gBAAe;aAPjB,CASE,kBAAC,QAAD;IAAM,IAAG;IAAI,IAAG;IAAI,IAAG;IAAK,IAAG;IAAM,CAAA,EACrC,kBAAC,QAAD,EAAM,GAAE,sBAAuB,CAAA,CAC3B;;EACD,CAAA;;AAIX,SAAS,EAAS,EAAE,SAAM,YAA+C;CAEvE,IAAM,IADO,EAAU,EAAK,MACV,MACZ,IAAU,EAAK,UAAU,EAAc,EAAK,WAAY,GACxD,IAAQ,EAAgB,EAAK,KAAK,EAClC,IAAW,GACf,EAAK,QAAQ,EAAK,KAAK,MAAM,IAAI,EAAK,KAAK,MAAM,CAAC,aAAa,KAAK,EAAM,aAAa;AAGzF,QACE,kBAAC,OAAD;EACE,MAAK;EACL,WAAW,6GAA6G,EAAO,OAAO,GAAG,EAAO;YAFlJ,CAIE,kBAAC,QAAD;GACE,WAAW,+DAA+D,EAAO;aAEjF,kBAAC,GAAD;IAAM,WAAU;IAAS,eAAY;IAAS,CAAA;GACzC,CAAA,EACP,kBAAC,QAAD;GAAM,WAAU;aAAhB,CACE,kBAAC,QAAD;IAAM,WAAU;cAAhB,CACE,kBAAC,QAAD;KAAM,WAAU;eACb,IAAQ;KACJ,CAAA,EACP,kBAAC,QAAD;KAAM,WAAU;eAAkD;KAAa,CAAA,CAC1E;OACN,IACC,kBAAC,QAAD;IAAM,WAAU;cAAwC,EAAK;IAAY,CAAA,GACvE,KACC;KACH;;;AAQV,SAAgB,EAAQ,EAAE,UAAO,gBAA2B;AAI1D,QAHI,EAAM,WAAW,IACZ,OAGP,kBAAC,OAAD;EACE,MAAK;EACL,cAAW;EACX,WAAW,8EAA8E,KAAa;YAErG,EAAM,KAAK,GAAM,MAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;GAAgB;GAAa;GAAS,CAAA,EACrC,IAAQ,EAAM,SAAS,IAAI,kBAAC,GAAD,EAAiB,CAAA,GAAG,KACvC,EAAA,EAHI,GAAG,EAAK,KAAK,GAAG,IAGpB,CACX;EACE,CAAA"}
@@ -1,11 +1,13 @@
1
1
  import "./ActionMenu.js";
2
2
  import "./EmptyState.js";
3
+ import "./ErrorState.js";
4
+ import "./LoadingState.js";
5
+ import "./ActivityFeedPanel.js";
6
+ import "./SafeLink.js";
7
+ import "./DiscussionPanel.js";
3
8
  import "./EntityFormShell.js";
4
9
  import "./EntityTagsField.js";
5
- import "./ErrorState.js";
6
10
  import "./FileUploadField.js";
7
- import "./LoadingState.js";
8
11
  import "./PageLayout.js";
9
12
  import "./Pagination.js";
10
- import "./SafeLink.js";
11
13
  import "./StatusBadge.js";